- [Core] Added new entry
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
using ACLLibs;
|
using System;
|
||||||
|
using ACLLibs;
|
||||||
|
|
||||||
namespace AssetStudio
|
namespace AssetStudio
|
||||||
{
|
{
|
||||||
@@ -8,17 +9,23 @@ namespace AssetStudio
|
|||||||
{
|
{
|
||||||
if (game.Type.IsSRGroup())
|
if (game.Type.IsSRGroup())
|
||||||
{
|
{
|
||||||
SRACL.DecompressAll(m_ACLClip.m_ClipData, out values, out times);
|
var aclClip = m_ACLClip as MHYACLClip;
|
||||||
|
SRACL.DecompressAll(aclClip.m_ClipData, out values, out times);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!m_ACLClip.m_DatabaseData.IsNullOrEmpty())
|
switch (m_ACLClip)
|
||||||
{
|
{
|
||||||
DBACL.DecompressTracks(m_ACLClip.m_ClipData, m_ACLClip.m_DatabaseData, out values, out times);
|
case GIACLClip giaclClip:
|
||||||
}
|
DBACL.DecompressTracks(giaclClip.m_ClipData, giaclClip.m_DatabaseData, out values, out times);
|
||||||
else
|
break;
|
||||||
{
|
case MHYACLClip mhyaclClip:
|
||||||
ACL.DecompressAll(m_ACLClip.m_ClipData, out values, out times);
|
ACL.DecompressAll(mhyaclClip.m_ClipData, out values, out times);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
values = Array.Empty<float>();
|
||||||
|
times = Array.Empty<float>();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -718,6 +718,8 @@ namespace AssetStudio
|
|||||||
dest = 1;
|
dest = 1;
|
||||||
else if (Game.Type.IsSRGroup() && texEnv.Key.Contains("Pack"))
|
else if (Game.Type.IsSRGroup() && texEnv.Key.Contains("Pack"))
|
||||||
dest = 0;
|
dest = 0;
|
||||||
|
else if (Game.Type.IsArknightsEndfield() && texEnv.Key == "_BaseMap")
|
||||||
|
dest = 0;
|
||||||
|
|
||||||
texture.Dest = dest;
|
texture.Dest = dest;
|
||||||
|
|
||||||
@@ -896,15 +898,15 @@ namespace AssetStudio
|
|||||||
var streamedFrames = m_Clip.m_StreamedClip.ReadData();
|
var streamedFrames = m_Clip.m_StreamedClip.ReadData();
|
||||||
var m_ClipBindingConstant = animationClip.m_ClipBindingConstant ?? m_Clip.ConvertValueArrayToGenericBinding();
|
var m_ClipBindingConstant = animationClip.m_ClipBindingConstant ?? m_Clip.ConvertValueArrayToGenericBinding();
|
||||||
var m_ACLClip = m_Clip.m_ACLClip;
|
var m_ACLClip = m_Clip.m_ACLClip;
|
||||||
var aclCount = m_ACLClip.m_CurveCount;
|
var aclCount = m_ACLClip.CurveCount;
|
||||||
if (!m_ACLClip.m_ClipData.IsNullOrEmpty() && !Game.Type.IsSRGroup())
|
if (m_ACLClip.IsSet && !Game.Type.IsSRGroup())
|
||||||
{
|
{
|
||||||
m_ACLClip.Process(Game, out var values, out var times);
|
m_ACLClip.Process(Game, out var values, out var times);
|
||||||
for (int frameIndex = 0; frameIndex < times.Length; frameIndex++)
|
for (int frameIndex = 0; frameIndex < times.Length; frameIndex++)
|
||||||
{
|
{
|
||||||
var time = times[frameIndex];
|
var time = times[frameIndex];
|
||||||
var frameOffset = frameIndex * m_ACLClip.m_CurveCount;
|
var frameOffset = frameIndex * m_ACLClip.CurveCount;
|
||||||
for (int curveIndex = 0; curveIndex < m_ACLClip.m_CurveCount;)
|
for (int curveIndex = 0; curveIndex < m_ACLClip.CurveCount;)
|
||||||
{
|
{
|
||||||
var index = curveIndex;
|
var index = curveIndex;
|
||||||
ReadCurveData(iAnim, m_ClipBindingConstant, index, time, values, (int)frameOffset, ref curveIndex);
|
ReadCurveData(iAnim, m_ClipBindingConstant, index, time, values, (int)frameOffset, ref curveIndex);
|
||||||
@@ -938,14 +940,14 @@ namespace AssetStudio
|
|||||||
ReadCurveData(iAnim, m_ClipBindingConstant, (int)index, time, m_DenseClip.m_SampleArray, (int)frameOffset, ref curveIndex);
|
ReadCurveData(iAnim, m_ClipBindingConstant, (int)index, time, m_DenseClip.m_SampleArray, (int)frameOffset, ref curveIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!m_ACLClip.m_ClipData.IsNullOrEmpty() && Game.Type.IsSRGroup())
|
if (m_ACLClip.IsSet && Game.Type.IsSRGroup())
|
||||||
{
|
{
|
||||||
m_ACLClip.Process(Game, out var values, out var times);
|
m_ACLClip.Process(Game, out var values, out var times);
|
||||||
for (int frameIndex = 0; frameIndex < times.Length; frameIndex++)
|
for (int frameIndex = 0; frameIndex < times.Length; frameIndex++)
|
||||||
{
|
{
|
||||||
var time = times[frameIndex];
|
var time = times[frameIndex];
|
||||||
var frameOffset = frameIndex * m_ACLClip.m_CurveCount;
|
var frameOffset = frameIndex * m_ACLClip.CurveCount;
|
||||||
for (int curveIndex = 0; curveIndex < m_ACLClip.m_CurveCount;)
|
for (int curveIndex = 0; curveIndex < m_ACLClip.CurveCount;)
|
||||||
{
|
{
|
||||||
var index = (int)(curveIndex + m_DenseClip.m_CurveCount + streamCount);
|
var index = (int)(curveIndex + m_DenseClip.m_CurveCount + streamCount);
|
||||||
ReadCurveData(iAnim, m_ClipBindingConstant, index, time, values, (int)frameOffset, ref curveIndex);
|
ReadCurveData(iAnim, m_ClipBindingConstant, index, time, values, (int)frameOffset, ref curveIndex);
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ namespace AssetStudio
|
|||||||
var lastSampleFrame = streamedFrames.Count > 1 ? streamedFrames[streamedFrames.Count - 2].time : 0.0f;
|
var lastSampleFrame = streamedFrames.Count > 1 ? streamedFrames[streamedFrames.Count - 2].time : 0.0f;
|
||||||
var lastFrame = Math.Max(lastDenseFrame, lastSampleFrame);
|
var lastFrame = Math.Max(lastDenseFrame, lastSampleFrame);
|
||||||
|
|
||||||
if (!m_Clip.m_ACLClip.m_ClipData.IsNullOrEmpty() && !game.Type.IsSRGroup())
|
if (m_Clip.m_ACLClip.IsSet && !game.Type.IsSRGroup())
|
||||||
{
|
{
|
||||||
var lastACLFrame = ProcessACLClip(m_Clip, bindings, tos);
|
var lastACLFrame = ProcessACLClip(m_Clip, bindings, tos);
|
||||||
lastFrame = Math.Max(lastFrame, lastACLFrame);
|
lastFrame = Math.Max(lastFrame, lastACLFrame);
|
||||||
@@ -65,7 +65,7 @@ namespace AssetStudio
|
|||||||
}
|
}
|
||||||
ProcessStreams(streamedFrames, bindings, tos, m_Clip.m_DenseClip.m_SampleRate);
|
ProcessStreams(streamedFrames, bindings, tos, m_Clip.m_DenseClip.m_SampleRate);
|
||||||
ProcessDenses(m_Clip, bindings, tos);
|
ProcessDenses(m_Clip, bindings, tos);
|
||||||
if (!m_Clip.m_ACLClip.m_ClipData.IsNullOrEmpty() && game.Type.IsSRGroup())
|
if (m_Clip.m_ACLClip.IsSet && game.Type.IsSRGroup())
|
||||||
{
|
{
|
||||||
var lastACLFrame = ProcessACLClip(m_Clip, bindings, tos);
|
var lastACLFrame = ProcessACLClip(m_Clip, bindings, tos);
|
||||||
lastFrame = Math.Max(lastFrame, lastACLFrame);
|
lastFrame = Math.Max(lastFrame, lastACLFrame);
|
||||||
@@ -112,7 +112,7 @@ namespace AssetStudio
|
|||||||
var curve = frame.keyList[curveIndex];
|
var curve = frame.keyList[curveIndex];
|
||||||
var index = curve.index;
|
var index = curve.index;
|
||||||
if (!game.Type.IsSRGroup())
|
if (!game.Type.IsSRGroup())
|
||||||
index += (int)animationClip.m_MuscleClip.m_Clip.m_ACLClip.m_CurveCount;
|
index += (int)animationClip.m_MuscleClip.m_Clip.m_ACLClip.CurveCount;
|
||||||
var binding = bindings.FindBinding(index);
|
var binding = bindings.FindBinding(index);
|
||||||
|
|
||||||
var path = GetCurvePath(tos, binding.path);
|
var path = GetCurvePath(tos, binding.path);
|
||||||
@@ -161,7 +161,7 @@ namespace AssetStudio
|
|||||||
{
|
{
|
||||||
var index = (int)streamCount + curveIndex;
|
var index = (int)streamCount + curveIndex;
|
||||||
if (!game.Type.IsSRGroup())
|
if (!game.Type.IsSRGroup())
|
||||||
index += (int)clip.m_ACLClip.m_CurveCount;
|
index += (int)clip.m_ACLClip.CurveCount;
|
||||||
var binding = bindings.FindBinding(index);
|
var binding = bindings.FindBinding(index);
|
||||||
var path = GetCurvePath(tos, binding.path);
|
var path = GetCurvePath(tos, binding.path);
|
||||||
var framePosition = frameOffset + curveIndex;
|
var framePosition = frameOffset + curveIndex;
|
||||||
@@ -193,8 +193,8 @@ namespace AssetStudio
|
|||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
|
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
|
||||||
{
|
{
|
||||||
float time = times[frameIndex];
|
float time = times[frameIndex];
|
||||||
int frameOffset = frameIndex * (int)acl.m_CurveCount;
|
int frameOffset = frameIndex * (int)acl.CurveCount;
|
||||||
for (int curveIndex = 0; curveIndex < acl.m_CurveCount;)
|
for (int curveIndex = 0; curveIndex < acl.CurveCount;)
|
||||||
{
|
{
|
||||||
var index = curveIndex;
|
var index = curveIndex;
|
||||||
if (game.Type.IsSRGroup())
|
if (game.Type.IsSRGroup())
|
||||||
@@ -236,8 +236,8 @@ namespace AssetStudio
|
|||||||
for (var curveIndex = 0; curveIndex < constant.data.Length;)
|
for (var curveIndex = 0; curveIndex < constant.data.Length;)
|
||||||
{
|
{
|
||||||
var index = (int)(streamCount + denseCount + curveIndex);
|
var index = (int)(streamCount + denseCount + curveIndex);
|
||||||
if (!clip.m_ACLClip.m_ClipData.IsNullOrEmpty())
|
if (clip.m_ACLClip.IsSet)
|
||||||
index += (int)clip.m_ACLClip.m_CurveCount;
|
index += (int)clip.m_ACLClip.CurveCount;
|
||||||
GenericBinding binding = bindings.FindBinding(index);
|
GenericBinding binding = bindings.FindBinding(index);
|
||||||
string path = GetCurvePath(tos, binding.path);
|
string path = GetCurvePath(tos, binding.path);
|
||||||
if (binding.typeID == ClassIDType.Transform)
|
if (binding.typeID == ClassIDType.Transform)
|
||||||
|
|||||||
@@ -634,36 +634,36 @@ namespace AssetStudio
|
|||||||
|
|
||||||
public static string FindPropertyNameByCRC28(this Material material, uint crc)
|
public static string FindPropertyNameByCRC28(this Material material, uint crc)
|
||||||
{
|
{
|
||||||
foreach (var property in material.m_SavedProperties.m_TexEnvs.Keys)
|
foreach (var property in material.m_SavedProperties.m_TexEnvs)
|
||||||
{
|
{
|
||||||
string hdrName = $"{property}_HDR";
|
string hdrName = $"{property.Key}_HDR";
|
||||||
if (CRC.Verify28DigestUTF8(hdrName, crc))
|
if (CRC.Verify28DigestUTF8(hdrName, crc))
|
||||||
{
|
{
|
||||||
return hdrName;
|
return hdrName;
|
||||||
}
|
}
|
||||||
string stName = $"{property}_ST";
|
string stName = $"{property.Key}_ST";
|
||||||
if (CRC.Verify28DigestUTF8(stName, crc))
|
if (CRC.Verify28DigestUTF8(stName, crc))
|
||||||
{
|
{
|
||||||
return stName;
|
return stName;
|
||||||
}
|
}
|
||||||
string texelName = $"{property}_TexelSize";
|
string texelName = $"{property.Key}_TexelSize";
|
||||||
if (CRC.Verify28DigestUTF8(texelName, crc))
|
if (CRC.Verify28DigestUTF8(texelName, crc))
|
||||||
{
|
{
|
||||||
return texelName;
|
return texelName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (var property in material.m_SavedProperties.m_Floats.Keys)
|
foreach (var property in material.m_SavedProperties.m_Floats)
|
||||||
{
|
{
|
||||||
if (CRC.Verify28DigestUTF8(property, crc))
|
if (CRC.Verify28DigestUTF8(property.Key, crc))
|
||||||
{
|
{
|
||||||
return property;
|
return property.Key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (var property in material.m_SavedProperties.m_Colors.Keys)
|
foreach (var property in material.m_SavedProperties.m_Colors)
|
||||||
{
|
{
|
||||||
if (CRC.Verify28DigestUTF8(property, crc))
|
if (CRC.Verify28DigestUTF8(property.Key, crc))
|
||||||
{
|
{
|
||||||
return property;
|
return property.Key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ namespace AssetStudio
|
|||||||
Lz4HC,
|
Lz4HC,
|
||||||
Lzham,
|
Lzham,
|
||||||
Lz4Mr0k,
|
Lz4Mr0k,
|
||||||
|
Lz4Inv = 5,
|
||||||
Zstd = 5
|
Zstd = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -542,6 +543,10 @@ namespace AssetStudio
|
|||||||
{
|
{
|
||||||
NetEaseUtils.Decrypt(compressedBytesSpan);
|
NetEaseUtils.Decrypt(compressedBytesSpan);
|
||||||
}
|
}
|
||||||
|
if (Game.Type.IsArknightsEndfield() && i == 0)
|
||||||
|
{
|
||||||
|
FairGuardUtils.Decrypt(compressedBytesSpan);
|
||||||
|
}
|
||||||
if (Game.Type.IsOPFP())
|
if (Game.Type.IsOPFP())
|
||||||
{
|
{
|
||||||
OPFPUtils.Decrypt(compressedBytesSpan, reader.FullPath);
|
OPFPUtils.Decrypt(compressedBytesSpan, reader.FullPath);
|
||||||
@@ -559,6 +564,26 @@ namespace AssetStudio
|
|||||||
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
|
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case CompressionType.Lz4Inv when Game.Type.IsArknightsEndfield():
|
||||||
|
{
|
||||||
|
var compressedSize = (int)blockInfo.compressedSize;
|
||||||
|
var compressedBytes = BigArrayPool<byte>.Shared.Rent(compressedSize);
|
||||||
|
reader.Read(compressedBytes, 0, compressedSize);
|
||||||
|
var compressedBytesSpan = compressedBytes.AsSpan(0, compressedSize);
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
FairGuardUtils.Decrypt(compressedBytesSpan);
|
||||||
|
|
||||||
|
}
|
||||||
|
var uncompressedSize = (int)blockInfo.uncompressedSize;
|
||||||
|
var uncompressedBytes = BigArrayPool<byte>.Shared.Rent(uncompressedSize);
|
||||||
|
var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize);
|
||||||
|
FairGuardUtils.Lz4.Decompress(compressedBytesSpan, uncompressedBytesSpan);
|
||||||
|
blocksStream.Write(uncompressedBytes, 0, uncompressedSize);
|
||||||
|
BigArrayPool<byte>.Shared.Return(compressedBytes);
|
||||||
|
BigArrayPool<byte>.Shared.Return(uncompressedBytes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case CompressionType.Zstd when !Game.Type.IsMhyGroup(): //Zstd
|
case CompressionType.Zstd when !Game.Type.IsMhyGroup(): //Zstd
|
||||||
{
|
{
|
||||||
var compressedSize = (int)blockInfo.compressedSize;
|
var compressedSize = (int)blockInfo.compressedSize;
|
||||||
|
|||||||
@@ -783,22 +783,71 @@ namespace AssetStudio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ACLClip
|
public abstract class ACLClip
|
||||||
{
|
{
|
||||||
public byte[] m_ClipData;
|
public virtual bool IsSet => false;
|
||||||
public byte[] m_DatabaseData;
|
public virtual uint CurveCount => 0;
|
||||||
|
public abstract void Read(ObjectReader reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class EmptyACLClip : ACLClip
|
||||||
|
{
|
||||||
|
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<byte>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
public uint m_CurveCount;
|
||||||
public uint m_ConstCurveCount;
|
public uint m_ConstCurveCount;
|
||||||
|
|
||||||
public ACLClip()
|
public byte[] m_ClipData;
|
||||||
|
|
||||||
|
public override bool IsSet => !m_ClipData.IsNullOrEmpty();
|
||||||
|
public override uint CurveCount => m_CurveCount;
|
||||||
|
|
||||||
|
public MHYACLClip()
|
||||||
{
|
{
|
||||||
m_CurveCount = 0;
|
m_CurveCount = 0;
|
||||||
m_ConstCurveCount = 0;
|
m_ConstCurveCount = 0;
|
||||||
m_ClipData = Array.Empty<byte>();
|
m_ClipData = Array.Empty<byte>();
|
||||||
m_DatabaseData = Array.Empty<byte>();
|
|
||||||
}
|
}
|
||||||
public void Read(ObjectReader reader)
|
public override void Read(ObjectReader reader)
|
||||||
{
|
{
|
||||||
var byteCount = reader.ReadInt32();
|
var byteCount = reader.ReadInt32();
|
||||||
|
|
||||||
@@ -817,7 +866,28 @@ namespace AssetStudio
|
|||||||
m_ConstCurveCount = reader.ReadUInt32();
|
m_ConstCurveCount = reader.ReadUInt32();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void ParseGI(ObjectReader reader)
|
}
|
||||||
|
|
||||||
|
public class GIACLClip : ACLClip
|
||||||
|
{
|
||||||
|
public uint m_CurveCount;
|
||||||
|
public uint m_ConstCurveCount;
|
||||||
|
|
||||||
|
public byte[] m_ClipData;
|
||||||
|
public byte[] m_DatabaseData;
|
||||||
|
|
||||||
|
public override bool IsSet => !m_ClipData.IsNullOrEmpty() && !m_DatabaseData.IsNullOrEmpty();
|
||||||
|
public override uint CurveCount => m_CurveCount;
|
||||||
|
|
||||||
|
public GIACLClip()
|
||||||
|
{
|
||||||
|
m_CurveCount = 0;
|
||||||
|
m_ConstCurveCount = 0;
|
||||||
|
m_ClipData = Array.Empty<byte>();
|
||||||
|
m_DatabaseData = Array.Empty<byte>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Read(ObjectReader reader)
|
||||||
{
|
{
|
||||||
var aclTracksCount = (int)reader.ReadUInt64();
|
var aclTracksCount = (int)reader.ReadUInt64();
|
||||||
var aclTracksOffset = reader.Position + reader.ReadInt64();
|
var aclTracksOffset = reader.Position + reader.ReadInt64();
|
||||||
@@ -1099,7 +1169,7 @@ namespace AssetStudio
|
|||||||
|
|
||||||
public class Clip
|
public class Clip
|
||||||
{
|
{
|
||||||
public ACLClip m_ACLClip = new();
|
public ACLClip m_ACLClip = new EmptyACLClip();
|
||||||
public StreamedClip m_StreamedClip;
|
public StreamedClip m_StreamedClip;
|
||||||
public DenseClip m_DenseClip;
|
public DenseClip m_DenseClip;
|
||||||
public ConstantClip m_ConstantClip;
|
public ConstantClip m_ConstantClip;
|
||||||
@@ -1113,6 +1183,12 @@ namespace AssetStudio
|
|||||||
m_DenseClip = new DenseClip(reader);
|
m_DenseClip = new DenseClip(reader);
|
||||||
if (reader.Game.Type.IsSRGroup())
|
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);
|
m_ACLClip.Read(reader);
|
||||||
}
|
}
|
||||||
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
|
if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up
|
||||||
@@ -1121,6 +1197,7 @@ namespace AssetStudio
|
|||||||
}
|
}
|
||||||
if (reader.Game.Type.IsGIGroup() || reader.Game.Type.IsBH3Group() || reader.Game.Type.IsZZZCB1())
|
if (reader.Game.Type.IsGIGroup() || reader.Game.Type.IsBH3Group() || reader.Game.Type.IsZZZCB1())
|
||||||
{
|
{
|
||||||
|
m_ACLClip = new MHYACLClip();
|
||||||
m_ACLClip.Read(reader);
|
m_ACLClip.Read(reader);
|
||||||
}
|
}
|
||||||
if (version[0] < 2018 || (version[0] == 2018 && version[1] < 3)) //2018.3 down
|
if (version[0] < 2018 || (version[0] == 2018 && version[1] < 3)) //2018.3 down
|
||||||
@@ -1143,7 +1220,8 @@ namespace AssetStudio
|
|||||||
clip.m_StreamedClip = StreamedClip.ParseGI(reader);
|
clip.m_StreamedClip = StreamedClip.ParseGI(reader);
|
||||||
clip.m_DenseClip = DenseClip.ParseGI(reader);
|
clip.m_DenseClip = DenseClip.ParseGI(reader);
|
||||||
clip.m_ConstantClip = ConstantClip.ParseGI(reader);
|
clip.m_ConstantClip = ConstantClip.ParseGI(reader);
|
||||||
clip.m_ACLClip.ParseGI(reader);
|
clip.m_ACLClip = new GIACLClip();
|
||||||
|
clip.m_ACLClip.Read(reader);
|
||||||
|
|
||||||
reader.Position = pos;
|
reader.Position = pos;
|
||||||
|
|
||||||
@@ -1683,6 +1761,10 @@ namespace AssetStudio
|
|||||||
|
|
||||||
m_SampleRate = reader.ReadSingle();
|
m_SampleRate = reader.ReadSingle();
|
||||||
m_WrapMode = reader.ReadInt32();
|
m_WrapMode = reader.ReadInt32();
|
||||||
|
if (reader.Game.Type.IsArknightsEndfield())
|
||||||
|
{
|
||||||
|
var m_aclType = reader.ReadInt32();
|
||||||
|
}
|
||||||
if (version[0] > 3 || (version[0] == 3 && version[1] >= 4)) //3.4 and up
|
if (version[0] > 3 || (version[0] == 3 && version[1] >= 4)) //3.4 and up
|
||||||
{
|
{
|
||||||
m_Bounds = new AABB(reader);
|
m_Bounds = new AABB(reader);
|
||||||
@@ -1746,14 +1828,16 @@ namespace AssetStudio
|
|||||||
m_StreamData = new StreamingInfo(reader);
|
m_StreamData = new StreamingInfo(reader);
|
||||||
if (!string.IsNullOrEmpty(m_StreamData?.path))
|
if (!string.IsNullOrEmpty(m_StreamData?.path))
|
||||||
{
|
{
|
||||||
|
var aclClip = m_MuscleClip.m_Clip.m_ACLClip as GIACLClip;
|
||||||
|
|
||||||
var resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size);
|
var resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size);
|
||||||
var ms = new MemoryStream();
|
var ms = new MemoryStream();
|
||||||
ms.Write(m_MuscleClip.m_Clip.m_ACLClip.m_DatabaseData);
|
ms.Write(aclClip.m_DatabaseData);
|
||||||
|
|
||||||
ms.Write(resourceReader.GetData());
|
ms.Write(resourceReader.GetData());
|
||||||
ms.AlignStream();
|
ms.AlignStream();
|
||||||
|
|
||||||
m_MuscleClip.m_Clip.m_ACLClip.m_DatabaseData = ms.ToArray();
|
aclClip.m_DatabaseData = ms.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -291,6 +291,10 @@ namespace AssetStudio
|
|||||||
|| (version[0] == 4 && version[1] == 1 && version[2] >= 3)) //4.1.3 and up
|
|| (version[0] == 4 && version[1] == 1 && version[2] >= 3)) //4.1.3 and up
|
||||||
{
|
{
|
||||||
m_CycleOffset = reader.ReadSingle();
|
m_CycleOffset = reader.ReadSingle();
|
||||||
|
if (reader.Game.Type.IsArknightsEndfield())
|
||||||
|
{
|
||||||
|
var m_StateNameHash = reader.ReadUInt32();
|
||||||
|
}
|
||||||
m_Mirror = reader.ReadBoolean();
|
m_Mirror = reader.ReadBoolean();
|
||||||
reader.AlignStream();
|
reader.AlignStream();
|
||||||
}
|
}
|
||||||
@@ -411,6 +415,12 @@ namespace AssetStudio
|
|||||||
m_Mirror = reader.ReadBoolean();
|
m_Mirror = reader.ReadBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reader.Game.Type.IsArknightsEndfield())
|
||||||
|
{
|
||||||
|
var m_SyncGroupID = reader.ReadUInt32();
|
||||||
|
var m_SyncGroupRole = reader.ReadUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
reader.AlignStream();
|
reader.AlignStream();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,49 +13,53 @@ namespace AssetStudio
|
|||||||
m_Texture = new PPtr<Texture>(reader);
|
m_Texture = new PPtr<Texture>(reader);
|
||||||
m_Scale = reader.ReadVector2();
|
m_Scale = reader.ReadVector2();
|
||||||
m_Offset = reader.ReadVector2();
|
m_Offset = reader.ReadVector2();
|
||||||
|
if (reader.Game.Type.IsArknightsEndfield())
|
||||||
|
{
|
||||||
|
var m_UVSetIndex = reader.ReadInt32();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UnityPropertySheet
|
public class UnityPropertySheet
|
||||||
{
|
{
|
||||||
public Dictionary<string, UnityTexEnv> m_TexEnvs;
|
public KeyValuePair<string, UnityTexEnv>[] m_TexEnvs;
|
||||||
public Dictionary<string, int> m_Ints;
|
public KeyValuePair<string, int>[] m_Ints;
|
||||||
public Dictionary<string, float> m_Floats;
|
public KeyValuePair<string, float>[] m_Floats;
|
||||||
public Dictionary<string, Color> m_Colors;
|
public KeyValuePair<string, Color>[] m_Colors;
|
||||||
|
|
||||||
public UnityPropertySheet(ObjectReader reader)
|
public UnityPropertySheet(ObjectReader reader)
|
||||||
{
|
{
|
||||||
var version = reader.version;
|
var version = reader.version;
|
||||||
|
|
||||||
int m_TexEnvsSize = reader.ReadInt32();
|
int m_TexEnvsSize = reader.ReadInt32();
|
||||||
m_TexEnvs = new Dictionary<string, UnityTexEnv>(m_TexEnvsSize);
|
m_TexEnvs = new KeyValuePair<string, UnityTexEnv>[m_TexEnvsSize];
|
||||||
for (int i = 0; i < m_TexEnvsSize; i++)
|
for (int i = 0; i < m_TexEnvsSize; i++)
|
||||||
{
|
{
|
||||||
m_TexEnvs.Add(reader.ReadAlignedString(), new UnityTexEnv(reader));
|
m_TexEnvs[i] = new(reader.ReadAlignedString(), new UnityTexEnv(reader));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version[0] >= 2021) //2021.1 and up
|
if (version[0] >= 2021) //2021.1 and up
|
||||||
{
|
{
|
||||||
int m_IntsSize = reader.ReadInt32();
|
int m_IntsSize = reader.ReadInt32();
|
||||||
m_Ints = new Dictionary<string, int>(m_IntsSize);
|
m_Ints = new KeyValuePair<string, int>[m_IntsSize];
|
||||||
for (int i = 0; i < m_IntsSize; i++)
|
for (int i = 0; i < m_IntsSize; i++)
|
||||||
{
|
{
|
||||||
m_Ints.Add(reader.ReadAlignedString(), reader.ReadInt32());
|
m_Ints[i] = new(reader.ReadAlignedString(), reader.ReadInt32());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int m_FloatsSize = reader.ReadInt32();
|
int m_FloatsSize = reader.ReadInt32();
|
||||||
m_Floats = new Dictionary<string, float>(m_FloatsSize);
|
m_Floats = new KeyValuePair<string, float>[m_FloatsSize];
|
||||||
for (int i = 0; i < m_FloatsSize; i++)
|
for (int i = 0; i < m_FloatsSize; i++)
|
||||||
{
|
{
|
||||||
m_Floats.Add(reader.ReadAlignedString(), reader.ReadSingle());
|
m_Floats[i] = new(reader.ReadAlignedString(), reader.ReadSingle());
|
||||||
}
|
}
|
||||||
|
|
||||||
int m_ColorsSize = reader.ReadInt32();
|
int m_ColorsSize = reader.ReadInt32();
|
||||||
m_Colors = new Dictionary<string, Color>(m_ColorsSize);
|
m_Colors = new KeyValuePair<string, Color>[m_ColorsSize];
|
||||||
for (int i = 0; i < m_ColorsSize; i++)
|
for (int i = 0; i < m_ColorsSize; i++)
|
||||||
{
|
{
|
||||||
m_Colors.Add(reader.ReadAlignedString(), reader.ReadColor4());
|
m_Colors[i] = new(reader.ReadAlignedString(), reader.ReadColor4());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -471,6 +471,7 @@ namespace AssetStudio
|
|||||||
private VertexData m_VertexData;
|
private VertexData m_VertexData;
|
||||||
private CompressedMesh m_CompressedMesh;
|
private CompressedMesh m_CompressedMesh;
|
||||||
private StreamingInfo m_StreamData;
|
private StreamingInfo m_StreamData;
|
||||||
|
private bool m_CollisionMeshBaked = false;
|
||||||
|
|
||||||
public List<uint> m_Indices = new List<uint>();
|
public List<uint> m_Indices = new List<uint>();
|
||||||
|
|
||||||
@@ -549,6 +550,12 @@ namespace AssetStudio
|
|||||||
}
|
}
|
||||||
var m_KeepVertices = reader.ReadBoolean();
|
var m_KeepVertices = reader.ReadBoolean();
|
||||||
var m_KeepIndices = reader.ReadBoolean();
|
var m_KeepIndices = reader.ReadBoolean();
|
||||||
|
if (reader.Game.Type.IsArknightsEndfield())
|
||||||
|
{
|
||||||
|
var m_CollisionMeshOnly = reader.ReadBoolean();
|
||||||
|
m_CollisionMeshBaked = reader.ReadBoolean();
|
||||||
|
var m_CollisionMeshConvex = reader.ReadBoolean();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
reader.AlignStream();
|
reader.AlignStream();
|
||||||
if (reader.Game.Type.IsGISubGroup())
|
if (reader.Game.Type.IsGISubGroup())
|
||||||
@@ -641,7 +648,7 @@ namespace AssetStudio
|
|||||||
m_VertexData = new VertexData(reader);
|
m_VertexData = new VertexData(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version[0] > 2 || (version[0] == 2 && version[1] >= 6)) //2.6.0 and later
|
if ((version[0] > 2 || (version[0] == 2 && version[1] >= 6)) && !m_CollisionMeshBaked) //2.6.0 and later
|
||||||
{
|
{
|
||||||
m_CompressedMesh = new CompressedMesh(reader);
|
m_CompressedMesh = new CompressedMesh(reader);
|
||||||
}
|
}
|
||||||
@@ -696,6 +703,10 @@ namespace AssetStudio
|
|||||||
var m_MeshMetrics = new float[2];
|
var m_MeshMetrics = new float[2];
|
||||||
m_MeshMetrics[0] = reader.ReadSingle();
|
m_MeshMetrics[0] = reader.ReadSingle();
|
||||||
m_MeshMetrics[1] = reader.ReadSingle();
|
m_MeshMetrics[1] = reader.ReadSingle();
|
||||||
|
if (reader.Game.Type.IsArknightsEndfield())
|
||||||
|
{
|
||||||
|
var m_MeshMetrics2 = reader.ReadSingle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reader.Game.Type.IsGIGroup())
|
if (reader.Game.Type.IsGIGroup())
|
||||||
@@ -735,7 +746,12 @@ namespace AssetStudio
|
|||||||
ReadVertexData();
|
ReadVertexData();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version[0] > 2 || (version[0] == 2 && version[1] >= 6)) //2.6.0 and later
|
if (m_CollisionMeshBaked)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((version[0] > 2 || (version[0] == 2 && version[1] >= 6))) //2.6.0 and later
|
||||||
{
|
{
|
||||||
DecompressCompressedMesh();
|
DecompressCompressedMesh();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,6 +98,12 @@ namespace AssetStudio
|
|||||||
if (version[0] >= 2021) //2021.1 and up
|
if (version[0] >= 2021) //2021.1 and up
|
||||||
{
|
{
|
||||||
var m_StaticShadowCaster = reader.ReadByte();
|
var m_StaticShadowCaster = reader.ReadByte();
|
||||||
|
if (reader.Game.Type.IsArknightsEndfield())
|
||||||
|
{
|
||||||
|
var m_RealtimeShadowCaster = reader.ReadByte();
|
||||||
|
var m_SubMeshRenderMode = reader.ReadByte();
|
||||||
|
var m_CharacterIndex = reader.ReadByte();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var m_MotionVectors = reader.ReadByte();
|
var m_MotionVectors = reader.ReadByte();
|
||||||
var m_LightProbeUsage = reader.ReadByte();
|
var m_LightProbeUsage = reader.ReadByte();
|
||||||
|
|||||||
305
AssetStudio/Crypto/FairGuardUtils.cs
Normal file
305
AssetStudio/Crypto/FairGuardUtils.cs
Normal file
@@ -0,0 +1,305 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace AssetStudio
|
||||||
|
{
|
||||||
|
//Special thanks to LukeFZ#4035.
|
||||||
|
public static class FairGuardUtils
|
||||||
|
{
|
||||||
|
public static void Decrypt(Span<byte> bytes)
|
||||||
|
{
|
||||||
|
Logger.Verbose($"Attempting to decrypt block with FairGuard encryption...");
|
||||||
|
|
||||||
|
var encryptedOffset = 0;
|
||||||
|
var encryptedSize = bytes.Length > 0x500 ? 0x500 : bytes.Length;
|
||||||
|
|
||||||
|
if (encryptedSize < 0x20)
|
||||||
|
{
|
||||||
|
Logger.Verbose($"block size is less that minimum, skipping...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var encrypted = bytes.Slice(encryptedOffset, encryptedSize);
|
||||||
|
var encryptedInts = MemoryMarshal.Cast<byte, int>(encrypted);
|
||||||
|
|
||||||
|
for (int i = 0; i < 0x20; i++)
|
||||||
|
{
|
||||||
|
encrypted[i] ^= 0xA6;
|
||||||
|
}
|
||||||
|
|
||||||
|
var seedPart0 = (uint)(encryptedInts[2] ^ 0x1274CBEC ^ encryptedInts[6] ^ 0x3F72EAF3);
|
||||||
|
var seedPart1 = (uint)(encryptedInts[3] ^ 0xBE482704 ^ encryptedInts[0] ^ encryptedSize);
|
||||||
|
var seedPart2 = (uint)(encryptedInts[1] ^ encryptedSize ^ encryptedInts[5] ^ 0x753BDCAA);
|
||||||
|
var seedPart3 = (uint)(encryptedInts[0] ^ 0x82C57E3C ^ encryptedInts[7] ^ 0xE3D947D3);
|
||||||
|
var seedPart4 = (uint)(encryptedInts[4] ^ 0x6F2A7347 ^ encryptedInts[7] ^ 0x4736C714);
|
||||||
|
|
||||||
|
var seedInts = new uint[] { seedPart0, seedPart1, seedPart2, seedPart3, seedPart4 };
|
||||||
|
var seedBytes = MemoryMarshal.AsBytes<uint>(seedInts);
|
||||||
|
|
||||||
|
var seed = GenerateSeed(seedBytes);
|
||||||
|
var seedBuffer = BitConverter.GetBytes(seed);
|
||||||
|
seed = CRC.CalculateDigest(seedBuffer, 0, (uint)seedBuffer.Length);
|
||||||
|
|
||||||
|
var key = seedInts[0] ^ seedInts[1] ^ seedInts[2] ^ seedInts[3] ^ seedInts[4] ^ (uint)encryptedSize;
|
||||||
|
|
||||||
|
RC4(seedBytes, key);
|
||||||
|
var keySeed = CRC.CalculateDigest(seedBytes.ToArray(), 0, (uint)seedBytes.Length);
|
||||||
|
var keySeedBytes = BitConverter.GetBytes(keySeed);
|
||||||
|
keySeed = GenerateSeed(keySeedBytes);
|
||||||
|
|
||||||
|
var keyPart0 = (seedInts[3] - 0x1C26B82D) ^ keySeed;
|
||||||
|
var keyPart1 = (seedInts[2] + 0x3F72EAF3) ^ seed;
|
||||||
|
var keyPart2 = seedInts[0] ^ 0x82C57E3C ^ keySeed;
|
||||||
|
var keyPart3 = (seedInts[1] + 0x6F2A7347) ^ seed;
|
||||||
|
var keyVector = new uint[] { keyPart0, keyPart1, keyPart2, keyPart3 };
|
||||||
|
|
||||||
|
var block = encrypted[0x20..];
|
||||||
|
if (block.Length >= 0x80)
|
||||||
|
{
|
||||||
|
RC4(block[..0x60], seed);
|
||||||
|
for (int i = 0; i < 0x60; i++)
|
||||||
|
{
|
||||||
|
block[i] ^= (byte)(seed ^ 0x6E);
|
||||||
|
}
|
||||||
|
|
||||||
|
block = block[0x60..];
|
||||||
|
var blockSize = (encryptedSize - 0x80) / 4;
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
var blockOffset = i * blockSize;
|
||||||
|
var blockKey = i switch
|
||||||
|
{
|
||||||
|
0 => 0x6142756Eu,
|
||||||
|
1 => 0x62496E66u,
|
||||||
|
2 => 0x1304B000u,
|
||||||
|
3 => 0x6E8E30ECu,
|
||||||
|
_ => throw new NotImplementedException()
|
||||||
|
};
|
||||||
|
RC4(block.Slice(blockOffset, blockSize), seed);
|
||||||
|
var blockInts = MemoryMarshal.Cast<byte, uint>(block[blockOffset..]);
|
||||||
|
for (int j = 0; j < blockSize / 4; j++)
|
||||||
|
{
|
||||||
|
blockInts[j] ^= seed ^ keyVector[i] ^ blockKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RC4(block, seed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint GenerateSeed(Span<byte> bytes)
|
||||||
|
{
|
||||||
|
var state = new uint[] { 0xC1646153, 0x78DA0550, 0x2947E56B };
|
||||||
|
for (int i = 0; i < bytes.Length; i++)
|
||||||
|
{
|
||||||
|
state[0] = 0x21 * state[0] + bytes[i];
|
||||||
|
if ((state[0] & 0xF) >= 0xB)
|
||||||
|
{
|
||||||
|
state[0] = (state[0] ^ RotateIsSet(state[2], 6)) - 0x2CD86315;
|
||||||
|
}
|
||||||
|
else if ((state[0] & 0xF0) >> 4 > 0xE)
|
||||||
|
{
|
||||||
|
state[0] = (state[1] ^ 0xAB4A010B) + (state[0] ^ RotateIsSet(state[2], 9));
|
||||||
|
}
|
||||||
|
else if ((state[0] & 0xF00) >> 8 < 2)
|
||||||
|
{
|
||||||
|
state[1] = ((state[2] >> 3) - 0x55EEAB7B) ^ state[0];
|
||||||
|
}
|
||||||
|
else if (state[1] + 0x567A >= 0xAB5489E4)
|
||||||
|
{
|
||||||
|
state[1] = (state[1] >> 16) ^ state[0];
|
||||||
|
}
|
||||||
|
else if ((state[1] ^ 0x738766FA) <= state[2])
|
||||||
|
{
|
||||||
|
state[1] = (state[1] >> 8) ^ state[2];
|
||||||
|
}
|
||||||
|
else if (state[1] == 0x68F53AA6)
|
||||||
|
{
|
||||||
|
if ((state[1] ^ (state[0] + state[2])) > 0x594AF86E)
|
||||||
|
{
|
||||||
|
state[1] -= 0x8CA292E;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state[2] -= 0x760A1649;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (state[0] > 0x865703AF)
|
||||||
|
{
|
||||||
|
state[1] = state[2] ^ (state[0] - 0x564389D7);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state[1] = (state[1] - 0x12B9DD92) ^ state[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
state[0] ^= RotateIsSet(state[1], 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return state[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint RotateIsSet(uint value, int count) => (((value >> count) != 0) || ((value << (32 - count))) != 0) ? 1u : 0u;
|
||||||
|
|
||||||
|
public class CRC
|
||||||
|
{
|
||||||
|
private static readonly uint[] Table;
|
||||||
|
|
||||||
|
static CRC()
|
||||||
|
{
|
||||||
|
Table = new uint[256];
|
||||||
|
const uint kPoly = 0xD35E417E;
|
||||||
|
for (uint i = 0; i < 256; i++)
|
||||||
|
{
|
||||||
|
uint r = i;
|
||||||
|
for (int j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
if ((r & 1) != 0)
|
||||||
|
r = (r >> 1) ^ kPoly;
|
||||||
|
else
|
||||||
|
r >>= 1;
|
||||||
|
}
|
||||||
|
Table[i] = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint _value = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
public void Update(byte[] data, uint offset, uint size)
|
||||||
|
{
|
||||||
|
for (uint i = 0; i < size; i++)
|
||||||
|
_value = (Table[(byte)_value ^ data[offset + i]] ^ (_value >> 9)) + 0x5B;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint GetDigest() { return ~_value - 0x41607A3D; }
|
||||||
|
|
||||||
|
public static uint CalculateDigest(byte[] data, uint offset, uint size)
|
||||||
|
{
|
||||||
|
var crc = new CRC();
|
||||||
|
crc.Update(data, offset, size);
|
||||||
|
return crc.GetDigest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RC4(Span<byte> data, uint key) => RC4(data, BitConverter.GetBytes(key));
|
||||||
|
|
||||||
|
public static void RC4(Span<byte> data, byte[] key)
|
||||||
|
{
|
||||||
|
int[] S = new int[0x100];
|
||||||
|
for (int _ = 0; _ < 0x100; _++)
|
||||||
|
{
|
||||||
|
S[_] = _;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] T = new int[0x100];
|
||||||
|
|
||||||
|
if (key.Length == 0x100)
|
||||||
|
{
|
||||||
|
Buffer.BlockCopy(key, 0, T, 0, key.Length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int _ = 0; _ < 0x100; _++)
|
||||||
|
{
|
||||||
|
T[_] = key[_ % key.Length];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
int j = 0;
|
||||||
|
for (i = 0; i < 0x100; i++)
|
||||||
|
{
|
||||||
|
j = (j + S[i] + T[i]) % 0x100;
|
||||||
|
|
||||||
|
(S[j], S[i]) = (S[i], S[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
i = j = 0;
|
||||||
|
for (int iteration = 0; iteration < data.Length; iteration++)
|
||||||
|
{
|
||||||
|
i = (i + 1) % 0x100;
|
||||||
|
j = (j + S[i]) % 0x100;
|
||||||
|
|
||||||
|
(S[j], S[i]) = (S[i], S[j]);
|
||||||
|
var K = (uint)S[(S[j] + S[i]) % 0x100];
|
||||||
|
|
||||||
|
var k = (byte)(K << 1) | (K >> 7);
|
||||||
|
data[iteration] ^= (byte)(k - 0x61);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Lz4
|
||||||
|
{
|
||||||
|
public static void Decompress(ReadOnlySpan<byte> cmp, Span<byte> dec)
|
||||||
|
{
|
||||||
|
int cmpPos = 0;
|
||||||
|
int decPos = 0;
|
||||||
|
|
||||||
|
// ReSharper disable once VariableHidesOuterVariable
|
||||||
|
int GetLength(int length, ReadOnlySpan<byte> cmp)
|
||||||
|
{
|
||||||
|
byte sum;
|
||||||
|
|
||||||
|
if (length == 0xf)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
length += sum = cmp[cmpPos++];
|
||||||
|
} while (sum == 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
byte token = cmp[cmpPos++];
|
||||||
|
|
||||||
|
int encCount = (token >> 4) & 0xf;
|
||||||
|
int litCount = (token >> 0) & 0xf;
|
||||||
|
|
||||||
|
//Copy literal chunk
|
||||||
|
litCount = GetLength(litCount, cmp);
|
||||||
|
|
||||||
|
cmp.Slice(cmpPos, litCount).CopyTo(dec.Slice(decPos));
|
||||||
|
|
||||||
|
cmpPos += litCount;
|
||||||
|
decPos += litCount;
|
||||||
|
|
||||||
|
if (cmpPos >= cmp.Length)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Copy compressed chunk
|
||||||
|
int back = cmp[cmpPos++] << 8 |
|
||||||
|
cmp[cmpPos++] << 0;
|
||||||
|
|
||||||
|
encCount = GetLength(encCount, cmp) + 4;
|
||||||
|
|
||||||
|
int encPos = decPos - back;
|
||||||
|
|
||||||
|
if (encCount <= back)
|
||||||
|
{
|
||||||
|
dec.Slice(encPos, encCount).CopyTo(dec.Slice(decPos));
|
||||||
|
|
||||||
|
decPos += encCount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (encCount-- > 0)
|
||||||
|
{
|
||||||
|
dec[decPos++] = dec[encPos++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (cmpPos < cmp.Length &&
|
||||||
|
decPos < dec.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,6 +41,7 @@ namespace AssetStudio
|
|||||||
Games.Add(index++, new Game(GameType.CodenameJump));
|
Games.Add(index++, new Game(GameType.CodenameJump));
|
||||||
Games.Add(index++, new Game(GameType.GirlsFrontline));
|
Games.Add(index++, new Game(GameType.GirlsFrontline));
|
||||||
Games.Add(index++, new Game(GameType.Reverse1999));
|
Games.Add(index++, new Game(GameType.Reverse1999));
|
||||||
|
Games.Add(index++, new Game(GameType.ArknightsEndfield));
|
||||||
}
|
}
|
||||||
public static Game GetGame(GameType gameType) => GetGame((int)gameType);
|
public static Game GetGame(GameType gameType) => GetGame((int)gameType);
|
||||||
public static Game GetGame(int index)
|
public static Game GetGame(int index)
|
||||||
@@ -152,7 +153,8 @@ namespace AssetStudio
|
|||||||
ProjectSekai,
|
ProjectSekai,
|
||||||
CodenameJump,
|
CodenameJump,
|
||||||
GirlsFrontline,
|
GirlsFrontline,
|
||||||
Reverse1999
|
Reverse1999,
|
||||||
|
ArknightsEndfield
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GameTypes
|
public static class GameTypes
|
||||||
@@ -174,6 +176,7 @@ namespace AssetStudio
|
|||||||
public static bool IsNaraka(this GameType type) => type == GameType.Naraka;
|
public static bool IsNaraka(this GameType type) => type == GameType.Naraka;
|
||||||
public static bool IsOPFP(this GameType type) => type == GameType.OPFP;
|
public static bool IsOPFP(this GameType type) => type == GameType.OPFP;
|
||||||
public static bool IsNetEase(this GameType type) => type == GameType.NetEase;
|
public static bool IsNetEase(this GameType type) => type == GameType.NetEase;
|
||||||
|
public static bool IsArknightsEndfield(this GameType type) => type == GameType.ArknightsEndfield;
|
||||||
public static bool IsGIGroup(this GameType type) => type switch
|
public static bool IsGIGroup(this GameType type) => type switch
|
||||||
{
|
{
|
||||||
GameType.GI or GameType.GI_Pack or GameType.GI_CB1 or GameType.GI_CB2 or GameType.GI_CB3 or GameType.GI_CB3Pre => true,
|
GameType.GI or GameType.GI_Pack or GameType.GI_CB1 or GameType.GI_CB2 or GameType.GI_CB3 or GameType.GI_CB3Pre => true,
|
||||||
@@ -200,7 +203,7 @@ namespace AssetStudio
|
|||||||
|
|
||||||
public static bool IsBlockFile(this GameType type) => type switch
|
public static bool IsBlockFile(this GameType type) => type switch
|
||||||
{
|
{
|
||||||
GameType.BH3 or GameType.BH3Pre or GameType.SR or GameType.GI_Pack or GameType.TOT => true,
|
GameType.BH3 or GameType.BH3Pre or GameType.SR or GameType.GI_Pack or GameType.TOT or GameType.ArknightsEndfield => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user