This commit is contained in:
Razmoth
2023-01-06 22:33:59 +04:00
parent a3cf868dfb
commit 2b31232b30
178 changed files with 5213 additions and 23780 deletions

View File

@@ -2,9 +2,9 @@ using System;
using System.Runtime.InteropServices;
using AssetStudio.PInvoke;
namespace ACL
namespace ACLLibs
{
public static class ACL
public static partial class ACL
{
private const string DLL_NAME = "acl";
static ACL()
@@ -27,20 +27,20 @@ namespace ACL
#region importfunctions
[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
private static extern void DecompressAll(IntPtr data, out IntPtr pValues, out int numValues, out IntPtr pTimes, out int numTimes);
[LibraryImport(DLL_NAME)]
private static partial void DecompressAll(IntPtr data, out IntPtr pValues, out int numValues, out IntPtr pTimes, out int numTimes);
#endregion
}
public static class SRACL
public static partial class SRACL
{
private const string DLL_NAME = "sracl";
static SRACL()
{
DllLoader.PreloadDll(DLL_NAME);
}
public static void DecompressAll(uint[] data, out float[] values, out float[] times)
public static void DecompressAll(byte[] data, out float[] values, out float[] times)
{
var pinned = GCHandle.Alloc(data, GCHandleType.Pinned);
var pData = pinned.AddrOfPinnedObject();
@@ -56,8 +56,8 @@ namespace ACL
#region importfunctions
[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
private static extern void DecompressAll(IntPtr data, out IntPtr pValues, out int numValues, out IntPtr pTimes, out int numTimes);
[LibraryImport(DLL_NAME)]
private static partial void DecompressAll(IntPtr data, out IntPtr pValues, out int numValues, out IntPtr pTimes, out int numTimes);
#endregion
}

View File

@@ -1,8 +1,10 @@
namespace AssetStudio
using ACLLibs;
namespace AssetStudio
{
public static class ACLExtensions
{
public static void Process(this ACLClip m_ACLClip, out float[] values, out float[] times) => ACL.ACL.DecompressAll(m_ACLClip.m_ClipData, out values, out times);
public static void ProcessSR(this ACLClip m_ACLClip, out float[] values, out float[] times) => ACL.SRACL.DecompressAll(m_ACLClip.m_ClipDataUint, out values, out times);
public static void Process(this ACLClip m_ACLClip, out float[] values, out float[] times) => ACL.DecompressAll(m_ACLClip.m_ClipData, out values, out times);
public static void ProcessSR(this ACLClip m_ACLClip, out float[] values, out float[] times) => SRACL.DecompressAll(m_ACLClip.m_ClipData, out values, out times);
}
}

View File

@@ -1,499 +0,0 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace AssetStudio
{
public class AnimationClipConverter
{
private readonly AnimationClip animationClip;
public static readonly Regex UnknownPathRegex = new Regex($@"^path_[0-9]{{1,10}}$", RegexOptions.Compiled);
private readonly Dictionary<Vector3Curve, List<Keyframe<Vector3>>> m_translations = new Dictionary<Vector3Curve, List<Keyframe<Vector3>>>();
private readonly Dictionary<QuaternionCurve, List<Keyframe<Quaternion>>> m_rotations = new Dictionary<QuaternionCurve, List<Keyframe<Quaternion>>>();
private readonly Dictionary<Vector3Curve, List<Keyframe<Vector3>>> m_scales = new Dictionary<Vector3Curve, List<Keyframe<Vector3>>>();
private readonly Dictionary<Vector3Curve, List<Keyframe<Vector3>>> m_eulers = new Dictionary<Vector3Curve, List<Keyframe<Vector3>>>();
private readonly Dictionary<FloatCurve, List<Keyframe<Float>>> m_floats = new Dictionary<FloatCurve, List<Keyframe<Float>>>();
private readonly Dictionary<PPtrCurve, List<PPtrKeyframe>> m_pptrs = new Dictionary<PPtrCurve, List<PPtrKeyframe>>();
public Vector3Curve[] Translations { get; private set; }
public QuaternionCurve[] Rotations { get; private set; }
public Vector3Curve[] Scales { get; private set; }
public Vector3Curve[] Eulers { get; private set; }
public FloatCurve[] Floats { get; private set; }
public PPtrCurve[] PPtrs { get; private set; }
public Game Game;
public AnimationClipConverter(AnimationClip clip, Game game)
{
animationClip = clip;
Game = game;
}
public static AnimationClipConverter Process(AnimationClip clip, Game game)
{
var converter = new AnimationClipConverter(clip, game);
converter.ProcessInner();
return converter;
}
private void ProcessInner()
{
var m_Clip = animationClip.m_MuscleClip.m_Clip;
var bindings = animationClip.m_ClipBindingConstant;
var tos = animationClip.FindTOS();
var streamedFrames = m_Clip.m_StreamedClip.ReadData();
var lastDenseFrame = m_Clip.m_DenseClip.m_FrameCount / m_Clip.m_DenseClip.m_SampleRate;
var lastSampleFrame = streamedFrames.Count > 1 ? streamedFrames[streamedFrames.Count - 2].time : 0.0f;
var lastFrame = Math.Max(lastDenseFrame, lastSampleFrame);
if (m_Clip.m_ACLClip.IsSet && Game.Name != "SR_CB2" && Game.Name != "SR_CB3")
{
var lastACLFrame = ProcessACLClip(m_Clip, bindings, tos);
lastFrame = Math.Max(lastFrame, lastACLFrame);
}
ProcessStreams(streamedFrames, bindings, tos, m_Clip.m_DenseClip.m_SampleRate);
ProcessDenses(m_Clip, bindings, tos);
if (m_Clip.m_ACLClip.IsSet && (Game.Name == "SR_CB2" || Game.Name == "SR_CB3"))
{
var lastACLFrame = ProcessACLClip(m_Clip, bindings, tos);
lastFrame = Math.Max(lastFrame, lastACLFrame);
}
if (m_Clip.m_ConstantClip != null)
{
ProcessConstant(m_Clip, bindings, tos, lastFrame);
}
CreateCurves();
}
private void CreateCurves()
{
Translations = m_translations.Select(t => new Vector3Curve(t.Key, t.Value)).ToArray();
Rotations = m_rotations.Select(t => new QuaternionCurve(t.Key, t.Value)).ToArray();
Scales = m_scales.Select(t => new Vector3Curve(t.Key, t.Value)).ToArray();
Eulers = m_eulers.Select(t => new Vector3Curve(t.Key, t.Value)).ToArray();
Floats = m_floats.Select(t => new FloatCurve(t.Key, t.Value)).ToArray();
PPtrs = m_pptrs.Select(t => new PPtrCurve(t.Key, t.Value)).ToArray();
}
private void ProcessStreams(List<StreamedClip.StreamedFrame> streamFrames, AnimationClipBindingConstant bindings, Dictionary<uint, string> tos, float sampleRate)
{
var curveValues = new float[4];
var inSlopeValues = new float[4];
var outSlopeValues = new float[4];
var interval = 1.0f / sampleRate;
// first (index [0]) stream frame is for slope calculation for the first real frame (index [1])
// last one (index [count - 1]) is +Infinity
// it is made for slope processing, but we don't need them
for (var frameIndex = 1; frameIndex < streamFrames.Count - 1; frameIndex++)
{
var frame = streamFrames[frameIndex];
for (var curveIndex = 0; curveIndex < frame.keyList.Length;)
{
var curve = frame.keyList[curveIndex];
var index = curve.index;
if (animationClip.m_MuscleClip.m_Clip.m_ACLClip.IsSet && Game.Name != "SR_CB2" && Game.Name != "SR_CB3")
index += (int)animationClip.m_MuscleClip.m_Clip.m_ACLClip.m_CurveCount;
var binding = bindings.FindBinding(index);
var path = GetCurvePath(tos, binding.path);
if (binding.typeID == ClassIDType.Transform)
{
GetPreviousFrame(streamFrames, curve.index, frameIndex, out var prevFrameIndex, out var prevCurveIndex);
var dimension = binding.GetDimension();
for (int key = 0; key < dimension; key++)
{
var keyCurve = frame.keyList[curveIndex];
var prevFrame = streamFrames[prevFrameIndex];
var prevKeyCurve = prevFrame.keyList[prevCurveIndex + key];
var deltaTime = frame.time - prevFrame.time;
curveValues[key] = keyCurve.value;
inSlopeValues[key] = prevKeyCurve.CalculateNextInSlope(deltaTime, keyCurve);
outSlopeValues[key] = keyCurve.outSlope;
curveIndex = GetNextCurve(frame, curveIndex);
}
AddTransformCurve(frame.time, binding.attribute, curveValues, inSlopeValues, outSlopeValues, 0, path);
}
else
{
if (binding.customType == 8)
{
AddAnimatorMuscleCurve(binding, frame.time, frame.keyList[curveIndex].value);
}
else if (binding.customType == 20)
{
AddBlendShapeCurve(binding, path, frame.time, frame.keyList[curveIndex].value);
}
curveIndex = GetNextCurve(frame, curveIndex);
}
}
}
}
private void ProcessDenses(Clip clip, AnimationClipBindingConstant bindings, Dictionary<uint, string> tos)
{
var dense = clip.m_DenseClip;
var streamCount = clip.m_StreamedClip.curveCount;
var slopeValues = new float[4]; // no slopes - 0 values
for (var frameIndex = 0; frameIndex < dense.m_FrameCount; frameIndex++)
{
var time = frameIndex / dense.m_SampleRate;
var frameOffset = frameIndex * (int)dense.m_CurveCount;
for (var curveIndex = 0; curveIndex < dense.m_CurveCount;)
{
var index = (int)streamCount + curveIndex;
if (clip.m_ACLClip.IsSet && Game.Name != "SR_CB2" && Game.Name != "SR_CB3")
index += (int)clip.m_ACLClip.m_CurveCount;
var binding = bindings.FindBinding(index);
var path = GetCurvePath(tos, binding.path);
var framePosition = frameOffset + curveIndex;
if (binding.typeID == ClassIDType.Transform)
{
AddTransformCurve(time, binding.attribute, dense.m_SampleArray, slopeValues, slopeValues, framePosition, path);
curveIndex += binding.GetDimension();
}
else
{
if (binding.customType == 8)
{
AddAnimatorMuscleCurve(binding, time, dense.m_SampleArray[framePosition]);
}
else if (binding.customType == 20)
{
AddBlendShapeCurve(binding, path, time, dense.m_SampleArray[framePosition]);
}
curveIndex++;
}
}
}
}
private float ProcessACLClip(Clip clip, AnimationClipBindingConstant bindings, Dictionary<uint, string> tos)
{
float[] values;
float[] times;
var acl = clip.m_ACLClip;
if (Game.Name != "SR_CB2" && Game.Name != "SR_CB3")
{
acl.Process(out values, out times);
}
else
{
acl.ProcessSR(out values, out times);
}
float[] slopeValues = new float[4]; // no slopes - 0 values
int frameCount = times.Length;
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++)
{
float time = times[frameIndex];
int frameOffset = frameIndex * (int)acl.m_CurveCount;
for (int curveIndex = 0; curveIndex < acl.m_CurveCount;)
{
var index = curveIndex;
if (Game.Name == "SR_CB2" || Game.Name == "SR_CB3")
index += (int)(clip.m_StreamedClip.curveCount + clip.m_DenseClip.m_CurveCount);
GenericBinding binding = bindings.FindBinding(index);
string path = GetCurvePath(tos, binding.path);
int framePosition = frameOffset + curveIndex;
if (binding.typeID == ClassIDType.Transform)
{
AddTransformCurve(time, binding.attribute, values, slopeValues, slopeValues, framePosition, path);
curveIndex += binding.GetDimension();
}
else
{
if (binding.customType == 8)
{
AddAnimatorMuscleCurve(binding, time, values[framePosition]);
}
else if (binding.customType == 20)
{
AddBlendShapeCurve(binding, path, time, values[framePosition]);
}
curveIndex++;
}
}
}
return times[frameCount - 1];
}
private void ProcessConstant(Clip clip, AnimationClipBindingConstant bindings, Dictionary<uint, string> tos, float lastFrame)
{
var constant = clip.m_ConstantClip;
var streamCount = clip.m_StreamedClip.curveCount;
var denseCount = clip.m_DenseClip.m_CurveCount;
var slopeValues = new float[4]; // no slopes - 0 values
// only first and last frames
var time = 0.0f;
for (var i = 0; i < 2; i++, time += lastFrame)
{
for (var curveIndex = 0; curveIndex < constant.data.Length;)
{
var index = (int)(streamCount + denseCount + curveIndex);
if (clip.m_ACLClip.IsSet)
index += (int)clip.m_ACLClip.m_CurveCount;
GenericBinding binding = bindings.FindBinding(index);
string path = GetCurvePath(tos, binding.path);
if (binding.typeID == ClassIDType.Transform)
{
AddTransformCurve(time, binding.attribute, constant.data, slopeValues, slopeValues, curveIndex, path);
curveIndex += binding.GetDimension();
}
else
{
if (binding.customType == 8)
{
AddAnimatorMuscleCurve(binding, time, constant.data[curveIndex]);
}
else if (binding.customType == 20)
{
AddBlendShapeCurve(binding, path, time, constant.data[curveIndex]);
}
curveIndex++;
}
}
}
}
private void AddTransformCurve(float time, uint transType, float[] curveValues,
float[] inSlopeValues, float[] outSlopeValues, int offset, string path)
{
switch (transType)
{
case 1:
{
Vector3Curve curve = new Vector3Curve(path);
if (!m_translations.TryGetValue(curve, out List<Keyframe<Vector3>> transCurve))
{
transCurve = new List<Keyframe<Vector3>>();
m_translations.Add(curve, transCurve);
}
float x = curveValues[offset + 0];
float y = curveValues[offset + 1];
float z = curveValues[offset + 2];
float inX = inSlopeValues[0];
float inY = inSlopeValues[1];
float inZ = inSlopeValues[2];
float outX = outSlopeValues[0];
float outY = outSlopeValues[1];
float outZ = outSlopeValues[2];
Vector3 value = new Vector3(x, y, z);
Vector3 inSlope = new Vector3(inX, inY, inZ);
Vector3 outSlope = new Vector3(outX, outY, outZ);
Keyframe<Vector3> transKey = new Keyframe<Vector3>(time, value, inSlope, outSlope, Keyframe<Vector3>.DefaultVector3Weight);
transCurve.Add(transKey);
}
break;
case 2:
{
QuaternionCurve curve = new QuaternionCurve(path);
if (!m_rotations.TryGetValue(curve, out List<Keyframe<Quaternion>> rotCurve))
{
rotCurve = new List<Keyframe<Quaternion>>();
m_rotations.Add(curve, rotCurve);
}
float x = curveValues[offset + 0];
float y = curveValues[offset + 1];
float z = curveValues[offset + 2];
float w = curveValues[offset + 3];
float inX = inSlopeValues[0];
float inY = inSlopeValues[1];
float inZ = inSlopeValues[2];
float inW = inSlopeValues[3];
float outX = outSlopeValues[0];
float outY = outSlopeValues[1];
float outZ = outSlopeValues[2];
float outW = outSlopeValues[3];
Quaternion value = new Quaternion(x, y, z, w);
Quaternion inSlope = new Quaternion(inX, inY, inZ, inW);
Quaternion outSlope = new Quaternion(outX, outY, outZ, outW);
Keyframe<Quaternion> rotKey = new Keyframe<Quaternion>(time, value, inSlope, outSlope, Keyframe<Quaternion>.DefaultQuaternionWeight);
rotCurve.Add(rotKey);
}
break;
case 3:
{
Vector3Curve curve = new Vector3Curve(path);
if (!m_scales.TryGetValue(curve, out List<Keyframe<Vector3>> scaleCurve))
{
scaleCurve = new List<Keyframe<Vector3>>();
m_scales.Add(curve, scaleCurve);
}
float x = curveValues[offset + 0];
float y = curveValues[offset + 1];
float z = curveValues[offset + 2];
float inX = inSlopeValues[0];
float inY = inSlopeValues[1];
float inZ = inSlopeValues[2];
float outX = outSlopeValues[0];
float outY = outSlopeValues[1];
float outZ = outSlopeValues[2];
Vector3 value = new Vector3(x, y, z);
Vector3 inSlope = new Vector3(inX, inY, inZ);
Vector3 outSlope = new Vector3(outX, outY, outZ);
Keyframe<Vector3> scaleKey = new Keyframe<Vector3>(time, value, inSlope, outSlope, Keyframe<Vector3>.DefaultVector3Weight);
scaleCurve.Add(scaleKey);
}
break;
case 4:
{
Vector3Curve curve = new Vector3Curve(path);
if (!m_eulers.TryGetValue(curve, out List<Keyframe<Vector3>> eulerCurve))
{
eulerCurve = new List<Keyframe<Vector3>>();
m_eulers.Add(curve, eulerCurve);
}
float x = curveValues[offset + 0];
float y = curveValues[offset + 1];
float z = curveValues[offset + 2];
float inX = inSlopeValues[0];
float inY = inSlopeValues[1];
float inZ = inSlopeValues[2];
float outX = outSlopeValues[0];
float outY = outSlopeValues[1];
float outZ = outSlopeValues[2];
Vector3 value = new Vector3(x, y, z);
Vector3 inSlope = new Vector3(inX, inY, inZ);
Vector3 outSlope = new Vector3(outX, outY, outZ);
Keyframe<Vector3> eulerKey = new Keyframe<Vector3>(time, value, inSlope, outSlope, Keyframe<Vector3>.DefaultVector3Weight);
eulerCurve.Add(eulerKey);
}
break;
default:
throw new NotImplementedException(transType.ToString());
}
}
private void AddAnimatorMuscleCurve(GenericBinding binding, float time, float value)
{
FloatCurve curve = new FloatCurve(string.Empty, binding.GetClipMuscle(), ClassIDType.Animator, new PPtr<MonoScript>(0, 0, null));
AddFloatKeyframe(curve, time, value);
}
private void AddBlendShapeCurve(GenericBinding binding, string path, float time, float value)
{
var attribute = "";
const string Prefix = "blendShape.";
if (UnknownPathRegex.IsMatch(path))
{
attribute = Prefix + binding.attribute;
}
foreach (GameObject root in animationClip.FindRoots().ToArray())
{
Transform rootTransform = root.GetTransform();
Transform child = rootTransform.FindChild(path);
if (child == null)
{
continue;
}
SkinnedMeshRenderer skin = null;
if (child.m_GameObject.TryGet(out var gameObject))
{
skin = gameObject.FindComponent<SkinnedMeshRenderer>();
}
if (skin == null)
{
continue;
}
if (!skin.m_Mesh.TryGet(out var mesh))
{
continue;
}
string shapeName = mesh.FindBlendShapeNameByCRC(binding.attribute);
if (shapeName == null)
{
continue;
}
attribute = Prefix + shapeName;
}
attribute = Prefix + attribute;
FloatCurve curve = new FloatCurve(path, attribute, binding.typeID, binding.script.CastTo<MonoScript>());
AddFloatKeyframe(curve, time, value);
}
private void AddFloatKeyframe(FloatCurve curve, float time, float value)
{
if (!m_floats.TryGetValue(curve, out List<Keyframe<Float>> floatCurve))
{
floatCurve = new List<Keyframe<Float>>();
m_floats.Add(curve, floatCurve);
}
Keyframe<Float> floatKey = new Keyframe<Float>(time, value, Keyframe<Float>.DefaultFloatWeight);
floatCurve.Add(floatKey);
}
private void GetPreviousFrame(List<StreamedClip.StreamedFrame> streamFrames, int curveID, int currentFrame, out int frameIndex, out int curveIndex)
{
for (frameIndex = currentFrame - 1; frameIndex >= 0; frameIndex--)
{
var frame = streamFrames[frameIndex];
for (curveIndex = 0; curveIndex < frame.keyList.Length; curveIndex++)
{
var curve = frame.keyList[curveIndex];
if (curve.index == curveID)
{
return;
}
}
}
throw new Exception($"There is no curve with index {curveID} in any of previous frames");
}
private int GetNextCurve(StreamedClip.StreamedFrame frame, int currentCurve)
{
var curve = frame.keyList[currentCurve];
int i = currentCurve + 1;
for (; i < frame.keyList.Length; i++)
{
if (frame.keyList[i].index != curve.index)
{
return i;
}
}
return i;
}
private static string GetCurvePath(Dictionary<uint, string> tos, uint hash)
{
if (tos.TryGetValue(hash, out string path))
{
return path;
}
else
{
return $"path_{hash}";
}
}
}
}

View File

@@ -1,250 +0,0 @@
using System.IO;
using System.Linq;
using System.Text;
namespace AssetStudio
{
public static class AnimationClipExtensions
{
public static string Convert(this AnimationClip animationClip, Game game)
{
var converter = AnimationClipConverter.Process(animationClip, game);
animationClip.m_RotationCurves = converter.Rotations.Union(animationClip.m_RotationCurves).ToArray();
animationClip.m_EulerCurves = converter.Eulers.Union(animationClip.m_EulerCurves).ToArray();
animationClip.m_PositionCurves = converter.Translations.Union(animationClip.m_PositionCurves).ToArray();
animationClip.m_ScaleCurves = converter.Scales.Union(animationClip.m_ScaleCurves).ToArray();
animationClip.m_FloatCurves = converter.Floats.Union(animationClip.m_FloatCurves).ToArray();
animationClip.m_PPtrCurves = converter.PPtrs.Union(animationClip.m_PPtrCurves).ToArray();
return ConvertSerializedAnimationClip(animationClip);
}
public static string ConvertSerializedAnimationClip(AnimationClip animationClip)
{
var sb = new StringBuilder();
using (var stringWriter = new StringWriter(sb))
{
YAMLWriter writer = new YAMLWriter();
YAMLDocument doc = ExportYAMLDocument(animationClip);
writer.AddDocument(doc);
writer.Write(stringWriter);
return sb.ToString();
}
}
public static YAMLDocument ExportYAMLDocument(AnimationClip animationClip)
{
YAMLDocument document = new YAMLDocument();
YAMLMappingNode root = document.CreateMappingRoot();
root.Tag = ((int)ClassIDType.AnimationClip).ToString();
root.Anchor = ((int)ClassIDType.AnimationClip * 100000).ToString();
YAMLMappingNode node = (YAMLMappingNode)animationClip.ExportYAML();
root.Add(ClassIDType.AnimationClip.ToString(), node);
return document;
}
public static string GetClipMuscle(this GenericBinding genericBinding) => ClipMuscles[genericBinding.attribute] ?? $"unknown_{genericBinding.attribute}";
public static string[] ClipMuscles =
{
"MotionT.x",
"MotionT.y",
"MotionT.z",
"MotionQ.x",
"MotionQ.y",
"MotionQ.z",
"MotionQ.w",
"RootT.x",
"RootT.y",
"RootT.z",
"RootQ.x",
"RootQ.y",
"RootQ.z",
"RootQ.w",
"LeftFootT.x",
"LeftFootT.y",
"LeftFootT.z",
"LeftFootQ.x",
"LeftFootQ.y",
"LeftFootQ.z",
"LeftFootQ.w",
"RightFootT.x",
"RightFootT.y",
"RightFootT.z",
"RightFootQ.x",
"RightFootQ.y",
"RightFootQ.z",
"RightFootQ.w",
"LeftHandT.x",
"LeftHandT.y",
"LeftHandT.z",
"LeftHandQ.x",
"LeftHandQ.y",
"LeftHandQ.z",
"LeftHandQ.w",
"RightHandT.x",
"RightHandT.y",
"RightHandT.z",
"RightHandQ.x",
"RightHandQ.y",
"RightHandQ.z",
"RightHandQ.w",
"Spine Front-Back",
"Spine Left-Right",
"Spine Twist Left-Right",
"Chest Front-Back",
"Chest Left-Right",
"Chest Twist Left-Right",
"UpperChest Front-Back",
"UpperChest Left-Right",
"UpperChest Twist Left-Right",
"Neck Nod Down-Up",
"Neck Tilt Left-Right",
"Neck Turn Left-Right",
"Head Nod Down-Up",
"Head Tilt Left-Right",
"Head Turn Left-Right",
"Left Eye Down-Up",
"Left Eye In-Out",
"Right Eye Down-Up",
"Right Eye In-Out",
"Jaw Close",
"Jaw Left-Right",
"Left Upper Leg Front-Back",
"Left Upper Leg In-Out",
"Left Upper Leg Twist In-Out",
"Left Lower Leg Stretch",
"Left Lower Leg Twist In-Out",
"Left Foot Up-Down",
"Left Foot Twist In-Out",
"Left Toes Up-Down",
"Right Upper Leg Front-Back",
"Right Upper Leg In-Out",
"Right Upper Leg Twist In-Out",
"Right Lower Leg Stretch",
"Right Lower Leg Twist In-Out",
"Right Foot Up-Down",
"Right Foot Twist In-Out",
"Right Toes Up-Down",
"Left Shoulder Down-Up",
"Left Shoulder Front-Back",
"Left Arm Down-Up",
"Left Arm Front-Back",
"Left Arm Twist In-Out",
"Left Forearm Stretch",
"Left Forearm Twist In-Out",
"Left Hand Down-Up",
"Left Hand In-Out",
"Right Shoulder Down-Up",
"Right Shoulder Front-Back",
"Right Arm Down-Up",
"Right Arm Front-Back",
"Right Arm Twist In-Out",
"Right Forearm Stretch",
"Right Forearm Twist In-Out",
"Right Hand Down-Up",
"Right Hand In-Out",
"LeftHand.Thumb.1 Stretched",
"LeftHand.Thumb.Spread",
"LeftHand.Thumb.2 Stretched",
"LeftHand.Thumb.3 Stretched",
"LeftHand.Index.1 Stretched",
"LeftHand.Index.Spread",
"LeftHand.Index.2 Stretched",
"LeftHand.Index.3 Stretched",
"LeftHand.Middle.1 Stretched",
"LeftHand.Middle.Spread",
"LeftHand.Middle.2 Stretched",
"LeftHand.Middle.3 Stretched",
"LeftHand.Ring.1 Stretched",
"LeftHand.Ring.Spread",
"LeftHand.Ring.2 Stretched",
"LeftHand.Ring.3 Stretched",
"LeftHand.Little.1 Stretched",
"LeftHand.Little.Spread",
"LeftHand.Little.2 Stretched",
"LeftHand.Little.3 Stretched",
"RightHand.Thumb.1 Stretched",
"RightHand.Thumb.Spread",
"RightHand.Thumb.2 Stretched",
"RightHand.Thumb.3 Stretched",
"RightHand.Index.1 Stretched",
"RightHand.Index.Spread",
"RightHand.Index.2 Stretched",
"RightHand.Index.3 Stretched",
"RightHand.Middle.1 Stretched",
"RightHand.Middle.Spread",
"RightHand.Middle.2 Stretched",
"RightHand.Middle.3 Stretched",
"RightHand.Ring.1 Stretched",
"RightHand.Ring.Spread",
"RightHand.Ring.2 Stretched",
"RightHand.Ring.3 Stretched",
"RightHand.Little.1 Stretched",
"RightHand.Little.Spread",
"RightHand.Little.2 Stretched",
"RightHand.Little.3 Stretched",
"SpineTDOF.x",
"SpineTDOF.y",
"SpineTDOF.z",
"ChestTDOF.x",
"ChestTDOF.y",
"ChestTDOF.z",
"UpperChestTDOF.x",
"UpperChestTDOF.y",
"UpperChestTDOF.z",
"NeckTDOF.x",
"NeckTDOF.y",
"NeckTDOF.z",
"HeadTDOF.x",
"HeadTDOF.y",
"HeadTDOF.z",
"LeftUpperLegTDOF.x",
"LeftUpperLegTDOF.y",
"LeftUpperLegTDOF.z",
"LeftLowerLegTDOF.x",
"LeftLowerLegTDOF.y",
"LeftLowerLegTDOF.z",
"LeftFootTDOF.x",
"LeftFootTDOF.y",
"LeftFootTDOF.z",
"LeftToesTDOF.x",
"LeftToesTDOF.y",
"LeftToesTDOF.z",
"RightUpperLegTDOF.x",
"RightUpperLegTDOF.y",
"RightUpperLegTDOF.z",
"RightLowerLegTDOF.x",
"RightLowerLegTDOF.y",
"RightLowerLegTDOF.z",
"RightFootTDOF.x",
"RightFootTDOF.y",
"RightFootTDOF.z",
"RightToesTDOF.x",
"RightToesTDOF.y",
"RightToesTDOF.z",
"LeftShoulderTDOF.x",
"LeftShoulderTDOF.y",
"LeftShoulderTDOF.z",
"LeftUpperArmTDOF.x",
"LeftUpperArmTDOF.y",
"LeftUpperArmTDOF.z",
"LeftLowerArmTDOF.x",
"LeftLowerArmTDOF.y",
"LeftLowerArmTDOF.z",
"LeftHandTDOF.x",
"LeftHandTDOF.y",
"LeftHandTDOF.z",
"RightShoulderTDOF.x",
"RightShoulderTDOF.y",
"RightShoulderTDOF.z",
"RightUpperArmTDOF.x",
"RightUpperArmTDOF.y",
"RightUpperArmTDOF.z",
"RightLowerArmTDOF.x",
"RightLowerArmTDOF.y",
"RightLowerArmTDOF.z",
"RightHandTDOF.x",
"RightHandTDOF.y",
"RightHandTDOF.z",
};
}
}

View File

@@ -1,16 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Version>0.18.60</Version>
<AssemblyVersion>0.18.60</AssemblyVersion>
<FileVersion>0.18.60</FileVersion>
<Copyright>Copyright © Razmoth 2022; Copyright © Perfare 2018-2022</Copyright>
<DebugType>embedded</DebugType>
<TargetFramework>net7.0</TargetFramework>
<Version>0.80.30</Version>
<AssemblyVersion>0.80.30</AssemblyVersion>
<FileVersion>0.80.30</FileVersion>
<Copyright>Copyright © Perfare 2018-2022</Copyright>
<DebugType>embedded</DebugType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
<PackageReference Include="Kyaru.Texture2DDecoder" Version="0.17.0" />
<PackageReference Include="Kyaru.Texture2DDecoder.Windows" Version="0.1.0" />
<PackageReference Include="Mono.Cecil" Version="0.11.4" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta15" />
</ItemGroup>
@@ -18,7 +21,6 @@
<ProjectReference Include="..\AssetStudio.PInvoke\AssetStudio.PInvoke.csproj" />
<ProjectReference Include="..\AssetStudioFBXWrapper\AssetStudioFBXWrapper.csproj" />
<ProjectReference Include="..\AssetStudio\AssetStudio.csproj" />
<ProjectReference Include="..\Texture2DDecoderWrapper\Texture2DDecoderWrapper.csproj" />
</ItemGroup>
</Project>

View File

@@ -23,7 +23,7 @@ namespace AssetStudio
var result = Factory.System_Create(out var system);
if (result != RESULT.OK)
return null;
result = system.init(1, INITFLAGS.NORMAL, IntPtr.Zero);
result = system.init(1, INITFLAGS.NORMAL, nint.Zero);
if (result != RESULT.OK)
return null;
exinfo.cbsize = Marshal.SizeOf(exinfo);

View File

@@ -1,24 +1,23 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices;
namespace AssetStudio
{
public static class ConsoleHelper
public static partial class ConsoleHelper
{
[DllImport("kernel32.dll", SetLastError = true)]
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AllocConsole();
public static partial bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
[LibraryImport("kernel32.dll", EntryPoint = "SetConsoleTitleA", SetLastError = true, StringMarshalling = StringMarshalling.Utf8)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetConsoleTitle(string lpConsoleTitle);
public static partial bool SetConsoleTitle(string lpConsoleTitle);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetConsoleWindow();
[LibraryImport("kernel32.dll", SetLastError = true)]
public static partial nint GetConsoleWindow();
[DllImport("user32.dll", SetLastError = true)]
[LibraryImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
public static partial bool ShowWindow(nint hWnd, int nCmdShow);
public const int SW_HIDE = 0;
public const int SW_SHOW = 5;

View File

@@ -0,0 +1,10 @@
using System.Runtime.InteropServices;
namespace AssetStudio
{
public static partial class FontHelper
{
[LibraryImport("gdi32.dll")]
public static partial nint AddFontMemResourceEx(nint pbFont, uint cbFont, nint pdv, ref uint pcFonts);
}
}

View File

@@ -1,5 +1,4 @@
using SevenZip;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -15,6 +14,7 @@ namespace AssetStudio
public List<ImportedKeyframedAnimation> AnimationList { get; protected set; } = new List<ImportedKeyframedAnimation>();
public List<ImportedMorph> MorphList { get; protected set; } = new List<ImportedMorph>();
private Game Game;
private ImageFormat imageFormat;
private Avatar avatar;
private HashSet<AnimationClip> animationClipHashSet = new HashSet<AnimationClip>();
@@ -23,18 +23,17 @@ namespace AssetStudio
private Dictionary<Texture2D, string> textureNameDictionary = new Dictionary<Texture2D, string>();
private Dictionary<Transform, ImportedFrame> transformDictionary = new Dictionary<Transform, ImportedFrame>();
Dictionary<uint, string> morphChannelNames = new Dictionary<uint, string>();
private Game Game;
public ModelConverter(GameObject m_GameObject, ImageFormat imageFormat, Game game, AnimationClip[] animationList = null, bool ignoreController = true)
public ModelConverter(GameObject m_GameObject, ImageFormat imageFormat, Game game, bool collectAnimations, AnimationClip[] animationList = null)
{
Game = game;
this.imageFormat = imageFormat;
if (m_GameObject.m_Animator != null)
{
InitWithAnimator(m_GameObject.m_Animator);
if (animationList == null)
if (animationList == null && collectAnimations)
{
CollectAnimationClip(m_GameObject.m_Animator, ignoreController);
CollectAnimationClip(m_GameObject.m_Animator);
}
}
else
@@ -51,16 +50,16 @@ namespace AssetStudio
ConvertAnimations();
}
public ModelConverter(string rootName, List<GameObject> m_GameObjects, ImageFormat imageFormat, Game game, AnimationClip[] animationList = null, bool ignoreController = true)
public ModelConverter(string rootName, List<GameObject> m_GameObjects, ImageFormat imageFormat, Game game, bool collectAnimations, AnimationClip[] animationList = null)
{
Game = game;
this.imageFormat = imageFormat;
RootFrame = CreateFrame(rootName, Vector3.Zero, new Quaternion(0, 0, 0, 0), Vector3.One);
foreach (var m_GameObject in m_GameObjects)
{
if (m_GameObject.m_Animator != null && animationList == null)
if (m_GameObject.m_Animator != null && animationList == null && collectAnimations)
{
CollectAnimationClip(m_GameObject.m_Animator, ignoreController);
CollectAnimationClip(m_GameObject.m_Animator);
}
var m_Transform = m_GameObject.m_Transform;
@@ -82,14 +81,14 @@ namespace AssetStudio
ConvertAnimations();
}
public ModelConverter(Animator m_Animator, ImageFormat imageFormat, Game game, AnimationClip[] animationList = null, bool ignoreController = true)
public ModelConverter(Animator m_Animator, ImageFormat imageFormat, Game game, bool collectAnimations, AnimationClip[] animationList = null)
{
Game = game;
this.imageFormat = imageFormat;
InitWithAnimator(m_Animator);
if (animationList == null)
if (animationList == null && collectAnimations)
{
CollectAnimationClip(m_Animator, ignoreController);
CollectAnimationClip(m_Animator);
}
else
{
@@ -185,9 +184,9 @@ namespace AssetStudio
}
}
private void CollectAnimationClip(Animator m_Animator, bool ignoreController)
private void CollectAnimationClip(Animator m_Animator)
{
if (m_Animator.m_Controller.TryGet(out var m_Controller) && !ignoreController)
if (m_Animator.m_Controller.TryGet(out var m_Controller))
{
switch (m_Controller)
{
@@ -512,7 +511,10 @@ namespace AssetStudio
var shapeChannel = mesh.m_Shapes.channels[i];
var blendShapeName = "blendShape." + shapeChannel.name;
morphChannelNames[CRC.CalculateDigestUTF8(blendShapeName)] = blendShapeName;
var crc = new SevenZip.CRC();
var bytes = Encoding.UTF8.GetBytes(blendShapeName);
crc.Update(bytes, 0, (uint)bytes.Length);
morphChannelNames[crc.GetDigest()] = blendShapeName;
channel.Name = shapeChannel.name.Split('.').Last();
channel.KeyframeList = new List<ImportedMorphKeyframe>(shapeChannel.frameCount);
@@ -796,7 +798,7 @@ namespace AssetStudio
foreach (var m_CompressedRotationCurve in animationClip.m_CompressedRotationCurves)
{
var track = iAnim.FindTrack(FixBonePath(animationClip, m_CompressedRotationCurve.m_Path));
var numKeys = m_CompressedRotationCurve.m_Times.m_NumItems;
var data = m_CompressedRotationCurve.m_Times.UnpackInts();
var times = new float[numKeys];
@@ -807,7 +809,7 @@ namespace AssetStudio
times[i] = t * 0.01f;
}
var quats = m_CompressedRotationCurve.m_Values.UnpackQuats();
for (int i = 0; i < numKeys; i++)
{
var quat = quats[i];
@@ -861,7 +863,7 @@ namespace AssetStudio
{
channelName = channelName.Substring(dotPos + 1);
}
var path = FixBonePath(animationClip, m_FloatCurve.path);
if (string.IsNullOrEmpty(path))
{
@@ -884,7 +886,7 @@ namespace AssetStudio
var m_ClipBindingConstant = animationClip.m_ClipBindingConstant ?? m_Clip.ConvertValueArrayToGenericBinding();
var m_ACLClip = m_Clip.m_ACLClip;
var aclCount = m_ACLClip.m_CurveCount;
if (m_ACLClip.m_CurveCount != 0 && Game.Name != "SR_CB2" && Game.Name != "SR_CB3")
if (!m_ACLClip.m_ClipData.IsNullOrEmpty() && !Game.Type.IsSRGroup())
{
m_ACLClip.Process(out var values, out var times);
for (int frameIndex = 0; frameIndex < times.Length; frameIndex++)
@@ -896,7 +898,7 @@ namespace AssetStudio
var index = curveIndex;
ReadCurveData(iAnim, m_ClipBindingConstant, index, time, values, (int)frameOffset, ref curveIndex);
}
}
}
for (int frameIndex = 1; frameIndex < streamedFrames.Count - 1; frameIndex++)
@@ -906,9 +908,9 @@ namespace AssetStudio
for (int curveIndex = 0; curveIndex < frame.keyList.Length;)
{
var index = frame.keyList[curveIndex].index;
if (Game.Name != "SR_CB2" && Game.Name != "SR_CB3")
if (!Game.Type.IsSRGroup())
index += (int)aclCount;
ReadCurveData(iAnim, m_ClipBindingConstant, (int)index, frame.time, streamedValues, 0, ref curveIndex);
ReadCurveData(iAnim, m_ClipBindingConstant, index, frame.time, streamedValues, 0, ref curveIndex);
}
}
var m_DenseClip = m_Clip.m_DenseClip;
@@ -920,12 +922,12 @@ namespace AssetStudio
for (int curveIndex = 0; curveIndex < m_DenseClip.m_CurveCount;)
{
var index = streamCount + curveIndex;
if (Game.Name != "SR_CB2" && Game.Name != "SR_CB3")
index += aclCount;
if (!Game.Type.IsSRGroup())
index += (int)aclCount;
ReadCurveData(iAnim, m_ClipBindingConstant, (int)index, time, m_DenseClip.m_SampleArray, (int)frameOffset, ref curveIndex);
}
}
if (m_ACLClip.m_CurveCount != 0 && Game.Name == "SR_CB2" && Game.Name != "SR_CB3")
if (!m_ACLClip.m_ClipData.IsNullOrEmpty() && Game.Type.IsSRGroup())
{
m_ACLClip.ProcessSR(out var values, out var times);
for (int frameIndex = 0; frameIndex < times.Length; frameIndex++)
@@ -1037,6 +1039,7 @@ namespace AssetStudio
curveIndex++;
}
}
private string GetPathFromHash(uint hash)
{
bonePathHash.TryGetValue(hash, out var boneName);
@@ -1054,12 +1057,18 @@ namespace AssetStudio
private void CreateBonePathHash(Transform m_Transform)
{
var name = GetTransformPathByFather(m_Transform);
bonePathHash[CRC.CalculateDigestUTF8(name)] = name;
var crc = new SevenZip.CRC();
var bytes = Encoding.UTF8.GetBytes(name);
crc.Update(bytes, 0, (uint)bytes.Length);
bonePathHash[crc.GetDigest()] = name;
int index;
while ((index = name.IndexOf("/", StringComparison.Ordinal)) >= 0)
{
name = name.Substring(index + 1);
bonePathHash[CRC.CalculateDigestUTF8(name)] = name;
crc = new SevenZip.CRC();
bytes = Encoding.UTF8.GetBytes(name);
crc.Update(bytes, 0, (uint)bytes.Length);
bonePathHash[crc.GetDigest()] = name;
}
foreach (var pptr in m_Transform.m_Children)
{

View File

@@ -16,7 +16,7 @@ namespace AssetStudio
{
var decompressedBytes = new byte[shader.decompressedSize];
LZ4Codec.Decode(shader.m_SubProgramBlob, decompressedBytes);
using (var blobReader = new BinaryReader(new MemoryStream(decompressedBytes)))
using (var blobReader = new EndianBinaryReader(new MemoryStream(decompressedBytes), EndianType.LittleEndian))
{
var program = new ShaderProgram(blobReader, shader.version);
program.Read(blobReader, 0);
@@ -44,7 +44,7 @@ namespace AssetStudio
var compressedLength = shader.compressedLengths[i][j];
var decompressedLength = shader.decompressedLengths[i][j];
var decompressedBytes = new byte[decompressedLength];
if (game.Name == "GI" || game.Name == "GI_CB2" || game.Name == "GI_CB3")
if (game.Type.IsGISubGroup())
{
Buffer.BlockCopy(shader.compressedBlob, (int)offset, decompressedBytes, 0, (int)decompressedLength);
}
@@ -52,7 +52,7 @@ namespace AssetStudio
{
LZ4Codec.Decode(shader.compressedBlob, (int)offset, (int)compressedLength, decompressedBytes, 0, (int)decompressedLength);
}
using (var blobReader = new BinaryReader(new MemoryStream(decompressedBytes)))
using (var blobReader = new EndianBinaryReader(new MemoryStream(decompressedBytes), EndianType.LittleEndian))
{
if (j == 0)
{
@@ -878,7 +878,7 @@ namespace AssetStudio
public int Length;
public int Segment;
public ShaderSubProgramEntry(BinaryReader reader, int[] version)
public ShaderSubProgramEntry(EndianBinaryReader reader, int[] version)
{
Offset = reader.ReadInt32();
Length = reader.ReadInt32();
@@ -894,7 +894,7 @@ namespace AssetStudio
public ShaderSubProgramEntry[] entries;
public ShaderSubProgram[] m_SubPrograms;
public ShaderProgram(BinaryReader reader, int[] version)
public ShaderProgram(EndianBinaryReader reader, int[] version)
{
var subProgramsCapacity = reader.ReadInt32();
entries = new ShaderSubProgramEntry[subProgramsCapacity];
@@ -905,7 +905,7 @@ namespace AssetStudio
m_SubPrograms = new ShaderSubProgram[subProgramsCapacity];
}
public void Read(BinaryReader reader, int segment)
public void Read(EndianBinaryReader reader, int segment)
{
for (int i = 0; i < entries.Length; i++)
{
@@ -938,7 +938,7 @@ namespace AssetStudio
public string[] m_LocalKeywords;
public byte[] m_ProgramCode;
public ShaderSubProgram(BinaryReader reader)
public ShaderSubProgram(EndianBinaryReader reader)
{
//LoadGpuProgramFromData
//201509030 - Unity 5.3
@@ -1049,7 +1049,7 @@ namespace AssetStudio
}
case ShaderGpuProgramType.MetalVS:
case ShaderGpuProgramType.MetalFS:
using (var reader = new BinaryReader(new MemoryStream(m_ProgramCode)))
using (var reader = new EndianBinaryReader(new MemoryStream(m_ProgramCode), EndianType.LittleEndian))
{
var fourCC = reader.ReadUInt32();
if (fourCC == 0xf00dcafe)

View File

@@ -139,9 +139,9 @@ namespace AssetStudio
var m_VertexData = m_RD.m_VertexData;
var m_Channel = m_VertexData.m_Channels[0]; //kShaderChannelVertex
var m_Stream = m_VertexData.m_Streams[m_Channel.stream];
using (var vertexReader = new BinaryReader(new MemoryStream(m_VertexData.m_DataSize)))
using (var vertexReader = new EndianBinaryReader(new MemoryStream(m_VertexData.m_DataSize), EndianType.LittleEndian))
{
using (var indexReader = new BinaryReader(new MemoryStream(m_RD.m_IndexBuffer)))
using (var indexReader = new EndianBinaryReader(new MemoryStream(m_RD.m_IndexBuffer), EndianType.LittleEndian))
{
foreach (var subMesh in m_RD.m_SubMeshes)
{

View File

@@ -12,7 +12,7 @@ namespace AssetStudio
private TextureFormat m_TextureFormat;
private int[] version;
private BuildTarget platform;
private int outputSize;
private int outPutSize;
public Texture2DConverter(Texture2D m_Texture2D)
{
@@ -22,7 +22,7 @@ namespace AssetStudio
m_TextureFormat = m_Texture2D.m_TextureFormat;
version = m_Texture2D.version;
platform = m_Texture2D.platform;
outputSize = m_Width * m_Height * 4;
outPutSize = m_Width * m_Height * 4;
}
public bool DecodeTexture2D(byte[] bytes)
@@ -57,7 +57,7 @@ namespace AssetStudio
flag = DecodeRGB565(buff, bytes);
break;
case TextureFormat.R16: //test pass
case TextureFormat.R16_2: //test pass
case TextureFormat.R16_Alt: //test pass
flag = DecodeR16(buff, bytes);
break;
case TextureFormat.DXT1: //test pass
@@ -272,7 +272,7 @@ namespace AssetStudio
private bool DecodeRGBA32(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
for (var i = 0; i < outPutSize; i += 4)
{
buff[i] = image_data[i + 2];
buff[i + 1] = image_data[i + 1];
@@ -284,7 +284,7 @@ namespace AssetStudio
private bool DecodeARGB32(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
for (var i = 0; i < outPutSize; i += 4)
{
buff[i] = image_data[i + 3];
buff[i + 1] = image_data[i + 2];
@@ -351,7 +351,7 @@ namespace AssetStudio
private bool DecodeBGRA32(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
for (var i = 0; i < outPutSize; i += 4)
{
buff[i] = image_data[i];
buff[i + 1] = image_data[i + 1];
@@ -363,7 +363,7 @@ namespace AssetStudio
private bool DecodeRHalf(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
for (var i = 0; i < outPutSize; i += 4)
{
buff[i] = 0;
buff[i + 1] = 0;
@@ -375,7 +375,7 @@ namespace AssetStudio
private bool DecodeRGHalf(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
for (var i = 0; i < outPutSize; i += 4)
{
buff[i] = 0;
buff[i + 1] = (byte)Math.Round(Half.ToHalf(image_data, i + 2) * 255f);
@@ -387,7 +387,7 @@ namespace AssetStudio
private bool DecodeRGBAHalf(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
for (var i = 0; i < outPutSize; i += 4)
{
buff[i] = (byte)Math.Round(Half.ToHalf(image_data, i * 2 + 4) * 255f);
buff[i + 1] = (byte)Math.Round(Half.ToHalf(image_data, i * 2 + 2) * 255f);
@@ -399,7 +399,7 @@ namespace AssetStudio
private bool DecodeRFloat(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
for (var i = 0; i < outPutSize; i += 4)
{
buff[i] = 0;
buff[i + 1] = 0;
@@ -411,7 +411,7 @@ namespace AssetStudio
private bool DecodeRGFloat(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
for (var i = 0; i < outPutSize; i += 4)
{
buff[i] = 0;
buff[i + 1] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 2 + 4) * 255f);
@@ -423,7 +423,7 @@ namespace AssetStudio
private bool DecodeRGBAFloat(byte[] image_data, byte[] buff)
{
for (var i = 0; i < buff.Length; i += 4)
for (var i = 0; i < outPutSize; i += 4)
{
buff[i] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 4 + 8) * 255f);
buff[i + 1] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 4 + 4) * 255f);
@@ -471,7 +471,7 @@ namespace AssetStudio
private bool DecodeRGB9e5Float(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
for (var i = 0; i < outPutSize; i += 4)
{
var n = BitConverter.ToInt32(image_data, i);
var scale = n >> 27 & 0x1f;
@@ -594,7 +594,7 @@ namespace AssetStudio
private bool DecodeRG16(byte[] image_data, byte[] buff)
{
var size = m_Width * m_Height;
for (var i = 0; i < size; i += 2)
for (var i = 0; i < size; i++)
{
buff[i * 4] = 0; //B
buff[i * 4 + 1] = image_data[i * 2 + 1];//G
@@ -617,49 +617,6 @@ namespace AssetStudio
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte DownScaleFrom16BitTo8Bit(ushort component)
{
return (byte)(((component * 255) + 32895) >> 16);
}
private bool DecodeRG32(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
{
buff[i] = 0; //b
buff[i + 1] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i + 2)); //g
buff[i + 2] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i)); //r
buff[i + 3] = byte.MaxValue; //a
}
return true;
}
private bool DecodeRGB48(byte[] image_data, byte[] buff)
{
var size = m_Width * m_Height;
for (var i = 0; i < size; i++)
{
buff[i * 4] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 6 + 4)); //b
buff[i * 4 + 1] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 6 + 2)); //g
buff[i * 4 + 2] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 6)); //r
buff[i * 4 + 3] = byte.MaxValue; //a
}
return true;
}
private bool DecodeRGBA64(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
{
buff[i] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 2 + 4)); //b
buff[i + 1] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 2 + 2)); //g
buff[i + 2] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 2)); //r
buff[i + 3] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 2 + 6)); //a
}
return true;
}
private bool DecodeETC1Crunched(byte[] image_data, byte[] buff)
{
if (UnpackCrunch(image_data, out var result))
@@ -684,6 +641,49 @@ namespace AssetStudio
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte DownScaleFrom16BitTo8Bit(ushort component)
{
return (byte)(((component * 255) + 32895) >> 16);
}
private bool DecodeRG32(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outPutSize; i += 4)
{
buff[i] = 0; //b
buff[i + 1] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i + 2)); //g
buff[i + 2] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i)); //r
buff[i + 3] = byte.MaxValue; //a
}
return true;
}
private bool DecodeRGB48(byte[] image_data, byte[] buff)
{
var size = m_Width * m_Height;
for (var i = 0; i < size; i++)
{
buff[i * 4] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 6 + 4)); //b
buff[i * 4 + 1] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 6 + 2)); //g
buff[i * 4 + 2] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 6)); //r
buff[i * 4 + 3] = byte.MaxValue; //a
}
return true;
}
private bool DecodeRGBA64(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outPutSize; i += 4)
{
buff[i] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 2 + 4)); //b
buff[i + 1] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 2 + 2)); //g
buff[i + 2] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 2)); //r
buff[i + 3] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 2 + 6)); //a
}
return true;
}
private bool UnpackCrunch(byte[] image_data, out byte[] result)
{
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3) //2017.3 and up