Add project files.

This commit is contained in:
Razmoth
2022-09-27 17:40:31 +04:00
parent 871d908948
commit a476ace7d7
305 changed files with 71340 additions and 84 deletions

View File

@@ -0,0 +1,35 @@
using System;
using System.Runtime.InteropServices;
using AssetStudio.PInvoke;
namespace ACL
{
public static class ACL
{
private const string DLL_NAME = "acl";
static ACL()
{
DllLoader.PreloadDll(DLL_NAME);
}
public static void DecompressAll(byte[] data, out float[] values, out float[] times)
{
var pinned = GCHandle.Alloc(data, GCHandleType.Pinned);
var pData = pinned.AddrOfPinnedObject();
DecompressAll(pData, out var pValues, out var numValues, out var pTimes, out var numTimes);
pinned.Free();
values = new float[numValues];
Marshal.Copy(pValues, values, 0, numValues);
times = new float[numTimes];
Marshal.Copy(pTimes, times, 0, numTimes);
}
#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);
#endregion
}
}

View File

@@ -0,0 +1,8 @@
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);
}
}

View File

@@ -0,0 +1,35 @@
using System;
using System.Runtime.InteropServices;
using AssetStudio.PInvoke;
namespace ACL
{
public static 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)
{
var pinned = GCHandle.Alloc(data, GCHandleType.Pinned);
var pData = pinned.AddrOfPinnedObject();
DecompressAll(pData, out var pValues, out var numValues, out var pTimes, out var numTimes);
pinned.Free();
values = new float[numValues];
Marshal.Copy(pValues, values, 0, numValues);
times = new float[numTimes];
Marshal.Copy(pTimes, times, 0, numTimes);
}
#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);
#endregion
}
}

View File

@@ -0,0 +1,436 @@
using System;
using System.Linq;
using System.Collections.Generic;
namespace AssetStudio
{
public class AnimationClipConverter
{
private readonly AnimationClip animationClip;
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")
{
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")
{
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")
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);
}
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")
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]);
}
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")
{
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")
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]);
}
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]);
}
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 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

@@ -0,0 +1,250 @@
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, game);
}
public static string ConvertSerializedAnimationClip(AnimationClip animationClip, Game game)
{
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

@@ -0,0 +1,65 @@
using Mono.Cecil;
using System.Collections.Generic;
using System.IO;
namespace AssetStudio
{
public class AssemblyLoader
{
public bool Loaded;
private Dictionary<string, ModuleDefinition> moduleDic = new Dictionary<string, ModuleDefinition>();
public void Load(string path)
{
var files = Directory.GetFiles(path, "*.dll");
var resolver = new MyAssemblyResolver();
var readerParameters = new ReaderParameters();
readerParameters.AssemblyResolver = resolver;
foreach (var file in files)
{
try
{
var assembly = AssemblyDefinition.ReadAssembly(file, readerParameters);
resolver.Register(assembly);
moduleDic.Add(assembly.MainModule.Name, assembly.MainModule);
}
catch
{
// ignored
}
}
Loaded = true;
}
public TypeDefinition GetTypeDefinition(string assemblyName, string fullName)
{
if (moduleDic.TryGetValue(assemblyName, out var module))
{
var typeDef = module.GetType(fullName);
if (typeDef == null && assemblyName == "UnityEngine.dll")
{
foreach (var pair in moduleDic)
{
typeDef = pair.Value.GetType(fullName);
if (typeDef != null)
{
break;
}
}
}
return typeDef;
}
return null;
}
public void Clear()
{
foreach (var pair in moduleDic)
{
pair.Value.Dispose();
}
moduleDic.Clear();
Loaded = false;
}
}
}

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net472;netstandard2.0;net5.0;net6.0</TargetFrameworks>
<Version>0.18.00</Version>
<AssemblyVersion>0.18.00</AssemblyVersion>
<FileVersion>0.18.00</FileVersion>
<Copyright>Copyright © Razmoth 2022; Copyright © Perfare 2018-2022</Copyright>
<DebugType>embedded</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Mono.Cecil" Version="0.11.3" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta13" />
</ItemGroup>
<ItemGroup>
<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

@@ -0,0 +1,191 @@
using FMOD;
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace AssetStudio
{
public class AudioClipConverter
{
private AudioClip m_AudioClip;
public AudioClipConverter(AudioClip audioClip)
{
m_AudioClip = audioClip;
}
public byte[] ConvertToWav()
{
var m_AudioData = m_AudioClip.m_AudioData.GetData();
if (m_AudioData == null || m_AudioData.Length == 0)
return null;
var exinfo = new CREATESOUNDEXINFO();
var result = Factory.System_Create(out var system);
if (result != RESULT.OK)
return null;
result = system.init(1, INITFLAGS.NORMAL, IntPtr.Zero);
if (result != RESULT.OK)
return null;
exinfo.cbsize = Marshal.SizeOf(exinfo);
exinfo.length = (uint)m_AudioClip.m_Size;
result = system.createSound(m_AudioData, MODE.OPENMEMORY, ref exinfo, out var sound);
if (result != RESULT.OK)
return null;
result = sound.getNumSubSounds(out var numsubsounds);
if (result != RESULT.OK)
return null;
byte[] buff;
if (numsubsounds > 0)
{
result = sound.getSubSound(0, out var subsound);
if (result != RESULT.OK)
return null;
buff = SoundToWav(subsound);
subsound.release();
}
else
{
buff = SoundToWav(sound);
}
sound.release();
system.release();
return buff;
}
public byte[] SoundToWav(Sound sound)
{
var result = sound.getFormat(out _, out _, out int channels, out int bits);
if (result != RESULT.OK)
return null;
result = sound.getDefaults(out var frequency, out _);
if (result != RESULT.OK)
return null;
var sampleRate = (int)frequency;
result = sound.getLength(out var length, TIMEUNIT.PCMBYTES);
if (result != RESULT.OK)
return null;
result = sound.@lock(0, length, out var ptr1, out var ptr2, out var len1, out var len2);
if (result != RESULT.OK)
return null;
byte[] buffer = new byte[len1 + 44];
//添加wav头
Encoding.UTF8.GetBytes("RIFF").CopyTo(buffer, 0);
BitConverter.GetBytes(len1 + 36).CopyTo(buffer, 4);
Encoding.UTF8.GetBytes("WAVEfmt ").CopyTo(buffer, 8);
BitConverter.GetBytes(16).CopyTo(buffer, 16);
BitConverter.GetBytes((short)1).CopyTo(buffer, 20);
BitConverter.GetBytes((short)channels).CopyTo(buffer, 22);
BitConverter.GetBytes(sampleRate).CopyTo(buffer, 24);
BitConverter.GetBytes(sampleRate * channels * bits / 8).CopyTo(buffer, 28);
BitConverter.GetBytes((short)(channels * bits / 8)).CopyTo(buffer, 32);
BitConverter.GetBytes((short)bits).CopyTo(buffer, 34);
Encoding.UTF8.GetBytes("data").CopyTo(buffer, 36);
BitConverter.GetBytes(len1).CopyTo(buffer, 40);
Marshal.Copy(ptr1, buffer, 44, (int)len1);
result = sound.unlock(ptr1, ptr2, len1, len2);
if (result != RESULT.OK)
return null;
return buffer;
}
public string GetExtensionName()
{
if (m_AudioClip.version[0] < 5)
{
switch (m_AudioClip.m_Type)
{
case FMODSoundType.ACC:
return ".m4a";
case FMODSoundType.AIFF:
return ".aif";
case FMODSoundType.IT:
return ".it";
case FMODSoundType.MOD:
return ".mod";
case FMODSoundType.MPEG:
return ".mp3";
case FMODSoundType.OGGVORBIS:
return ".ogg";
case FMODSoundType.S3M:
return ".s3m";
case FMODSoundType.WAV:
return ".wav";
case FMODSoundType.XM:
return ".xm";
case FMODSoundType.XMA:
return ".wav";
case FMODSoundType.VAG:
return ".vag";
case FMODSoundType.AUDIOQUEUE:
return ".fsb";
}
}
else
{
switch (m_AudioClip.m_CompressionFormat)
{
case AudioCompressionFormat.PCM:
return ".fsb";
case AudioCompressionFormat.Vorbis:
return ".fsb";
case AudioCompressionFormat.ADPCM:
return ".fsb";
case AudioCompressionFormat.MP3:
return ".fsb";
case AudioCompressionFormat.PSMVAG:
return ".fsb";
case AudioCompressionFormat.HEVAG:
return ".fsb";
case AudioCompressionFormat.XMA:
return ".fsb";
case AudioCompressionFormat.AAC:
return ".m4a";
case AudioCompressionFormat.GCADPCM:
return ".fsb";
case AudioCompressionFormat.ATRAC9:
return ".fsb";
}
}
return ".AudioClip";
}
public bool IsSupport
{
get
{
if (m_AudioClip.version[0] < 5)
{
switch (m_AudioClip.m_Type)
{
case FMODSoundType.AIFF:
case FMODSoundType.IT:
case FMODSoundType.MOD:
case FMODSoundType.S3M:
case FMODSoundType.XM:
case FMODSoundType.XMA:
case FMODSoundType.AUDIOQUEUE:
return true;
default:
return false;
}
}
else
{
switch (m_AudioClip.m_CompressionFormat)
{
case AudioCompressionFormat.PCM:
case AudioCompressionFormat.Vorbis:
case AudioCompressionFormat.ADPCM:
case AudioCompressionFormat.MP3:
case AudioCompressionFormat.XMA:
return true;
default:
return false;
}
}
}
}
}
}

View File

@@ -0,0 +1,221 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace SpirV
{
public struct ModuleHeader
{
public Version Version { get; set; }
public string GeneratorVendor { get; set; }
public string GeneratorName { get; set; }
public int GeneratorVersion { get; set; }
public uint Bound { get; set; }
public uint Reserved { get; set; }
}
[Flags]
public enum DisassemblyOptions
{
None,
ShowTypes,
ShowNames,
Default = ShowTypes | ShowNames
}
public class Disassembler
{
public string Disassemble (Module module)
{
return Disassemble(module, DisassemblyOptions.Default);
}
public string Disassemble(Module module, DisassemblyOptions options)
{
m_sb.AppendLine("; SPIR-V");
m_sb.Append("; Version: ").Append(module.Header.Version).AppendLine();
if (module.Header.GeneratorName == null)
{
m_sb.Append("; Generator: unknown; ").Append(module.Header.GeneratorVersion).AppendLine();
}
else
{
m_sb.Append("; Generator: ").Append(module.Header.GeneratorVendor).Append(' ').
Append(module.Header.GeneratorName).Append("; ").Append(module.Header.GeneratorVersion).AppendLine();
}
m_sb.Append("; Bound: ").Append(module.Header.Bound).AppendLine();
m_sb.Append("; Schema: ").Append(module.Header.Reserved).AppendLine();
string[] lines = new string[module.Instructions.Count + 1];
lines[0] = m_sb.ToString();
m_sb.Clear();
for (int i = 0; i < module.Instructions.Count; i++)
{
ParsedInstruction instruction = module.Instructions[i];
PrintInstruction(m_sb, instruction, options);
lines[i + 1] = m_sb.ToString();
m_sb.Clear();
}
int longestPrefix = 0;
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i];
longestPrefix = Math.Max(longestPrefix, line.IndexOf('='));
if (longestPrefix > 50)
{
longestPrefix = 50;
break;
}
}
m_sb.Append(lines[0]);
for (int i = 1; i < lines.Length; i++)
{
string line = lines[i];
int index = line.IndexOf('=');
if (index == -1)
{
m_sb.Append(' ', longestPrefix + 4);
m_sb.Append(line);
}
else
{
int pad = Math.Max(0, longestPrefix - index);
m_sb.Append(' ', pad);
m_sb.Append(line, 0, index);
m_sb.Append('=');
m_sb.Append(line, index + 1, line.Length - index - 1);
}
m_sb.AppendLine();
}
string result = m_sb.ToString();
m_sb.Clear();
return result;
}
private static void PrintInstruction(StringBuilder sb, ParsedInstruction instruction, DisassemblyOptions options)
{
if (instruction.Operands.Count == 0)
{
sb.Append(instruction.Instruction.Name);
return;
}
int currentOperand = 0;
if (instruction.Instruction.Operands[currentOperand].Type is IdResultType)
{
if (options.HasFlag(DisassemblyOptions.ShowTypes))
{
instruction.ResultType.ToString(sb).Append(' ');
}
++currentOperand;
}
if (currentOperand < instruction.Operands.Count && instruction.Instruction.Operands[currentOperand].Type is IdResult)
{
if (!options.HasFlag(DisassemblyOptions.ShowNames) || string.IsNullOrWhiteSpace(instruction.Name))
{
PrintOperandValue(sb, instruction.Operands[currentOperand].Value, options);
}
else
{
sb.Append(instruction.Name);
}
sb.Append(" = ");
++currentOperand;
}
sb.Append(instruction.Instruction.Name);
sb.Append(' ');
for (; currentOperand < instruction.Operands.Count; ++currentOperand)
{
PrintOperandValue(sb, instruction.Operands[currentOperand].Value, options);
sb.Append(' ');
}
}
private static void PrintOperandValue(StringBuilder sb, object value, DisassemblyOptions options)
{
switch (value)
{
case System.Type t:
sb.Append(t.Name);
break;
case string s:
{
sb.Append('"');
sb.Append(s);
sb.Append('"');
}
break;
case ObjectReference or:
{
if (options.HasFlag(DisassemblyOptions.ShowNames) && or.Reference != null && !string.IsNullOrWhiteSpace(or.Reference.Name))
{
sb.Append(or.Reference.Name);
}
else
{
or.ToString(sb);
}
}
break;
case IBitEnumOperandValue beov:
PrintBitEnumValue(sb, beov, options);
break;
case IValueEnumOperandValue veov:
PrintValueEnumValue(sb, veov, options);
break;
case VaryingOperandValue varOpVal:
varOpVal.ToString(sb);
break;
default:
sb.Append(value);
break;
}
}
private static void PrintBitEnumValue(StringBuilder sb, IBitEnumOperandValue enumOperandValue, DisassemblyOptions options)
{
foreach (uint key in enumOperandValue.Values.Keys)
{
sb.Append(enumOperandValue.EnumerationType.GetEnumName(key));
IReadOnlyList<object> value = enumOperandValue.Values[key];
if (value.Count != 0)
{
sb.Append(' ');
foreach (object v in value)
{
PrintOperandValue(sb, v, options);
}
}
}
}
private static void PrintValueEnumValue(StringBuilder sb, IValueEnumOperandValue valueOperandValue, DisassemblyOptions options)
{
sb.Append(valueOperandValue.Key);
if (valueOperandValue.Value is IList<object> valueList && valueList.Count > 0)
{
sb.Append(' ');
foreach (object v in valueList)
{
PrintOperandValue(sb, v, options);
}
}
}
private readonly StringBuilder m_sb = new StringBuilder();
}
}

View File

@@ -0,0 +1,42 @@
#if NETSTANDARD1_0 || NETSTANDARD1_1 || NETSTANDARD1_2 || NETSTANDARD1_3 || NETSTANDARD1_4 || NETSTANDARD1_5 || NETSTANDARD1_6
using System;
using System.Linq;
using System.Reflection;
namespace SpirV
{
public static class EnumValuesExtensions
{
public static Array GetEnumValues(this System.Type _this)
{
TypeInfo typeInfo = _this.GetTypeInfo ();
if (!typeInfo.IsEnum) {
throw new ArgumentException ("GetEnumValues: Type '" + _this.Name + "' is not an enum");
}
return
(
from field in typeInfo.DeclaredFields
where field.IsLiteral
select field.GetValue (null)
)
.ToArray();
}
public static string GetEnumName(this System.Type _this, object value)
{
TypeInfo typeInfo = _this.GetTypeInfo ();
if (!typeInfo.IsEnum) {
throw new ArgumentException ("GetEnumName: Type '" + _this.Name + "' is not an enum");
}
return
(
from field in typeInfo.DeclaredFields
where field.IsLiteral && (uint)field.GetValue(null) == (uint)value
select field.Name
)
.First();
}
}
}
#endif

View File

@@ -0,0 +1,51 @@
using System.Collections.Generic;
namespace SpirV
{
public enum OperandQuantifier
{
/// <summary>
/// 1
/// </summary>
Default,
/// <summary>
/// 0 or 1
/// </summary>
Optional,
/// <summary>
/// 0+
/// </summary>
Varying
}
public class Operand
{
public Operand(OperandType kind, string name, OperandQuantifier quantifier)
{
Name = name;
Type = kind;
Quantifier = quantifier;
}
public string Name { get; }
public OperandType Type { get; }
public OperandQuantifier Quantifier { get; }
}
public class Instruction
{
public Instruction (string name)
: this (name, new List<Operand> ())
{
}
public Instruction (string name, IReadOnlyList<Operand> operands)
{
Operands = operands;
Name = name;
}
public string Name { get; }
public IReadOnlyList<Operand> Operands { get; }
}
}

View File

@@ -0,0 +1,25 @@
BSD 2-Clause License
Copyright (c) 2017, Matthäus G. Chajdas
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,426 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
namespace SpirV
{
public class Module
{
[StructLayout(LayoutKind.Explicit)]
private struct FloatUIntUnion
{
[FieldOffset(0)]
public uint Int;
[FieldOffset(0)]
public float Float;
}
[StructLayout(LayoutKind.Explicit)]
private struct DoubleULongUnion
{
[FieldOffset(0)]
public ulong Long;
[FieldOffset(0)]
public double Double;
}
public Module(ModuleHeader header, IReadOnlyList<ParsedInstruction> instructions)
{
Header = header;
Instructions = instructions;
Read(Instructions, objects_);
}
public static bool IsDebugInstruction(ParsedInstruction instruction)
{
return debugInstructions_.Contains(instruction.Instruction.Name);
}
private static void Read(IReadOnlyList<ParsedInstruction> instructions, Dictionary<uint, ParsedInstruction> objects)
{
// Debug instructions can be only processed after everything
// else has been parsed, as they may reference types which haven't
// been seen in the file yet
List<ParsedInstruction> debugInstructions = new List<ParsedInstruction>();
// Entry points contain forward references
// Those need to be resolved afterwards
List<ParsedInstruction> entryPoints = new List<ParsedInstruction>();
foreach (var instruction in instructions)
{
if (IsDebugInstruction(instruction))
{
debugInstructions.Add(instruction);
continue;
}
if (instruction.Instruction is OpEntryPoint)
{
entryPoints.Add(instruction);
continue;
}
if (instruction.Instruction.Name.StartsWith("OpType", StringComparison.Ordinal))
{
ProcessTypeInstruction(instruction, objects);
}
instruction.ResolveResultType(objects);
if (instruction.HasResult)
{
objects[instruction.ResultId] = instruction;
}
switch (instruction.Instruction)
{
// Constants require that the result type has been resolved
case OpSpecConstant sc:
case OpConstant oc:
{
Type t = instruction.ResultType;
Debug.Assert (t != null);
Debug.Assert (t is ScalarType);
object constant = ConvertConstant(instruction.ResultType as ScalarType, instruction.Words, 3);
instruction.Operands[2].Value = constant;
instruction.Value = constant;
}
break;
}
}
foreach (ParsedInstruction instruction in debugInstructions)
{
switch (instruction.Instruction)
{
case OpMemberName mn:
{
StructType t = (StructType)objects[instruction.Words[1]].ResultType;
t.SetMemberName((uint)instruction.Operands[1].Value, (string)instruction.Operands[2].Value);
}
break;
case OpName n:
{
// We skip naming objects we don't know about
ParsedInstruction t = objects[instruction.Words[1]];
t.Name = (string)instruction.Operands[1].Value;
}
break;
}
}
foreach (ParsedInstruction instruction in instructions)
{
instruction.ResolveReferences(objects);
}
}
public static Module ReadFrom(Stream stream)
{
BinaryReader br = new BinaryReader(stream);
Reader reader = new Reader(br);
uint versionNumber = reader.ReadDWord();
int majorVersion = (int)(versionNumber >> 16);
int minorVersion = (int)((versionNumber >> 8) & 0xFF);
Version version = new Version(majorVersion, minorVersion);
uint generatorMagicNumber = reader.ReadDWord();
int generatorToolId = (int)(generatorMagicNumber >> 16);
string generatorVendor = "unknown";
string generatorName = null;
if (Meta.Tools.ContainsKey(generatorToolId))
{
Meta.ToolInfo toolInfo = Meta.Tools[generatorToolId];
generatorVendor = toolInfo.Vendor;
if (toolInfo.Name != null)
{
generatorName = toolInfo.Name;
}
}
// Read header
ModuleHeader header = new ModuleHeader();
header.Version = version;
header.GeneratorName = generatorName;
header.GeneratorVendor = generatorVendor;
header.GeneratorVersion = (int)(generatorMagicNumber & 0xFFFF);
header.Bound = reader.ReadDWord();
header.Reserved = reader.ReadDWord();
List<ParsedInstruction> instructions = new List<ParsedInstruction>();
while (!reader.EndOfStream)
{
uint instructionStart = reader.ReadDWord ();
ushort wordCount = (ushort)(instructionStart >> 16);
int opCode = (int)(instructionStart & 0xFFFF);
uint[] words = new uint[wordCount];
words[0] = instructionStart;
for (ushort i = 1; i < wordCount; ++i)
{
words[i] = reader.ReadDWord();
}
ParsedInstruction instruction = new ParsedInstruction(opCode, words);
instructions.Add(instruction);
}
return new Module(header, instructions);
}
/// <summary>
/// Collect types from OpType* instructions
/// </summary>
private static void ProcessTypeInstruction(ParsedInstruction i, IReadOnlyDictionary<uint, ParsedInstruction> objects)
{
switch (i.Instruction)
{
case OpTypeInt t:
{
i.ResultType = new IntegerType((int)i.Words[2], i.Words[3] == 1u);
}
break;
case OpTypeFloat t:
{
i.ResultType = new FloatingPointType((int)i.Words[2]);
}
break;
case OpTypeVector t:
{
i.ResultType = new VectorType((ScalarType)objects[i.Words[2]].ResultType, (int)i.Words[3]);
}
break;
case OpTypeMatrix t:
{
i.ResultType = new MatrixType((VectorType)objects[i.Words[2]].ResultType, (int)i.Words[3]);
}
break;
case OpTypeArray t:
{
object constant = objects[i.Words[3]].Value;
int size = 0;
switch (constant)
{
case ushort u16:
size = u16;
break;
case uint u32:
size = (int)u32;
break;
case ulong u64:
size = (int)u64;
break;
case short i16:
size = i16;
break;
case int i32:
size = i32;
break;
case long i64:
size = (int)i64;
break;
}
i.ResultType = new ArrayType(objects[i.Words[2]].ResultType, size);
}
break;
case OpTypeRuntimeArray t:
{
i.ResultType = new RuntimeArrayType((Type)objects[i.Words[2]].ResultType);
}
break;
case OpTypeBool t:
{
i.ResultType = new BoolType();
}
break;
case OpTypeOpaque t:
{
i.ResultType = new OpaqueType();
}
break;
case OpTypeVoid t:
{
i.ResultType = new VoidType();
}
break;
case OpTypeImage t:
{
Type sampledType = objects[i.Operands[1].GetId ()].ResultType;
Dim dim = i.Operands[2].GetSingleEnumValue<Dim>();
uint depth = (uint)i.Operands[3].Value;
bool isArray = (uint)i.Operands[4].Value != 0;
bool isMultiSampled = (uint)i.Operands[5].Value != 0;
uint sampled = (uint)i.Operands[6].Value;
ImageFormat imageFormat = i.Operands[7].GetSingleEnumValue<ImageFormat>();
i.ResultType = new ImageType(sampledType,
dim,
(int)depth, isArray, isMultiSampled,
(int)sampled, imageFormat,
i.Operands.Count > 8 ? i.Operands[8].GetSingleEnumValue<AccessQualifier>() : AccessQualifier.ReadOnly);
}
break;
case OpTypeSampler st:
{
i.ResultType = new SamplerType();
break;
}
case OpTypeSampledImage t:
{
i.ResultType = new SampledImageType((ImageType)objects[i.Words[2]].ResultType);
}
break;
case OpTypeFunction t:
{
List<Type> parameterTypes = new List<Type>();
for (int j = 3; j < i.Words.Count; ++j)
{
parameterTypes.Add(objects[i.Words[j]].ResultType);
}
i.ResultType = new FunctionType(objects[i.Words[2]].ResultType, parameterTypes);
}
break;
case OpTypeForwardPointer t:
{
// We create a normal pointer, but with unspecified type
// This will get resolved later on
i.ResultType = new PointerType((StorageClass)i.Words[2]);
}
break;
case OpTypePointer t:
{
if (objects.ContainsKey(i.Words[1]))
{
// If there is something present, it must have been
// a forward reference. The storage type must
// match
PointerType pt = (PointerType)i.ResultType;
Debug.Assert (pt != null);
Debug.Assert (pt.StorageClass == (StorageClass)i.Words[2]);
pt.ResolveForwardReference (objects[i.Words[3]].ResultType);
}
else
{
i.ResultType = new PointerType((StorageClass)i.Words[2], objects[i.Words[3]].ResultType);
}
}
break;
case OpTypeStruct t:
{
List<Type> memberTypes = new List<Type>();
for (int j = 2; j < i.Words.Count; ++j)
{
memberTypes.Add(objects[i.Words[j]].ResultType);
}
i.ResultType = new StructType(memberTypes);
}
break;
}
}
private static object ConvertConstant(ScalarType type, IReadOnlyList<uint> words, int index)
{
switch (type)
{
case IntegerType i:
{
if (i.Signed)
{
if (i.Width == 16)
{
return unchecked((short)(words[index]));
}
else if (i.Width == 32)
{
return unchecked((int)(words[index]));
}
else if (i.Width == 64)
{
return unchecked((long)(words[index] | (ulong)(words[index + 1]) << 32));
}
}
else
{
if (i.Width == 16)
{
return unchecked((ushort)(words[index]));
}
else if (i.Width == 32)
{
return words[index];
}
else if (i.Width == 64)
{
return words[index] | (ulong)(words[index + 1]) << 32;
}
}
throw new Exception ("Cannot construct integer literal.");
}
case FloatingPointType f:
{
if (f.Width == 32)
{
return new FloatUIntUnion { Int = words[0] }.Float;
}
else if (f.Width == 64)
{
return new DoubleULongUnion { Long = (words[index] | (ulong)(words[index + 1]) << 32) }.Double;
}
else
{
throw new Exception("Cannot construct floating point literal.");
}
}
}
return null;
}
public ModuleHeader Header { get; }
public IReadOnlyList<ParsedInstruction> Instructions { get; }
private static HashSet<string> debugInstructions_ = new HashSet<string>
{
"OpSourceContinued",
"OpSource",
"OpSourceExtension",
"OpName",
"OpMemberName",
"OpString",
"OpLine",
"OpNoLine",
"OpModuleProcessed"
};
private readonly Dictionary<uint, ParsedInstruction> objects_ = new Dictionary<uint, ParsedInstruction>();
}
}

View File

@@ -0,0 +1,302 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Reflection;
namespace SpirV
{
public class OperandType
{
public virtual bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
// This returns the dynamic type
value = GetType();
wordsUsed = 1;
return true;
}
}
public class Literal : OperandType
{
}
public class LiteralNumber : Literal
{
}
// The SPIR-V JSON file uses only literal integers
public class LiteralInteger : LiteralNumber
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
value = words[index];
wordsUsed = 1;
return true;
}
}
public class LiteralString : Literal
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
// This is just a fail-safe -- the loop below must terminate
wordsUsed = 1;
int bytesUsed = 0;
byte[] bytes = new byte[(words.Count - index) * 4];
for (int i = index; i < words.Count; ++i)
{
uint word = words[i];
byte b0 = (byte)(word & 0xFF);
if (b0 == 0)
{
break;
}
else
{
bytes[bytesUsed++] = b0;
}
byte b1 = (byte)((word >> 8) & 0xFF);
if (b1 == 0)
{
break;
}
else
{
bytes[bytesUsed++] = b1;
}
byte b2 = (byte)((word >> 16) & 0xFF);
if (b2 == 0)
{
break;
}
else
{
bytes[bytesUsed++] = b2;
}
byte b3 = (byte)(word >> 24);
if (b3 == 0)
{
break;
}
else
{
bytes[bytesUsed++] = b3;
}
wordsUsed++;
}
value = Encoding.UTF8.GetString(bytes, 0, bytesUsed);
return true;
}
}
public class LiteralContextDependentNumber : Literal
{
// This is handled during parsing by ConvertConstant
}
public class LiteralExtInstInteger : Literal
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
value = words[index];
wordsUsed = 1;
return true;
}
}
public class LiteralSpecConstantOpInteger : Literal
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
List<ObjectReference> result = new List<ObjectReference>();
for (int i = index; i < words.Count; i++)
{
ObjectReference objRef = new ObjectReference(words[i]);
result.Add(objRef);
}
value = result;
wordsUsed = words.Count - index;
return true;
}
}
public class Parameter
{
public virtual IReadOnlyList<OperandType> OperandTypes { get; }
}
public class ParameterFactory
{
public virtual Parameter CreateParameter(object value)
{
return null;
}
}
public class EnumType<T> : EnumType<T, ParameterFactory>
where T : Enum
{
};
public class EnumType<T, U> : OperandType
where T : Enum
where U : ParameterFactory, new ()
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
int wordsUsedForParameters = 0;
if (typeof(T).GetTypeInfo().GetCustomAttributes<FlagsAttribute>().Any())
{
Dictionary<uint, IReadOnlyList<object>> result = new Dictionary<uint, IReadOnlyList<object>>();
foreach (object enumValue in EnumerationType.GetEnumValues())
{
uint bit = (uint)enumValue;
// bit == 0 and words[0] == 0 handles the 0x0 = None cases
if ((words[index] & bit) != 0 || (bit == 0 && words[index] == 0))
{
Parameter p = parameterFactory_.CreateParameter(bit);
if (p == null)
{
result.Add(bit, Array.Empty<object>());
}
else
{
object[] resultItems = new object[p.OperandTypes.Count];
for (int j = 0; j < p.OperandTypes.Count; ++j)
{
p.OperandTypes[j].ReadValue(words, 1 + wordsUsedForParameters, out object pValue, out int pWordsUsed);
wordsUsedForParameters += pWordsUsed;
resultItems[j] = pValue;
}
result.Add(bit, resultItems);
}
}
}
value = new BitEnumOperandValue<T>(result);
}
else
{
object[] resultItems;
Parameter p = parameterFactory_.CreateParameter(words[index]);
if (p == null)
{
resultItems = Array.Empty<object>();
}
else
{
resultItems = new object[p.OperandTypes.Count];
for (int j = 0; j < p.OperandTypes.Count; ++j)
{
p.OperandTypes[j].ReadValue(words, 1 + wordsUsedForParameters, out object pValue, out int pWordsUsed);
wordsUsedForParameters += pWordsUsed;
resultItems[j] = pValue;
}
}
value = new ValueEnumOperandValue<T>((T)(object)words[index], resultItems);
}
wordsUsed = wordsUsedForParameters + 1;
return true;
}
public System.Type EnumerationType => typeof(T);
private U parameterFactory_ = new U();
}
public class IdScope : OperandType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
value = (Scope)words[index];
wordsUsed = 1;
return true;
}
}
public class IdMemorySemantics : OperandType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
value = (MemorySemantics)words[index];
wordsUsed = 1;
return true;
}
}
public class IdType : OperandType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
value = words[index];
wordsUsed = 1;
return true;
}
}
public class IdResult : IdType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
value = new ObjectReference(words[index]);
wordsUsed = 1;
return true;
}
}
public class IdResultType : IdType
{
}
public class IdRef : IdType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
value = new ObjectReference(words[index]);
wordsUsed = 1;
return true;
}
}
public class PairIdRefIdRef : OperandType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
ObjectReference variable = new ObjectReference(words[index]);
ObjectReference parent = new ObjectReference(words[index + 1]);
value = new { Variable = variable, Parent = parent };
wordsUsed = 2;
return true;
}
}
public class PairIdRefLiteralInteger : OperandType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
ObjectReference type = new ObjectReference(words[index]);
uint word = words[index + 1];
value = new { Type = type, Member = word };
wordsUsed = 2;
return true;
}
}
public class PairLiteralIntegerIdRef : OperandType
{
public override bool ReadValue(IReadOnlyList<uint> words, int index, out object value, out int wordsUsed)
{
uint selector = words[index];
ObjectReference label = new ObjectReference(words[index + 1]);
value = new { Selector = selector, Label = label };
wordsUsed = 2;
return true;
}
}
}

View File

@@ -0,0 +1,265 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace SpirV
{
public class ParsedOperand
{
public ParsedOperand(IReadOnlyList<uint> words, int index, int count, object value, Operand operand)
{
uint[] array = new uint[count];
for (int i = 0; i < count; i++)
{
array[i] = words[index + i];
}
Words = array;
Value = value;
Operand = operand;
}
public T GetSingleEnumValue<T>()
where T : Enum
{
IValueEnumOperandValue v = (IValueEnumOperandValue)Value;
if (v.Value.Count == 0)
{
// If there's no value at all, the enum is probably something like ImageFormat.
// In which case we just return the enum value
return (T)v.Key;
}
else
{
// This means the enum has a value attached to it, so we return the attached value
return (T)((IValueEnumOperandValue)Value).Value[0];
}
}
public uint GetId()
{
return ((ObjectReference)Value).Id;
}
public T GetBitEnumValue<T>()
where T : Enum
{
var v = Value as IBitEnumOperandValue;
uint result = 0;
foreach (var k in v.Values.Keys)
{
result |= k;
}
return (T)(object)result;
}
public IReadOnlyList<uint> Words { get; }
public object Value { get; set; }
public Operand Operand { get; }
}
public class VaryingOperandValue
{
public VaryingOperandValue(IReadOnlyList<object> values)
{
Values = values;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
ToString(sb);
return sb.ToString();
}
public StringBuilder ToString(StringBuilder sb)
{
for (int i = 0; i < Values.Count; ++i)
{
if (Values[i] is ObjectReference objRef)
{
objRef.ToString(sb);
}
else
{
sb.Append(Values[i]);
}
if (i < (Values.Count - 1))
{
sb.Append(' ');
}
}
return sb;
}
public IReadOnlyList<object> Values { get; }
}
public interface IEnumOperandValue
{
System.Type EnumerationType { get; }
}
public interface IBitEnumOperandValue : IEnumOperandValue
{
IReadOnlyDictionary<uint, IReadOnlyList<object>> Values { get; }
}
public interface IValueEnumOperandValue : IEnumOperandValue
{
object Key { get; }
IReadOnlyList<object> Value { get; }
}
public class ValueEnumOperandValue<T> : IValueEnumOperandValue
where T : Enum
{
public ValueEnumOperandValue(T key, IReadOnlyList<object> value)
{
Key = key;
Value = value;
}
public System.Type EnumerationType => typeof(T);
public object Key { get; }
public IReadOnlyList<object> Value { get; }
}
public class BitEnumOperandValue<T> : IBitEnumOperandValue
where T : Enum
{
public BitEnumOperandValue(Dictionary<uint, IReadOnlyList<object>> values)
{
Values = values;
}
public IReadOnlyDictionary<uint, IReadOnlyList<object>> Values { get; }
public System.Type EnumerationType => typeof(T);
}
public class ObjectReference
{
public ObjectReference(uint id)
{
Id = id;
}
public void Resolve(IReadOnlyDictionary<uint, ParsedInstruction> objects)
{
Reference = objects[Id];
}
public override string ToString()
{
return $"%{Id}";
}
public StringBuilder ToString(StringBuilder sb)
{
return sb.Append('%').Append(Id);
}
public uint Id { get; }
public ParsedInstruction Reference { get; private set; }
}
public class ParsedInstruction
{
public ParsedInstruction(int opCode, IReadOnlyList<uint> words)
{
Words = words;
Instruction = Instructions.OpcodeToInstruction[opCode];
ParseOperands();
}
private void ParseOperands()
{
if (Instruction.Operands.Count == 0)
{
return;
}
// Word 0 describes this instruction so we can ignore it
int currentWord = 1;
int currentOperand = 0;
List<object> varyingOperandValues = new List<object>();
int varyingWordStart = 0;
Operand varyingOperand = null;
while (currentWord < Words.Count)
{
Operand operand = Instruction.Operands[currentOperand];
operand.Type.ReadValue(Words, currentWord, out object value, out int wordsUsed);
if (operand.Quantifier == OperandQuantifier.Varying)
{
varyingOperandValues.Add(value);
varyingWordStart = currentWord;
varyingOperand = operand;
}
else
{
int wordCount = Math.Min(Words.Count - currentWord, wordsUsed);
ParsedOperand parsedOperand = new ParsedOperand(Words, currentWord, wordCount, value, operand);
Operands.Add(parsedOperand);
}
currentWord += wordsUsed;
if (operand.Quantifier != OperandQuantifier.Varying)
{
++currentOperand;
}
}
if (varyingOperand != null)
{
VaryingOperandValue varOperantValue = new VaryingOperandValue(varyingOperandValues);
ParsedOperand parsedOperand = new ParsedOperand(Words, currentWord, Words.Count - currentWord, varOperantValue, varyingOperand);
Operands.Add(parsedOperand);
}
}
public void ResolveResultType(IReadOnlyDictionary<uint, ParsedInstruction> objects)
{
if (Instruction.Operands.Count > 0 && Instruction.Operands[0].Type is IdResultType)
{
ResultType = objects[(uint)Operands[0].Value].ResultType;
}
}
public void ResolveReferences (IReadOnlyDictionary<uint, ParsedInstruction> objects)
{
foreach (var operand in Operands)
{
if (operand.Value is ObjectReference objectReference)
{
objectReference.Resolve (objects);
}
}
}
public Type ResultType { get; set; }
public uint ResultId
{
get
{
for (int i = 0; i < Instruction.Operands.Count; ++i)
{
if (Instruction.Operands[i].Type is IdResult)
{
return Operands[i].GetId();
}
}
return 0;
}
}
public bool HasResult => ResultId != 0;
public IReadOnlyList<uint> Words { get; }
public Instruction Instruction { get; }
public IList<ParsedOperand> Operands { get; } = new List<ParsedOperand>();
public string Name { get; set; }
public object Value { get; set; }
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.IO;
using System.Runtime.CompilerServices;
namespace SpirV
{
internal sealed class Reader
{
public Reader(BinaryReader reader)
{
reader_ = reader;
uint magicNumber = reader_.ReadUInt32();
if (magicNumber == Meta.MagicNumber)
{
littleEndian_ = true;
}
else if (Reverse(magicNumber) == Meta.MagicNumber)
{
littleEndian_ = false;
}
else
{
throw new Exception("Invalid magic number");
}
}
public uint ReadDWord()
{
if (littleEndian_)
{
return reader_.ReadUInt32 ();
}
else
{
return Reverse(reader_.ReadUInt32());
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint Reverse(uint u)
{
return (u << 24) | (u & 0xFF00U) << 8 | (u >> 8) & 0xFF00U | (u >> 24);
}
public bool EndOfStream => reader_.BaseStream.Position == reader_.BaseStream.Length;
private readonly BinaryReader reader_;
private readonly bool littleEndian_;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,54 @@
using System.Collections.Generic;
namespace SpirV
{
internal class Meta
{
public class ToolInfo
{
public ToolInfo(string vendor)
{
Vendor = vendor;
}
public ToolInfo(string vendor, string name)
{
Vendor = vendor;
Name = name;
}
public string Name { get; }
public string Vendor { get; }
}
public static uint MagicNumber => 119734787U;
public static uint Version => 66048U;
public static uint Revision => 2U;
public static uint OpCodeMask => 65535U;
public static uint WordCountShift => 16U;
public static IReadOnlyDictionary<int, ToolInfo> Tools => toolInfos_;
private readonly static Dictionary<int, ToolInfo> toolInfos_ = new Dictionary<int, ToolInfo>
{
{ 0, new ToolInfo("Khronos") },
{ 1, new ToolInfo("LunarG") },
{ 2, new ToolInfo("Valve") },
{ 3, new ToolInfo("Codeplay") },
{ 4, new ToolInfo("NVIDIA") },
{ 5, new ToolInfo("ARM") },
{ 6, new ToolInfo("Khronos", "LLVM/SPIR-V Translator") },
{ 7, new ToolInfo("Khronos", "SPIR-V Tools Assembler") },
{ 8, new ToolInfo("Khronos", "Glslang Reference Front End") },
{ 9, new ToolInfo("Qualcomm") },
{ 10, new ToolInfo("AMD") },
{ 11, new ToolInfo("Intel") },
{ 12, new ToolInfo("Imagination") },
{ 13, new ToolInfo("Google", "Shaderc over Glslang") },
{ 14, new ToolInfo("Google", "spiregg") },
{ 15, new ToolInfo("Google", "rspirv") },
{ 16, new ToolInfo("X-LEGEND", "Mesa-IR/SPIR-V Translator") },
{ 17, new ToolInfo("Khronos", "SPIR-V Tools Linker") },
};
}
}

View File

@@ -0,0 +1,428 @@
using System.Collections.Generic;
using System.Text;
namespace SpirV
{
public class Type
{
public virtual StringBuilder ToString(StringBuilder sb)
{
return sb;
}
}
public class VoidType : Type
{
public override string ToString()
{
return "void";
}
public override StringBuilder ToString(StringBuilder sb)
{
return sb.Append("void");
}
}
public class ScalarType : Type
{
}
public class BoolType : ScalarType
{
public override string ToString()
{
return "bool";
}
public override StringBuilder ToString(StringBuilder sb)
{
return sb.Append("bool");
}
}
public class IntegerType : ScalarType
{
public IntegerType (int width, bool signed)
{
Width = width;
Signed = signed;
}
public override string ToString()
{
if (Signed)
{
return $"i{Width}";
}
else
{
return $"u{Width}";
}
}
public override StringBuilder ToString(StringBuilder sb)
{
if (Signed)
{
sb.Append('i').Append(Width);
}
else
{
sb.Append('u').Append(Width);
}
return sb;
}
public int Width { get; }
public bool Signed { get; }
}
public class FloatingPointType : ScalarType
{
public FloatingPointType (int width)
{
Width = width;
}
public override string ToString()
{
return $"f{Width}";
}
public override StringBuilder ToString(StringBuilder sb)
{
return sb.Append('f').Append(Width);
}
public int Width { get; }
}
public class VectorType : Type
{
public VectorType (ScalarType scalarType, int componentCount)
{
ComponentType = scalarType;
ComponentCount = componentCount;
}
public override string ToString()
{
return $"{ComponentType}_{ComponentCount}";
}
public override StringBuilder ToString(StringBuilder sb)
{
return ComponentType.ToString(sb).Append('_').Append(ComponentCount);
}
public ScalarType ComponentType { get; }
public int ComponentCount { get; }
}
public class MatrixType : Type
{
public MatrixType (VectorType vectorType, int columnCount)
{
ColumnType = vectorType;
ColumnCount = columnCount;
}
public override string ToString ()
{
return $"{ColumnType}x{ColumnCount}";
}
public override StringBuilder ToString(StringBuilder sb)
{
return sb.Append(ColumnType).Append('x').Append(ColumnCount);
}
public VectorType ColumnType { get; }
public int ColumnCount { get; }
public int RowCount => ColumnType.ComponentCount;
}
public class ImageType : Type
{
public ImageType (Type sampledType, Dim dim, int depth, bool isArray, bool isMultisampled, int sampleCount,
ImageFormat imageFormat, AccessQualifier accessQualifier)
{
SampledType = sampledType;
Dim = dim;
Depth = depth;
IsArray = isArray;
IsMultisampled = isMultisampled;
SampleCount = sampleCount;
Format = imageFormat;
AccessQualifier = accessQualifier;
}
public override string ToString ()
{
StringBuilder sb = new StringBuilder ();
ToString(sb);
return sb.ToString();
}
public override StringBuilder ToString(StringBuilder sb)
{
switch (AccessQualifier)
{
case AccessQualifier.ReadWrite:
sb.Append("read_write ");
break;
case AccessQualifier.WriteOnly:
sb.Append("write_only ");
break;
case AccessQualifier.ReadOnly:
sb.Append("read_only ");
break;
}
sb.Append("Texture");
switch (Dim)
{
case Dim.Dim1D:
sb.Append("1D");
break;
case Dim.Dim2D:
sb.Append("2D");
break;
case Dim.Dim3D:
sb.Append("3D");
break;
case Dim.Cube:
sb.Append("Cube");
break;
}
if (IsMultisampled)
{
sb.Append("MS");
}
if (IsArray)
{
sb.Append("Array");
}
return sb;
}
public Type SampledType { get; }
public Dim Dim { get; }
public int Depth { get; }
public bool IsArray { get; }
public bool IsMultisampled { get; }
public int SampleCount { get; }
public ImageFormat Format { get; }
public AccessQualifier AccessQualifier { get; }
}
public class SamplerType : Type
{
public override string ToString()
{
return "sampler";
}
public override StringBuilder ToString(StringBuilder sb)
{
return sb.Append("sampler");
}
}
public class SampledImageType : Type
{
public SampledImageType (ImageType imageType)
{
ImageType = imageType;
}
public override string ToString()
{
return $"{ImageType}Sampled";
}
public override StringBuilder ToString(StringBuilder sb)
{
return ImageType.ToString(sb).Append("Sampled");
}
public ImageType ImageType { get; }
}
public class ArrayType : Type
{
public ArrayType (Type elementType, int elementCount)
{
ElementType = elementType;
ElementCount = elementCount;
}
public override string ToString()
{
return $"{ElementType}[{ElementCount}]";
}
public override StringBuilder ToString(StringBuilder sb)
{
return ElementType.ToString(sb).Append('[').Append(ElementCount).Append(']');
}
public int ElementCount { get; }
public Type ElementType { get; }
}
public class RuntimeArrayType : Type
{
public RuntimeArrayType(Type elementType)
{
ElementType = elementType;
}
public Type ElementType { get; }
}
public class StructType : Type
{
public StructType(IReadOnlyList<Type> memberTypes)
{
MemberTypes = memberTypes;
memberNames_ = new List<string>();
for (int i = 0; i < memberTypes.Count; ++i)
{
memberNames_.Add(string.Empty);
}
}
public void SetMemberName(uint member, string name)
{
memberNames_[(int)member] = name;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
ToString(sb);
return sb.ToString();
}
public override StringBuilder ToString(StringBuilder sb)
{
sb.Append("struct {");
for (int i = 0; i < MemberTypes.Count; ++i)
{
Type memberType = MemberTypes[i];
memberType.ToString(sb);
if (!string.IsNullOrEmpty(memberNames_[i]))
{
sb.Append(' ');
sb.Append(MemberNames[i]);
}
sb.Append(';');
if (i < (MemberTypes.Count - 1))
{
sb.Append(' ');
}
}
sb.Append('}');
return sb;
}
public IReadOnlyList<Type> MemberTypes { get; }
public IReadOnlyList<string> MemberNames => memberNames_;
private List<string> memberNames_;
}
public class OpaqueType : Type
{
}
public class PointerType : Type
{
public PointerType(StorageClass storageClass, Type type)
{
StorageClass = storageClass;
Type = type;
}
public PointerType(StorageClass storageClass)
{
StorageClass = storageClass;
}
public void ResolveForwardReference(Type t)
{
Type = t;
}
public override string ToString()
{
if (Type == null)
{
return $"{StorageClass} *";
}
else
{
return $"{StorageClass} {Type}*";
}
}
public override StringBuilder ToString(StringBuilder sb)
{
sb.Append(StorageClass.ToString()).Append(' ');
if (Type != null)
{
Type.ToString(sb);
}
sb.Append('*');
return sb;
}
public StorageClass StorageClass { get; }
public Type Type { get; private set; }
}
public class FunctionType : Type
{
public FunctionType(Type returnType, IReadOnlyList<Type> parameterTypes)
{
ReturnType = returnType;
ParameterTypes = parameterTypes;
}
public Type ReturnType { get; }
public IReadOnlyList<Type> ParameterTypes { get; }
}
public class EventType : Type
{
}
public class DeviceEventType : Type
{
}
public class ReserveIdType : Type
{
}
public class QueueType : Type
{
}
public class PipeType : Type
{
}
public class PipeStorage : Type
{
}
public class NamedBarrier : Type
{
}
}

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,103 @@
/* =================================================================================================== */
/* FMOD Studio - Error string header file. Copyright (c), Firelight Technologies Pty, Ltd. 2004-2016. */
/* */
/* Use this header if you want to store or display a string version / english explanation of */
/* the FMOD error codes. */
/* */
/* =================================================================================================== */
namespace FMOD
{
public class Error
{
public static string String(FMOD.RESULT errcode)
{
switch (errcode)
{
case FMOD.RESULT.OK: return "No errors.";
case FMOD.RESULT.ERR_BADCOMMAND: return "Tried to call a function on a data type that does not allow this type of functionality (ie calling Sound::lock on a streaming sound).";
case FMOD.RESULT.ERR_CHANNEL_ALLOC: return "Error trying to allocate a channel.";
case FMOD.RESULT.ERR_CHANNEL_STOLEN: return "The specified channel has been reused to play another sound.";
case FMOD.RESULT.ERR_DMA: return "DMA Failure. See debug output for more information.";
case FMOD.RESULT.ERR_DSP_CONNECTION: return "DSP connection error. Connection possibly caused a cyclic dependency or connected dsps with incompatible buffer counts.";
case FMOD.RESULT.ERR_DSP_DONTPROCESS: return "DSP return code from a DSP process query callback. Tells mixer not to call the process callback and therefore not consume CPU. Use this to optimize the DSP graph.";
case FMOD.RESULT.ERR_DSP_FORMAT: return "DSP Format error. A DSP unit may have attempted to connect to this network with the wrong format, or a matrix may have been set with the wrong size if the target unit has a specified channel map.";
case FMOD.RESULT.ERR_DSP_INUSE: return "DSP is already in the mixer's DSP network. It must be removed before being reinserted or released.";
case FMOD.RESULT.ERR_DSP_NOTFOUND: return "DSP connection error. Couldn't find the DSP unit specified.";
case FMOD.RESULT.ERR_DSP_RESERVED: return "DSP operation error. Cannot perform operation on this DSP as it is reserved by the system.";
case FMOD.RESULT.ERR_DSP_SILENCE: return "DSP return code from a DSP process query callback. Tells mixer silence would be produced from read, so go idle and not consume CPU. Use this to optimize the DSP graph.";
case FMOD.RESULT.ERR_DSP_TYPE: return "DSP operation cannot be performed on a DSP of this type.";
case FMOD.RESULT.ERR_FILE_BAD: return "Error loading file.";
case FMOD.RESULT.ERR_FILE_COULDNOTSEEK: return "Couldn't perform seek operation. This is a limitation of the medium (ie netstreams) or the file format.";
case FMOD.RESULT.ERR_FILE_DISKEJECTED: return "Media was ejected while reading.";
case FMOD.RESULT.ERR_FILE_EOF: return "End of file unexpectedly reached while trying to read essential data (truncated?).";
case FMOD.RESULT.ERR_FILE_ENDOFDATA: return "End of current chunk reached while trying to read data.";
case FMOD.RESULT.ERR_FILE_NOTFOUND: return "File not found.";
case FMOD.RESULT.ERR_FORMAT: return "Unsupported file or audio format.";
case FMOD.RESULT.ERR_HEADER_MISMATCH: return "There is a version mismatch between the FMOD header and either the FMOD Studio library or the FMOD Low Level library.";
case FMOD.RESULT.ERR_HTTP: return "A HTTP error occurred. This is a catch-all for HTTP errors not listed elsewhere.";
case FMOD.RESULT.ERR_HTTP_ACCESS: return "The specified resource requires authentication or is forbidden.";
case FMOD.RESULT.ERR_HTTP_PROXY_AUTH: return "Proxy authentication is required to access the specified resource.";
case FMOD.RESULT.ERR_HTTP_SERVER_ERROR: return "A HTTP server error occurred.";
case FMOD.RESULT.ERR_HTTP_TIMEOUT: return "The HTTP request timed out.";
case FMOD.RESULT.ERR_INITIALIZATION: return "FMOD was not initialized correctly to support this function.";
case FMOD.RESULT.ERR_INITIALIZED: return "Cannot call this command after System::init.";
case FMOD.RESULT.ERR_INTERNAL: return "An error occurred that wasn't supposed to. Contact support.";
case FMOD.RESULT.ERR_INVALID_FLOAT: return "Value passed in was a NaN, Inf or denormalized float.";
case FMOD.RESULT.ERR_INVALID_HANDLE: return "An invalid object handle was used.";
case FMOD.RESULT.ERR_INVALID_PARAM: return "An invalid parameter was passed to this function.";
case FMOD.RESULT.ERR_INVALID_POSITION: return "An invalid seek position was passed to this function.";
case FMOD.RESULT.ERR_INVALID_SPEAKER: return "An invalid speaker was passed to this function based on the current speaker mode.";
case FMOD.RESULT.ERR_INVALID_SYNCPOINT: return "The syncpoint did not come from this sound handle.";
case FMOD.RESULT.ERR_INVALID_THREAD: return "Tried to call a function on a thread that is not supported.";
case FMOD.RESULT.ERR_INVALID_VECTOR: return "The vectors passed in are not unit length, or perpendicular.";
case FMOD.RESULT.ERR_MAXAUDIBLE: return "Reached maximum audible playback count for this sound's soundgroup.";
case FMOD.RESULT.ERR_MEMORY: return "Not enough memory or resources.";
case FMOD.RESULT.ERR_MEMORY_CANTPOINT: return "Can't use FMOD_OPENMEMORY_POINT on non PCM source data, or non mp3/xma/adpcm data if FMOD_CREATECOMPRESSEDSAMPLE was used.";
case FMOD.RESULT.ERR_NEEDS3D: return "Tried to call a command on a 2d sound when the command was meant for 3d sound.";
case FMOD.RESULT.ERR_NEEDSHARDWARE: return "Tried to use a feature that requires hardware support.";
case FMOD.RESULT.ERR_NET_CONNECT: return "Couldn't connect to the specified host.";
case FMOD.RESULT.ERR_NET_SOCKET_ERROR: return "A socket error occurred. This is a catch-all for socket-related errors not listed elsewhere.";
case FMOD.RESULT.ERR_NET_URL: return "The specified URL couldn't be resolved.";
case FMOD.RESULT.ERR_NET_WOULD_BLOCK: return "Operation on a non-blocking socket could not complete immediately.";
case FMOD.RESULT.ERR_NOTREADY: return "Operation could not be performed because specified sound/DSP connection is not ready.";
case FMOD.RESULT.ERR_OUTPUT_ALLOCATED: return "Error initializing output device, but more specifically, the output device is already in use and cannot be reused.";
case FMOD.RESULT.ERR_OUTPUT_CREATEBUFFER: return "Error creating hardware sound buffer.";
case FMOD.RESULT.ERR_OUTPUT_DRIVERCALL: return "A call to a standard soundcard driver failed, which could possibly mean a bug in the driver or resources were missing or exhausted.";
case FMOD.RESULT.ERR_OUTPUT_FORMAT: return "Soundcard does not support the specified format.";
case FMOD.RESULT.ERR_OUTPUT_INIT: return "Error initializing output device.";
case FMOD.RESULT.ERR_OUTPUT_NODRIVERS: return "The output device has no drivers installed. If pre-init, FMOD_OUTPUT_NOSOUND is selected as the output mode. If post-init, the function just fails.";
case FMOD.RESULT.ERR_PLUGIN: return "An unspecified error has been returned from a plugin.";
case FMOD.RESULT.ERR_PLUGIN_MISSING: return "A requested output, dsp unit type or codec was not available.";
case FMOD.RESULT.ERR_PLUGIN_RESOURCE: return "A resource that the plugin requires cannot be found. (ie the DLS file for MIDI playback)";
case FMOD.RESULT.ERR_PLUGIN_VERSION: return "A plugin was built with an unsupported SDK version.";
case FMOD.RESULT.ERR_RECORD: return "An error occurred trying to initialize the recording device.";
case FMOD.RESULT.ERR_REVERB_CHANNELGROUP: return "Reverb properties cannot be set on this channel because a parent channelgroup owns the reverb connection.";
case FMOD.RESULT.ERR_REVERB_INSTANCE: return "Specified instance in FMOD_REVERB_PROPERTIES couldn't be set. Most likely because it is an invalid instance number or the reverb doesn't exist.";
case FMOD.RESULT.ERR_SUBSOUNDS: return "The error occurred because the sound referenced contains subsounds when it shouldn't have, or it doesn't contain subsounds when it should have. The operation may also not be able to be performed on a parent sound.";
case FMOD.RESULT.ERR_SUBSOUND_ALLOCATED: return "This subsound is already being used by another sound, you cannot have more than one parent to a sound. Null out the other parent's entry first.";
case FMOD.RESULT.ERR_SUBSOUND_CANTMOVE: return "Shared subsounds cannot be replaced or moved from their parent stream, such as when the parent stream is an FSB file.";
case FMOD.RESULT.ERR_TAGNOTFOUND: return "The specified tag could not be found or there are no tags.";
case FMOD.RESULT.ERR_TOOMANYCHANNELS: return "The sound created exceeds the allowable input channel count. This can be increased using the 'maxinputchannels' parameter in System::setSoftwareFormat.";
case FMOD.RESULT.ERR_TRUNCATED: return "The retrieved string is too long to fit in the supplied buffer and has been truncated.";
case FMOD.RESULT.ERR_UNIMPLEMENTED: return "Something in FMOD hasn't been implemented when it should be! contact support!";
case FMOD.RESULT.ERR_UNINITIALIZED: return "This command failed because System::init or System::setDriver was not called.";
case FMOD.RESULT.ERR_UNSUPPORTED: return "A command issued was not supported by this object. Possibly a plugin without certain callbacks specified.";
case FMOD.RESULT.ERR_VERSION: return "The version number of this file format is not supported.";
case FMOD.RESULT.ERR_EVENT_ALREADY_LOADED: return "The specified bank has already been loaded.";
case FMOD.RESULT.ERR_EVENT_LIVEUPDATE_BUSY: return "The live update connection failed due to the game already being connected.";
case FMOD.RESULT.ERR_EVENT_LIVEUPDATE_MISMATCH: return "The live update connection failed due to the game data being out of sync with the tool.";
case FMOD.RESULT.ERR_EVENT_LIVEUPDATE_TIMEOUT: return "The live update connection timed out.";
case FMOD.RESULT.ERR_EVENT_NOTFOUND: return "The requested event, bus or vca could not be found.";
case FMOD.RESULT.ERR_STUDIO_UNINITIALIZED: return "The Studio::System object is not yet initialized.";
case FMOD.RESULT.ERR_STUDIO_NOT_LOADED: return "The specified resource is not loaded, so it can't be unloaded.";
case FMOD.RESULT.ERR_INVALID_STRING: return "An invalid string was passed to this function.";
case FMOD.RESULT.ERR_ALREADY_LOCKED: return "The specified resource is already locked.";
case FMOD.RESULT.ERR_NOT_LOCKED: return "The specified resource is not locked, so it can't be unlocked.";
case FMOD.RESULT.ERR_RECORD_DISCONNECTED: return "The specified recording driver has been disconnected.";
case FMOD.RESULT.ERR_TOOMANYSAMPLES: return "The length provided exceed the allowable limit.";
default: return "Unknown error.";
}
}
}
}

View File

@@ -0,0 +1,55 @@
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Tga;
using SixLabors.ImageSharp.PixelFormats;
using System.IO;
using System.Runtime.InteropServices;
namespace AssetStudio
{
public static class ImageExtensions
{
public static void WriteToStream(this Image image, Stream stream, ImageFormat imageFormat)
{
switch (imageFormat)
{
case ImageFormat.Jpeg:
image.SaveAsJpeg(stream);
break;
case ImageFormat.Png:
image.SaveAsPng(stream);
break;
case ImageFormat.Bmp:
image.Save(stream, new BmpEncoder
{
BitsPerPixel = BmpBitsPerPixel.Pixel32,
SupportTransparency = true
});
break;
case ImageFormat.Tga:
image.Save(stream, new TgaEncoder
{
BitsPerPixel = TgaBitsPerPixel.Pixel32,
Compression = TgaCompression.None
});
break;
}
}
public static MemoryStream ConvertToStream(this Image image, ImageFormat imageFormat)
{
var stream = new MemoryStream();
image.WriteToStream(stream, imageFormat);
return stream;
}
public static byte[] ConvertToBytes<TPixel>(this Image<TPixel> image) where TPixel : unmanaged, IPixel<TPixel>
{
if (image.TryGetSinglePixelSpan(out var pixelSpan))
{
return MemoryMarshal.AsBytes(pixelSpan).ToArray();
}
return null;
}
}
}

View File

@@ -0,0 +1,10 @@
namespace AssetStudio
{
public enum ImageFormat
{
Jpeg,
Png,
Bmp,
Tga
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
namespace AssetStudio
{
public static class ModelExporter
{
public static void ExportFbx(string path, IImported imported, bool eulerFilter, float filterPrecision,
bool allNodes, bool skins, bool animation, bool blendShape, bool castToBone, float boneSize, bool exportAllUvsAsDiffuseMaps, float scaleFactor, int versionIndex, bool isAscii)
{
Fbx.Exporter.Export(path, imported, eulerFilter, filterPrecision, allNodes, skins, animation, blendShape, castToBone, boneSize, exportAllUvsAsDiffuseMaps, scaleFactor, versionIndex, isAscii);
}
}
}

View File

@@ -0,0 +1,25 @@
using System.Collections.Generic;
namespace AssetStudio
{
public static class MonoBehaviourConverter
{
public static TypeTree ConvertToTypeTree(this MonoBehaviour m_MonoBehaviour, AssemblyLoader assemblyLoader)
{
var m_Type = new TypeTree();
m_Type.m_Nodes = new List<TypeTreeNode>();
var helper = new SerializedTypeHelper(m_MonoBehaviour.version);
helper.AddMonoBehaviour(m_Type.m_Nodes, 0);
if (m_MonoBehaviour.m_Script.TryGet(out var m_Script))
{
var typeDef = assemblyLoader.GetTypeDefinition(m_Script.m_AssemblyName, string.IsNullOrEmpty(m_Script.m_Namespace) ? m_Script.m_ClassName : $"{m_Script.m_Namespace}.{m_Script.m_ClassName}");
if (typeDef != null)
{
var typeDefinitionConverter = new TypeDefinitionConverter(typeDef, helper, 1);
m_Type.m_Nodes.AddRange(typeDefinitionConverter.ConvertToTypeTreeNodes());
}
}
return m_Type;
}
}
}

View File

@@ -0,0 +1,12 @@
using Mono.Cecil;
namespace AssetStudio
{
public class MyAssemblyResolver : DefaultAssemblyResolver
{
public void Register(AssemblyDefinition assembly)
{
RegisterAssembly(assembly);
}
}
}

View File

@@ -0,0 +1,281 @@
using System.Collections.Generic;
namespace AssetStudio
{
public class SerializedTypeHelper
{
private readonly int[] version;
public SerializedTypeHelper(int[] version)
{
this.version = version;
}
public void AddMonoBehaviour(List<TypeTreeNode> nodes, int indent)
{
nodes.Add(new TypeTreeNode("MonoBehaviour", "Base", indent, false));
AddPPtr(nodes, "GameObject", "m_GameObject", indent + 1);
nodes.Add(new TypeTreeNode("UInt8", "m_Enabled", indent + 1, true));
AddPPtr(nodes, "MonoScript", "m_Script", indent + 1);
AddString(nodes, "m_Name", indent + 1);
}
public void AddPPtr(List<TypeTreeNode> nodes, string type, string name, int indent)
{
nodes.Add(new TypeTreeNode($"PPtr<{type}>", name, indent, false));
nodes.Add(new TypeTreeNode("int", "m_FileID", indent + 1, false));
if (version[0] >= 5) //5.0 and up
{
nodes.Add(new TypeTreeNode("SInt64", "m_PathID", indent + 1, false));
}
else
{
nodes.Add(new TypeTreeNode("int", "m_PathID", indent + 1, false));
}
}
public void AddString(List<TypeTreeNode> nodes, string name, int indent)
{
nodes.Add(new TypeTreeNode("string", name, indent, false));
nodes.Add(new TypeTreeNode("Array", "Array", indent + 1, true));
nodes.Add(new TypeTreeNode("int", "size", indent + 2, false));
nodes.Add(new TypeTreeNode("char", "data", indent + 2, false));
}
public void AddArray(List<TypeTreeNode> nodes, int indent)
{
nodes.Add(new TypeTreeNode("Array", "Array", indent, false));
nodes.Add(new TypeTreeNode("int", "size", indent + 1, false));
}
public void AddAnimationCurve(List<TypeTreeNode> nodes, string name, int indent)
{
nodes.Add(new TypeTreeNode("AnimationCurve", name, indent, false));
nodes.Add(new TypeTreeNode("vector", "m_Curve", indent + 1, false));
AddArray(nodes, indent + 2); //TODO 2017 and up Array align but no effect
nodes.Add(new TypeTreeNode("Keyframe", "data", indent + 3, false));
nodes.Add(new TypeTreeNode("float", "time", indent + 4, false));
nodes.Add(new TypeTreeNode("float", "value", indent + 4, false));
nodes.Add(new TypeTreeNode("float", "inSlope", indent + 4, false));
nodes.Add(new TypeTreeNode("float", "outSlope", indent + 4, false));
if (version[0] >= 2018) //2018 and up
{
nodes.Add(new TypeTreeNode("int", "weightedMode", indent + 4, false));
nodes.Add(new TypeTreeNode("float", "inWeight", indent + 4, false));
nodes.Add(new TypeTreeNode("float", "outWeight", indent + 4, false));
}
nodes.Add(new TypeTreeNode("int", "m_PreInfinity", indent + 1, false));
nodes.Add(new TypeTreeNode("int", "m_PostInfinity", indent + 1, false));
if (version[0] > 5 || (version[0] == 5 && version[1] >= 3)) //5.3 and up
{
nodes.Add(new TypeTreeNode("int", "m_RotationOrder", indent + 1, false));
}
}
public void AddGradient(List<TypeTreeNode> nodes, string name, int indent)
{
nodes.Add(new TypeTreeNode("Gradient", name, indent, false));
if (version[0] > 5 || (version[0] == 5 && version[1] >= 6)) //5.6 and up
{
AddColorRGBA(nodes, "key0", indent + 1);
AddColorRGBA(nodes, "key1", indent + 1);
AddColorRGBA(nodes, "key2", indent + 1);
AddColorRGBA(nodes, "key3", indent + 1);
AddColorRGBA(nodes, "key4", indent + 1);
AddColorRGBA(nodes, "key5", indent + 1);
AddColorRGBA(nodes, "key6", indent + 1);
AddColorRGBA(nodes, "key7", indent + 1);
}
else
{
AddColor32(nodes, "key0", indent + 1);
AddColor32(nodes, "key1", indent + 1);
AddColor32(nodes, "key2", indent + 1);
AddColor32(nodes, "key3", indent + 1);
AddColor32(nodes, "key4", indent + 1);
AddColor32(nodes, "key5", indent + 1);
AddColor32(nodes, "key6", indent + 1);
AddColor32(nodes, "key7", indent + 1);
}
nodes.Add(new TypeTreeNode("UInt16", "ctime0", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt16", "ctime1", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt16", "ctime2", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt16", "ctime3", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt16", "ctime4", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt16", "ctime5", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt16", "ctime6", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt16", "ctime7", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt16", "atime0", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt16", "atime1", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt16", "atime2", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt16", "atime3", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt16", "atime4", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt16", "atime5", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt16", "atime6", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt16", "atime7", indent + 1, false));
if (version[0] > 5 || (version[0] == 5 && version[1] >= 5)) //5.5 and up
{
nodes.Add(new TypeTreeNode("int", "m_Mode", indent + 1, false));
}
nodes.Add(new TypeTreeNode("UInt8", "m_NumColorKeys", indent + 1, false));
nodes.Add(new TypeTreeNode("UInt8", "m_NumAlphaKeys", indent + 1, true));
}
public void AddGUIStyle(List<TypeTreeNode> nodes, string name, int indent)
{
nodes.Add(new TypeTreeNode("GUIStyle", name, indent, false));
AddString(nodes, "m_Name", indent + 1);
AddGUIStyleState(nodes, "m_Normal", indent + 1);
AddGUIStyleState(nodes, "m_Hover", indent + 1);
AddGUIStyleState(nodes, "m_Active", indent + 1);
AddGUIStyleState(nodes, "m_Focused", indent + 1);
AddGUIStyleState(nodes, "m_OnNormal", indent + 1);
AddGUIStyleState(nodes, "m_OnHover", indent + 1);
AddGUIStyleState(nodes, "m_OnActive", indent + 1);
AddGUIStyleState(nodes, "m_OnFocused", indent + 1);
AddRectOffset(nodes, "m_Border", indent + 1);
if (version[0] >= 4) //4 and up
{
AddRectOffset(nodes, "m_Margin", indent + 1);
AddRectOffset(nodes, "m_Padding", indent + 1);
}
else
{
AddRectOffset(nodes, "m_Padding", indent + 1);
AddRectOffset(nodes, "m_Margin", indent + 1);
}
AddRectOffset(nodes, "m_Overflow", indent + 1);
AddPPtr(nodes, "Font", "m_Font", indent + 1);
if (version[0] >= 4) //4 and up
{
nodes.Add(new TypeTreeNode("int", "m_FontSize", indent + 1, false));
nodes.Add(new TypeTreeNode("int", "m_FontStyle", indent + 1, false));
nodes.Add(new TypeTreeNode("int", "m_Alignment", indent + 1, false));
nodes.Add(new TypeTreeNode("bool", "m_WordWrap", indent + 1, false));
nodes.Add(new TypeTreeNode("bool", "m_RichText", indent + 1, true));
nodes.Add(new TypeTreeNode("int", "m_TextClipping", indent + 1, false));
nodes.Add(new TypeTreeNode("int", "m_ImagePosition", indent + 1, false));
AddVector2f(nodes, "m_ContentOffset", indent + 1);
nodes.Add(new TypeTreeNode("float", "m_FixedWidth", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "m_FixedHeight", indent + 1, false));
nodes.Add(new TypeTreeNode("bool", "m_StretchWidth", indent + 1, false));
nodes.Add(new TypeTreeNode("bool", "m_StretchHeight", indent + 1, true));
}
else
{
nodes.Add(new TypeTreeNode("int", "m_ImagePosition", indent + 1, false));
nodes.Add(new TypeTreeNode("int", "m_Alignment", indent + 1, false));
nodes.Add(new TypeTreeNode("bool", "m_WordWrap", indent + 1, true));
nodes.Add(new TypeTreeNode("int", "m_TextClipping", indent + 1, false));
AddVector2f(nodes, "m_ContentOffset", indent + 1);
AddVector2f(nodes, "m_ClipOffset", indent + 1);
nodes.Add(new TypeTreeNode("float", "m_FixedWidth", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "m_FixedHeight", indent + 1, false));
if (version[0] >= 3) //3 and up
{
nodes.Add(new TypeTreeNode("int", "m_FontSize", indent + 1, false));
nodes.Add(new TypeTreeNode("int", "m_FontStyle", indent + 1, false));
}
nodes.Add(new TypeTreeNode("bool", "m_StretchWidth", indent + 1, true));
nodes.Add(new TypeTreeNode("bool", "m_StretchHeight", indent + 1, true));
}
}
public void AddGUIStyleState(List<TypeTreeNode> nodes, string name, int indent)
{
nodes.Add(new TypeTreeNode("GUIStyleState", name, indent, false));
AddPPtr(nodes, "Texture2D", "m_Background", indent + 1);
AddColorRGBA(nodes, "m_TextColor", indent + 1);
}
public void AddVector2f(List<TypeTreeNode> nodes, string name, int indent)
{
nodes.Add(new TypeTreeNode("Vector2f", name, indent, false));
nodes.Add(new TypeTreeNode("float", "x", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "y", indent + 1, false));
}
public void AddRectOffset(List<TypeTreeNode> nodes, string name, int indent)
{
nodes.Add(new TypeTreeNode("RectOffset", name, indent, false));
nodes.Add(new TypeTreeNode("int", "m_Left", indent + 1, false));
nodes.Add(new TypeTreeNode("int", "m_Right", indent + 1, false));
nodes.Add(new TypeTreeNode("int", "m_Top", indent + 1, false));
nodes.Add(new TypeTreeNode("int", "m_Bottom", indent + 1, false));
}
public void AddColorRGBA(List<TypeTreeNode> nodes, string name, int indent)
{
nodes.Add(new TypeTreeNode("ColorRGBA", name, indent, false));
nodes.Add(new TypeTreeNode("float", "r", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "g", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "b", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "a", indent + 1, false));
}
public void AddColor32(List<TypeTreeNode> nodes, string name, int indent)
{
nodes.Add(new TypeTreeNode("ColorRGBA", name, indent, false));
nodes.Add(new TypeTreeNode("unsigned int", "rgba", indent + 1, false));
}
public void AddMatrix4x4(List<TypeTreeNode> nodes, string name, int indent)
{
nodes.Add(new TypeTreeNode("Matrix4x4f", name, indent, false));
nodes.Add(new TypeTreeNode("float", "e00", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "e01", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "e02", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "e03", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "e10", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "e11", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "e12", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "e13", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "e20", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "e21", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "e22", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "e23", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "e30", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "e31", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "e32", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "e33", indent + 1, false));
}
public void AddSphericalHarmonicsL2(List<TypeTreeNode> nodes, string name, int indent)
{
nodes.Add(new TypeTreeNode("SphericalHarmonicsL2", name, indent, false));
nodes.Add(new TypeTreeNode("float", "sh[ 0]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[ 1]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[ 2]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[ 3]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[ 4]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[ 5]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[ 6]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[ 7]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[ 8]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[ 9]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[10]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[11]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[12]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[13]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[14]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[15]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[16]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[17]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[18]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[19]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[20]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[21]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[22]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[23]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[24]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[25]", indent + 1, false));
nodes.Add(new TypeTreeNode("float", "sh[26]", indent + 1, false));
}
public void AddPropertyName(List<TypeTreeNode> nodes, string name, int indent)
{
nodes.Add(new TypeTreeNode("PropertyName", name, indent, false));
AddString(nodes, "id", indent + 1);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,365 @@
namespace Smolv
{
public struct OpData
{
public OpData(byte hasResult, byte hasType, sbyte deltaFromResult, byte varrest)
{
this.hasResult = hasResult;
this.hasType = hasType;
this.deltaFromResult = deltaFromResult;
this.varrest = varrest;
}
/// <summary>
/// Does it have result ID?
/// </summary>
public byte hasResult;
/// <summary>
/// Does it have type ID?
/// </summary>
public byte hasType;
/// <summary>
/// How many words after (optional) type+result to write out as deltas from result?
/// </summary>
public sbyte deltaFromResult;
/// <summary>
/// Should the rest of words be written in varint encoding?
/// </summary>
public byte varrest;
public static readonly OpData[] SpirvOpData =
{
new OpData(0, 0, 0, 0), // Nop
new OpData(1, 1, 0, 0), // Undef
new OpData(0, 0, 0, 0), // SourceContinued
new OpData(0, 0, 0, 1), // Source
new OpData(0, 0, 0, 0), // SourceExtension
new OpData(0, 0, 0, 0), // Name
new OpData(0, 0, 0, 0), // MemberName
new OpData(0, 0, 0, 0), // String
new OpData(0, 0, 0, 1), // Line
new OpData(1, 1, 0, 0), // #9
new OpData(0, 0, 0, 0), // Extension
new OpData(1, 0, 0, 0), // ExtInstImport
new OpData(1, 1, 0, 1), // ExtInst
new OpData(1, 1, 2, 1), // VectorShuffleCompact - new in SMOLV
new OpData(0, 0, 0, 1), // MemoryModel
new OpData(0, 0, 0, 1), // EntryPoint
new OpData(0, 0, 0, 1), // ExecutionMode
new OpData(0, 0, 0, 1), // Capability
new OpData(1, 1, 0, 0), // #18
new OpData(1, 0, 0, 1), // TypeVoid
new OpData(1, 0, 0, 1), // TypeBool
new OpData(1, 0, 0, 1), // TypeInt
new OpData(1, 0, 0, 1), // TypeFloat
new OpData(1, 0, 0, 1), // TypeVector
new OpData(1, 0, 0, 1), // TypeMatrix
new OpData(1, 0, 0, 1), // TypeImage
new OpData(1, 0, 0, 1), // TypeSampler
new OpData(1, 0, 0, 1), // TypeSampledImage
new OpData(1, 0, 0, 1), // TypeArray
new OpData(1, 0, 0, 1), // TypeRuntimeArray
new OpData(1, 0, 0, 1), // TypeStruct
new OpData(1, 0, 0, 1), // TypeOpaque
new OpData(1, 0, 0, 1), // TypePointer
new OpData(1, 0, 0, 1), // TypeFunction
new OpData(1, 0, 0, 1), // TypeEvent
new OpData(1, 0, 0, 1), // TypeDeviceEvent
new OpData(1, 0, 0, 1), // TypeReserveId
new OpData(1, 0, 0, 1), // TypeQueue
new OpData(1, 0, 0, 1), // TypePipe
new OpData(0, 0, 0, 1), // TypeForwardPointer
new OpData(1, 1, 0, 0), // #40
new OpData(1, 1, 0, 0), // ConstantTrue
new OpData(1, 1, 0, 0), // ConstantFalse
new OpData(1, 1, 0, 0), // Constant
new OpData(1, 1, 9, 0), // ConstantComposite
new OpData(1, 1, 0, 1), // ConstantSampler
new OpData(1, 1, 0, 0), // ConstantNull
new OpData(1, 1, 0, 0), // #47
new OpData(1, 1, 0, 0), // SpecConstantTrue
new OpData(1, 1, 0, 0), // SpecConstantFalse
new OpData(1, 1, 0, 0), // SpecConstant
new OpData(1, 1, 9, 0), // SpecConstantComposite
new OpData(1, 1, 0, 0), // SpecConstantOp
new OpData(1, 1, 0, 0), // #53
new OpData(1, 1, 0, 1), // Function
new OpData(1, 1, 0, 0), // FunctionParameter
new OpData(0, 0, 0, 0), // FunctionEnd
new OpData(1, 1, 9, 0), // FunctionCall
new OpData(1, 1, 0, 0), // #58
new OpData(1, 1, 0, 1), // Variable
new OpData(1, 1, 0, 0), // ImageTexelPointer
new OpData(1, 1, 1, 1), // Load
new OpData(0, 0, 2, 1), // Store
new OpData(0, 0, 0, 0), // CopyMemory
new OpData(0, 0, 0, 0), // CopyMemorySized
new OpData(1, 1, 0, 1), // AccessChain
new OpData(1, 1, 0, 0), // InBoundsAccessChain
new OpData(1, 1, 0, 0), // PtrAccessChain
new OpData(1, 1, 0, 0), // ArrayLength
new OpData(1, 1, 0, 0), // GenericPtrMemSemantics
new OpData(1, 1, 0, 0), // InBoundsPtrAccessChain
new OpData(0, 0, 0, 1), // Decorate
new OpData(0, 0, 0, 1), // MemberDecorate
new OpData(1, 0, 0, 0), // DecorationGroup
new OpData(0, 0, 0, 0), // GroupDecorate
new OpData(0, 0, 0, 0), // GroupMemberDecorate
new OpData(1, 1, 0, 0), // #76
new OpData(1, 1, 1, 1), // VectorExtractDynamic
new OpData(1, 1, 2, 1), // VectorInsertDynamic
new OpData(1, 1, 2, 1), // VectorShuffle
new OpData(1, 1, 9, 0), // CompositeConstruct
new OpData(1, 1, 1, 1), // CompositeExtract
new OpData(1, 1, 2, 1), // CompositeInsert
new OpData(1, 1, 1, 0), // CopyObject
new OpData(1, 1, 0, 0), // Transpose
new OpData(1, 1, 0, 0), // #85
new OpData(1, 1, 0, 0), // SampledImage
new OpData(1, 1, 2, 1), // ImageSampleImplicitLod
new OpData(1, 1, 2, 1), // ImageSampleExplicitLod
new OpData(1, 1, 3, 1), // ImageSampleDrefImplicitLod
new OpData(1, 1, 3, 1), // ImageSampleDrefExplicitLod
new OpData(1, 1, 2, 1), // ImageSampleProjImplicitLod
new OpData(1, 1, 2, 1), // ImageSampleProjExplicitLod
new OpData(1, 1, 3, 1), // ImageSampleProjDrefImplicitLod
new OpData(1, 1, 3, 1), // ImageSampleProjDrefExplicitLod
new OpData(1, 1, 2, 1), // ImageFetch
new OpData(1, 1, 3, 1), // ImageGather
new OpData(1, 1, 3, 1), // ImageDrefGather
new OpData(1, 1, 2, 1), // ImageRead
new OpData(0, 0, 3, 1), // ImageWrite
new OpData(1, 1, 1, 0), // Image
new OpData(1, 1, 1, 0), // ImageQueryFormat
new OpData(1, 1, 1, 0), // ImageQueryOrder
new OpData(1, 1, 2, 0), // ImageQuerySizeLod
new OpData(1, 1, 1, 0), // ImageQuerySize
new OpData(1, 1, 2, 0), // ImageQueryLod
new OpData(1, 1, 1, 0), // ImageQueryLevels
new OpData(1, 1, 1, 0), // ImageQuerySamples
new OpData(1, 1, 0, 0), // #108
new OpData(1, 1, 1, 0), // ConvertFToU
new OpData(1, 1, 1, 0), // ConvertFToS
new OpData(1, 1, 1, 0), // ConvertSToF
new OpData(1, 1, 1, 0), // ConvertUToF
new OpData(1, 1, 1, 0), // UConvert
new OpData(1, 1, 1, 0), // SConvert
new OpData(1, 1, 1, 0), // FConvert
new OpData(1, 1, 1, 0), // QuantizeToF16
new OpData(1, 1, 1, 0), // ConvertPtrToU
new OpData(1, 1, 1, 0), // SatConvertSToU
new OpData(1, 1, 1, 0), // SatConvertUToS
new OpData(1, 1, 1, 0), // ConvertUToPtr
new OpData(1, 1, 1, 0), // PtrCastToGeneric
new OpData(1, 1, 1, 0), // GenericCastToPtr
new OpData(1, 1, 1, 1), // GenericCastToPtrExplicit
new OpData(1, 1, 1, 0), // Bitcast
new OpData(1, 1, 0, 0), // #125
new OpData(1, 1, 1, 0), // SNegate
new OpData(1, 1, 1, 0), // FNegate
new OpData(1, 1, 2, 0), // IAdd
new OpData(1, 1, 2, 0), // FAdd
new OpData(1, 1, 2, 0), // ISub
new OpData(1, 1, 2, 0), // FSub
new OpData(1, 1, 2, 0), // IMul
new OpData(1, 1, 2, 0), // FMul
new OpData(1, 1, 2, 0), // UDiv
new OpData(1, 1, 2, 0), // SDiv
new OpData(1, 1, 2, 0), // FDiv
new OpData(1, 1, 2, 0), // UMod
new OpData(1, 1, 2, 0), // SRem
new OpData(1, 1, 2, 0), // SMod
new OpData(1, 1, 2, 0), // FRem
new OpData(1, 1, 2, 0), // FMod
new OpData(1, 1, 2, 0), // VectorTimesScalar
new OpData(1, 1, 2, 0), // MatrixTimesScalar
new OpData(1, 1, 2, 0), // VectorTimesMatrix
new OpData(1, 1, 2, 0), // MatrixTimesVector
new OpData(1, 1, 2, 0), // MatrixTimesMatrix
new OpData(1, 1, 2, 0), // OuterProduct
new OpData(1, 1, 2, 0), // Dot
new OpData(1, 1, 2, 0), // IAddCarry
new OpData(1, 1, 2, 0), // ISubBorrow
new OpData(1, 1, 2, 0), // UMulExtended
new OpData(1, 1, 2, 0), // SMulExtended
new OpData(1, 1, 0, 0), // #153
new OpData(1, 1, 1, 0), // Any
new OpData(1, 1, 1, 0), // All
new OpData(1, 1, 1, 0), // IsNan
new OpData(1, 1, 1, 0), // IsInf
new OpData(1, 1, 1, 0), // IsFinite
new OpData(1, 1, 1, 0), // IsNormal
new OpData(1, 1, 1, 0), // SignBitSet
new OpData(1, 1, 2, 0), // LessOrGreater
new OpData(1, 1, 2, 0), // Ordered
new OpData(1, 1, 2, 0), // Unordered
new OpData(1, 1, 2, 0), // LogicalEqual
new OpData(1, 1, 2, 0), // LogicalNotEqual
new OpData(1, 1, 2, 0), // LogicalOr
new OpData(1, 1, 2, 0), // LogicalAnd
new OpData(1, 1, 1, 0), // LogicalNot
new OpData(1, 1, 3, 0), // Select
new OpData(1, 1, 2, 0), // IEqual
new OpData(1, 1, 2, 0), // INotEqual
new OpData(1, 1, 2, 0), // UGreaterThan
new OpData(1, 1, 2, 0), // SGreaterThan
new OpData(1, 1, 2, 0), // UGreaterThanEqual
new OpData(1, 1, 2, 0), // SGreaterThanEqual
new OpData(1, 1, 2, 0), // ULessThan
new OpData(1, 1, 2, 0), // SLessThan
new OpData(1, 1, 2, 0), // ULessThanEqual
new OpData(1, 1, 2, 0), // SLessThanEqual
new OpData(1, 1, 2, 0), // FOrdEqual
new OpData(1, 1, 2, 0), // FUnordEqual
new OpData(1, 1, 2, 0), // FOrdNotEqual
new OpData(1, 1, 2, 0), // FUnordNotEqual
new OpData(1, 1, 2, 0), // FOrdLessThan
new OpData(1, 1, 2, 0), // FUnordLessThan
new OpData(1, 1, 2, 0), // FOrdGreaterThan
new OpData(1, 1, 2, 0), // FUnordGreaterThan
new OpData(1, 1, 2, 0), // FOrdLessThanEqual
new OpData(1, 1, 2, 0), // FUnordLessThanEqual
new OpData(1, 1, 2, 0), // FOrdGreaterThanEqual
new OpData(1, 1, 2, 0), // FUnordGreaterThanEqual
new OpData(1, 1, 0, 0), // #192
new OpData(1, 1, 0, 0), // #193
new OpData(1, 1, 2, 0), // ShiftRightLogical
new OpData(1, 1, 2, 0), // ShiftRightArithmetic
new OpData(1, 1, 2, 0), // ShiftLeftLogical
new OpData(1, 1, 2, 0), // BitwiseOr
new OpData(1, 1, 2, 0), // BitwiseXor
new OpData(1, 1, 2, 0), // BitwiseAnd
new OpData(1, 1, 1, 0), // Not
new OpData(1, 1, 4, 0), // BitFieldInsert
new OpData(1, 1, 3, 0), // BitFieldSExtract
new OpData(1, 1, 3, 0), // BitFieldUExtract
new OpData(1, 1, 1, 0), // BitReverse
new OpData(1, 1, 1, 0), // BitCount
new OpData(1, 1, 0, 0), // #206
new OpData(1, 1, 0, 0), // DPdx
new OpData(1, 1, 0, 0), // DPdy
new OpData(1, 1, 0, 0), // Fwidth
new OpData(1, 1, 0, 0), // DPdxFine
new OpData(1, 1, 0, 0), // DPdyFine
new OpData(1, 1, 0, 0), // FwidthFine
new OpData(1, 1, 0, 0), // DPdxCoarse
new OpData(1, 1, 0, 0), // DPdyCoarse
new OpData(1, 1, 0, 0), // FwidthCoarse
new OpData(1, 1, 0, 0), // #216
new OpData(1, 1, 0, 0), // #217
new OpData(0, 0, 0, 0), // EmitVertex
new OpData(0, 0, 0, 0), // EndPrimitive
new OpData(0, 0, 0, 0), // EmitStreamVertex
new OpData(0, 0, 0, 0), // EndStreamPrimitive
new OpData(1, 1, 0, 0), // #222
new OpData(1, 1, 0, 0), // #223
new OpData(0, 0, -3, 0), // ControlBarrier
new OpData(0, 0, -2, 0), // MemoryBarrier
new OpData(1, 1, 0, 0), // #226
new OpData(1, 1, 0, 0), // AtomicLoad
new OpData(0, 0, 0, 0), // AtomicStore
new OpData(1, 1, 0, 0), // AtomicExchange
new OpData(1, 1, 0, 0), // AtomicCompareExchange
new OpData(1, 1, 0, 0), // AtomicCompareExchangeWeak
new OpData(1, 1, 0, 0), // AtomicIIncrement
new OpData(1, 1, 0, 0), // AtomicIDecrement
new OpData(1, 1, 0, 0), // AtomicIAdd
new OpData(1, 1, 0, 0), // AtomicISub
new OpData(1, 1, 0, 0), // AtomicSMin
new OpData(1, 1, 0, 0), // AtomicUMin
new OpData(1, 1, 0, 0), // AtomicSMax
new OpData(1, 1, 0, 0), // AtomicUMax
new OpData(1, 1, 0, 0), // AtomicAnd
new OpData(1, 1, 0, 0), // AtomicOr
new OpData(1, 1, 0, 0), // AtomicXor
new OpData(1, 1, 0, 0), // #243
new OpData(1, 1, 0, 0), // #244
new OpData(1, 1, 0, 0), // Phi
new OpData(0, 0, -2, 1), // LoopMerge
new OpData(0, 0, -1, 1), // SelectionMerge
new OpData(1, 0, 0, 0), // Label
new OpData(0, 0, -1, 0), // Branch
new OpData(0, 0, -3, 1), // BranchConditional
new OpData(0, 0, 0, 0), // Switch
new OpData(0, 0, 0, 0), // Kill
new OpData(0, 0, 0, 0), // Return
new OpData(0, 0, 0, 0), // ReturnValue
new OpData(0, 0, 0, 0), // Unreachable
new OpData(0, 0, 0, 0), // LifetimeStart
new OpData(0, 0, 0, 0), // LifetimeStop
new OpData(1, 1, 0, 0), // #258
new OpData(1, 1, 0, 0), // GroupAsyncCopy
new OpData(0, 0, 0, 0), // GroupWaitEvents
new OpData(1, 1, 0, 0), // GroupAll
new OpData(1, 1, 0, 0), // GroupAny
new OpData(1, 1, 0, 0), // GroupBroadcast
new OpData(1, 1, 0, 0), // GroupIAdd
new OpData(1, 1, 0, 0), // GroupFAdd
new OpData(1, 1, 0, 0), // GroupFMin
new OpData(1, 1, 0, 0), // GroupUMin
new OpData(1, 1, 0, 0), // GroupSMin
new OpData(1, 1, 0, 0), // GroupFMax
new OpData(1, 1, 0, 0), // GroupUMax
new OpData(1, 1, 0, 0), // GroupSMax
new OpData(1, 1, 0, 0), // #272
new OpData(1, 1, 0, 0), // #273
new OpData(1, 1, 0, 0), // ReadPipe
new OpData(1, 1, 0, 0), // WritePipe
new OpData(1, 1, 0, 0), // ReservedReadPipe
new OpData(1, 1, 0, 0), // ReservedWritePipe
new OpData(1, 1, 0, 0), // ReserveReadPipePackets
new OpData(1, 1, 0, 0), // ReserveWritePipePackets
new OpData(0, 0, 0, 0), // CommitReadPipe
new OpData(0, 0, 0, 0), // CommitWritePipe
new OpData(1, 1, 0, 0), // IsValidReserveId
new OpData(1, 1, 0, 0), // GetNumPipePackets
new OpData(1, 1, 0, 0), // GetMaxPipePackets
new OpData(1, 1, 0, 0), // GroupReserveReadPipePackets
new OpData(1, 1, 0, 0), // GroupReserveWritePipePackets
new OpData(0, 0, 0, 0), // GroupCommitReadPipe
new OpData(0, 0, 0, 0), // GroupCommitWritePipe
new OpData(1, 1, 0, 0), // #289
new OpData(1, 1, 0, 0), // #290
new OpData(1, 1, 0, 0), // EnqueueMarker
new OpData(1, 1, 0, 0), // EnqueueKernel
new OpData(1, 1, 0, 0), // GetKernelNDrangeSubGroupCount
new OpData(1, 1, 0, 0), // GetKernelNDrangeMaxSubGroupSize
new OpData(1, 1, 0, 0), // GetKernelWorkGroupSize
new OpData(1, 1, 0, 0), // GetKernelPreferredWorkGroupSizeMultiple
new OpData(0, 0, 0, 0), // RetainEvent
new OpData(0, 0, 0, 0), // ReleaseEvent
new OpData(1, 1, 0, 0), // CreateUserEvent
new OpData(1, 1, 0, 0), // IsValidEvent
new OpData(0, 0, 0, 0), // SetUserEventStatus
new OpData(0, 0, 0, 0), // CaptureEventProfilingInfo
new OpData(1, 1, 0, 0), // GetDefaultQueue
new OpData(1, 1, 0, 0), // BuildNDRange
new OpData(1, 1, 2, 1), // ImageSparseSampleImplicitLod
new OpData(1, 1, 2, 1), // ImageSparseSampleExplicitLod
new OpData(1, 1, 3, 1), // ImageSparseSampleDrefImplicitLod
new OpData(1, 1, 3, 1), // ImageSparseSampleDrefExplicitLod
new OpData(1, 1, 2, 1), // ImageSparseSampleProjImplicitLod
new OpData(1, 1, 2, 1), // ImageSparseSampleProjExplicitLod
new OpData(1, 1, 3, 1), // ImageSparseSampleProjDrefImplicitLod
new OpData(1, 1, 3, 1), // ImageSparseSampleProjDrefExplicitLod
new OpData(1, 1, 2, 1), // ImageSparseFetch
new OpData(1, 1, 3, 1), // ImageSparseGather
new OpData(1, 1, 3, 1), // ImageSparseDrefGather
new OpData(1, 1, 1, 0), // ImageSparseTexelsResident
new OpData(0, 0, 0, 0), // NoLine
new OpData(1, 1, 0, 0), // AtomicFlagTestAndSet
new OpData(0, 0, 0, 0), // AtomicFlagClear
new OpData(1, 1, 0, 0), // ImageSparseRead
new OpData(1, 1, 0, 0), // SizeOf
new OpData(1, 1, 0, 0), // TypePipeStorage
new OpData(1, 1, 0, 0), // ConstantPipeStorage
new OpData(1, 1, 0, 0), // CreatePipeFromPipeStorage
new OpData(1, 1, 0, 0), // GetKernelLocalSizeForSubgroupCount
new OpData(1, 1, 0, 0), // GetKernelMaxNumSubgroups
new OpData(1, 1, 0, 0), // TypeNamedBarrier
new OpData(1, 1, 0, 1), // NamedBarrierInitialize
new OpData(0, 0, -2, 1), // MemoryNamedBarrier
new OpData(1, 1, 0, 0), // ModuleProcessed
};
};
}

View File

@@ -0,0 +1,479 @@
using System;
using System.IO;
using System.Text;
namespace Smolv
{
public static class SmolvDecoder
{
public static int GetDecodedBufferSize(byte[] data)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
if (!CheckSmolHeader(data))
{
return 0;
}
int size = BitConverter.ToInt32(data, 5 * sizeof(uint));
return size;
}
public static int GetDecodedBufferSize(Stream stream)
{
if (stream == null)
{
throw new ArgumentNullException(nameof(stream));
}
if (!stream.CanSeek)
{
throw new ArgumentException(nameof(stream));
}
if (stream.Position + HeaderSize > stream.Length)
{
return 0;
}
long initPosition = stream.Position;
stream.Position += HeaderSize - sizeof(uint);
int size = stream.ReadByte() | stream.ReadByte() << 8 | stream.ReadByte() << 16 | stream.ReadByte() << 24;
stream.Position = initPosition;
return size;
}
public static byte[] Decode(byte[] data)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
int bufferSize = GetDecodedBufferSize(data);
if (bufferSize == 0)
{
// invalid SMOL-V
return null;
}
byte[] output = new byte[bufferSize];
if (Decode(data, output))
{
return output;
}
return null;
}
public static bool Decode(byte[] data, byte[] output)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
if (output == null)
{
throw new ArgumentNullException(nameof(output));
}
int bufferSize = GetDecodedBufferSize(data);
if (bufferSize > output.Length)
{
return false;
}
using (MemoryStream outputStream = new MemoryStream(output))
{
return Decode(data, outputStream);
}
}
public static bool Decode(byte[] data, Stream outputStream)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
using (MemoryStream inputStream = new MemoryStream(data))
{
return Decode(inputStream, data.Length, outputStream);
}
}
public static bool Decode(Stream inputStream, int inputSize, Stream outputStream)
{
if (inputStream == null)
{
throw new ArgumentNullException(nameof(inputStream));
}
if (outputStream == null)
{
throw new ArgumentNullException(nameof(outputStream));
}
if (inputStream.Length < HeaderSize)
{
return false;
}
using (BinaryReader input = new BinaryReader(inputStream, Encoding.UTF8, true))
{
using (BinaryWriter output = new BinaryWriter(outputStream, Encoding.UTF8, true))
{
long inputEndPosition = input.BaseStream.Position + inputSize;
long outputStartPosition = output.BaseStream.Position;
// Header
output.Write(SpirVHeaderMagic);
input.BaseStream.Position += sizeof(uint);
uint version = input.ReadUInt32();
output.Write(version);
uint generator = input.ReadUInt32();
output.Write(generator);
int bound = input.ReadInt32();
output.Write(bound);
uint schema = input.ReadUInt32();
output.Write(schema);
int decodedSize = input.ReadInt32();
// Body
int prevResult = 0;
int prevDecorate = 0;
while (input.BaseStream.Position < inputEndPosition)
{
// read length + opcode
if (!ReadLengthOp(input, out uint instrLen, out SpvOp op))
{
return false;
}
bool wasSwizzle = op == SpvOp.VectorShuffleCompact;
if (wasSwizzle)
{
op = SpvOp.VectorShuffle;
}
output.Write((instrLen << 16) | (uint)op);
uint ioffs = 1;
// read type as varint, if we have it
if (op.OpHasType())
{
if (!ReadVarint(input, out uint value))
{
return false;
}
output.Write(value);
ioffs++;
}
// read result as delta+varint, if we have it
if (op.OpHasResult())
{
if (!ReadVarint(input, out uint value))
{
return false;
}
int zds = prevResult + ZigDecode(value);
output.Write(zds);
prevResult = zds;
ioffs++;
}
// Decorate: IDs relative to previous decorate
if (op == SpvOp.Decorate || op == SpvOp.MemberDecorate)
{
if (!ReadVarint(input, out uint value))
{
return false;
}
int zds = prevDecorate + unchecked((int)value);
output.Write(zds);
prevDecorate = zds;
ioffs++;
}
// Read this many IDs, that are relative to result ID
int relativeCount = op.OpDeltaFromResult();
bool inverted = false;
if (relativeCount < 0)
{
inverted = true;
relativeCount = -relativeCount;
}
for (int i = 0; i < relativeCount && ioffs < instrLen; ++i, ++ioffs)
{
if (!ReadVarint(input, out uint value))
{
return false;
}
int zd = inverted ? ZigDecode(value) : unchecked((int)value);
output.Write(prevResult - zd);
}
if (wasSwizzle && instrLen <= 9)
{
uint swizzle = input.ReadByte();
if (instrLen > 5) output.Write(swizzle >> 6);
if (instrLen > 6) output.Write((swizzle >> 4) & 3);
if (instrLen > 7) output.Write((swizzle >> 2) & 3);
if (instrLen > 8) output.Write(swizzle & 3);
}
else if (op.OpVarRest())
{
// read rest of words with variable encoding
for (; ioffs < instrLen; ++ioffs)
{
if (!ReadVarint(input, out uint value))
{
return false;
}
output.Write(value);
}
}
else
{
// read rest of words without any encoding
for (; ioffs < instrLen; ++ioffs)
{
if (input.BaseStream.Position + 4 > input.BaseStream.Length)
{
return false;
}
uint val = input.ReadUInt32();
output.Write(val);
}
}
}
if (output.BaseStream.Position != outputStartPosition + decodedSize)
{
// something went wrong during decoding? we should have decoded to exact output size
return false;
}
return true;
}
}
}
private static bool CheckSmolHeader(byte[] data)
{
if (!CheckGenericHeader(data, SmolHeaderMagic))
{
return false;
}
return true;
}
private static bool CheckGenericHeader(byte[] data, uint expectedMagic)
{
if (data == null)
{
return false;
}
if (data.Length < HeaderSize)
{
return false;
}
uint headerMagic = BitConverter.ToUInt32(data, 0 * sizeof(uint));
if (headerMagic != expectedMagic)
{
return false;
}
uint headerVersion = BitConverter.ToUInt32(data, 1 * sizeof(uint));
if (headerVersion < 0x00010000 || headerVersion > 0x00010300)
{
// only support 1.0 through 1.3
return false;
}
return true;
}
private static bool ReadVarint(BinaryReader input, out uint value)
{
uint v = 0;
int shift = 0;
while (input.BaseStream.Position < input.BaseStream.Length)
{
byte b = input.ReadByte();
v |= unchecked((uint)(b & 127) << shift);
shift += 7;
if ((b & 128) == 0)
{
break;
}
}
value = v;
// @TODO: report failures
return true;
}
private static bool ReadLengthOp(BinaryReader input, out uint len, out SpvOp op)
{
len = default;
op = default;
if (!ReadVarint(input, out uint value))
{
return false;
}
len = ((value >> 20) << 4) | ((value >> 4) & 0xF);
op = (SpvOp) (((value >> 4) & 0xFFF0) | (value & 0xF));
op = RemapOp(op);
len = DecodeLen(op, len);
return true;
}
/// <summary>
/// Remap most common Op codes (Load, Store, Decorate, VectorShuffle etc.) to be in &lt; 16 range, for
/// more compact varint encoding. This basically swaps rarely used op values that are &lt; 16 with the
/// ones that are common.
/// </summary>
private static SpvOp RemapOp(SpvOp op)
{
switch (op)
{
// 0: 24%
case SpvOp.Decorate:
return SpvOp.Nop;
case SpvOp.Nop:
return SpvOp.Decorate;
// 1: 17%
case SpvOp.Load:
return SpvOp.Undef;
case SpvOp.Undef:
return SpvOp.Load;
// 2: 9%
case SpvOp.Store:
return SpvOp.SourceContinued;
case SpvOp.SourceContinued:
return SpvOp.Store;
// 3: 7.2%
case SpvOp.AccessChain:
return SpvOp.Source;
case SpvOp.Source:
return SpvOp.AccessChain;
// 4: 5.0%
// Name - already small enum value - 5: 4.4%
// MemberName - already small enum value - 6: 2.9%
case SpvOp.VectorShuffle:
return SpvOp.SourceExtension;
case SpvOp.SourceExtension:
return SpvOp.VectorShuffle;
// 7: 4.0%
case SpvOp.MemberDecorate:
return SpvOp.String;
case SpvOp.String:
return SpvOp.MemberDecorate;
// 8: 0.9%
case SpvOp.Label:
return SpvOp.Line;
case SpvOp.Line:
return SpvOp.Label;
// 9: 3.9%
case SpvOp.Variable:
return (SpvOp)9;
case (SpvOp)9:
return SpvOp.Variable;
// 10: 3.9%
case SpvOp.FMul:
return SpvOp.Extension;
case SpvOp.Extension:
return SpvOp.FMul;
// 11: 2.5%
// ExtInst - already small enum value - 12: 1.2%
// VectorShuffleCompact - already small enum value - used for compact shuffle encoding
case SpvOp.FAdd:
return SpvOp.ExtInstImport;
case SpvOp.ExtInstImport:
return SpvOp.FAdd;
// 14: 2.2%
case SpvOp.TypePointer:
return SpvOp.MemoryModel;
case SpvOp.MemoryModel:
return SpvOp.TypePointer;
// 15: 1.1%
case SpvOp.FNegate:
return SpvOp.EntryPoint;
case SpvOp.EntryPoint:
return SpvOp.FNegate;
}
return op;
}
private static uint DecodeLen(SpvOp op, uint len)
{
len++;
switch (op)
{
case SpvOp.VectorShuffle:
len += 4;
break;
case SpvOp.VectorShuffleCompact:
len += 4;
break;
case SpvOp.Decorate:
len += 2;
break;
case SpvOp.Load:
len += 3;
break;
case SpvOp.AccessChain:
len += 3;
break;
}
return len;
}
private static int DecorationExtraOps(int dec)
{
// RelaxedPrecision, Block..ColMajor
if (dec == 0 || (dec >= 2 && dec <= 5))
{
return 0;
}
// Stream..XfbStride
if (dec >= 29 && dec <= 37)
{
return 1;
}
// unknown, encode length
return -1;
}
private static int ZigDecode(uint u)
{
return (u & 1) != 0 ? unchecked((int)(~(u >> 1))) : unchecked((int)(u >> 1));
}
public const uint SpirVHeaderMagic = 0x07230203;
/// <summary>
/// 'SMOL' ascii
/// </summary>
public const uint SmolHeaderMagic = 0x534D4F4C;
private const int HeaderSize = 6 * sizeof(uint);
}
}

View File

@@ -0,0 +1,369 @@
namespace Smolv
{
public enum SpvOp
{
Nop = 0,
Undef = 1,
SourceContinued = 2,
Source = 3,
SourceExtension = 4,
Name = 5,
MemberName = 6,
String = 7,
Line = 8,
Extension = 10,
ExtInstImport = 11,
ExtInst = 12,
/// <summary>
/// Not in SPIR-V, added for SMOL-V!
/// </summary>
VectorShuffleCompact = 13,
MemoryModel = 14,
EntryPoint = 15,
ExecutionMode = 16,
Capability = 17,
TypeVoid = 19,
TypeBool = 20,
TypeInt = 21,
TypeFloat = 22,
TypeVector = 23,
TypeMatrix = 24,
TypeImage = 25,
TypeSampler = 26,
TypeSampledImage = 27,
TypeArray = 28,
TypeRuntimeArray = 29,
TypeStruct = 30,
TypeOpaque = 31,
TypePointer = 32,
TypeFunction = 33,
TypeEvent = 34,
TypeDeviceEvent = 35,
TypeReserveId = 36,
TypeQueue = 37,
TypePipe = 38,
TypeForwardPointer = 39,
ConstantTrue = 41,
ConstantFalse = 42,
Constant = 43,
ConstantComposite = 44,
ConstantSampler = 45,
ConstantNull = 46,
SpecConstantTrue = 48,
SpecConstantFalse = 49,
SpecConstant = 50,
SpecConstantComposite = 51,
SpecConstantOp = 52,
Function = 54,
FunctionParameter = 55,
FunctionEnd = 56,
FunctionCall = 57,
Variable = 59,
ImageTexelPointer = 60,
Load = 61,
Store = 62,
CopyMemory = 63,
CopyMemorySized = 64,
AccessChain = 65,
InBoundsAccessChain = 66,
PtrAccessChain = 67,
ArrayLength = 68,
GenericPtrMemSemantics = 69,
InBoundsPtrAccessChain = 70,
Decorate = 71,
MemberDecorate = 72,
DecorationGroup = 73,
GroupDecorate = 74,
GroupMemberDecorate = 75,
VectorExtractDynamic = 77,
VectorInsertDynamic = 78,
VectorShuffle = 79,
CompositeConstruct = 80,
CompositeExtract = 81,
CompositeInsert = 82,
CopyObject = 83,
Transpose = 84,
SampledImage = 86,
ImageSampleImplicitLod = 87,
ImageSampleExplicitLod = 88,
ImageSampleDrefImplicitLod = 89,
ImageSampleDrefExplicitLod = 90,
ImageSampleProjImplicitLod = 91,
ImageSampleProjExplicitLod = 92,
ImageSampleProjDrefImplicitLod = 93,
ImageSampleProjDrefExplicitLod = 94,
ImageFetch = 95,
ImageGather = 96,
ImageDrefGather = 97,
ImageRead = 98,
ImageWrite = 99,
Image = 100,
ImageQueryFormat = 101,
ImageQueryOrder = 102,
ImageQuerySizeLod = 103,
ImageQuerySize = 104,
ImageQueryLod = 105,
ImageQueryLevels = 106,
ImageQuerySamples = 107,
ConvertFToU = 109,
ConvertFToS = 110,
ConvertSToF = 111,
ConvertUToF = 112,
UConvert = 113,
SConvert = 114,
FConvert = 115,
QuantizeToF16 = 116,
ConvertPtrToU = 117,
SatConvertSToU = 118,
SatConvertUToS = 119,
ConvertUToPtr = 120,
PtrCastToGeneric = 121,
GenericCastToPtr = 122,
GenericCastToPtrExplicit = 123,
Bitcast = 124,
SNegate = 126,
FNegate = 127,
IAdd = 128,
FAdd = 129,
ISub = 130,
FSub = 131,
IMul = 132,
FMul = 133,
UDiv = 134,
SDiv = 135,
FDiv = 136,
UMod = 137,
SRem = 138,
SMod = 139,
FRem = 140,
FMod = 141,
VectorTimesScalar = 142,
MatrixTimesScalar = 143,
VectorTimesMatrix = 144,
MatrixTimesVector = 145,
MatrixTimesMatrix = 146,
OuterProduct = 147,
Dot = 148,
IAddCarry = 149,
ISubBorrow = 150,
UMulExtended = 151,
SMulExtended = 152,
Any = 154,
All = 155,
IsNan = 156,
IsInf = 157,
IsFinite = 158,
IsNormal = 159,
SignBitSet = 160,
LessOrGreater = 161,
Ordered = 162,
Unordered = 163,
LogicalEqual = 164,
LogicalNotEqual = 165,
LogicalOr = 166,
LogicalAnd = 167,
LogicalNot = 168,
Select = 169,
IEqual = 170,
INotEqual = 171,
UGreaterThan = 172,
SGreaterThan = 173,
UGreaterThanEqual = 174,
SGreaterThanEqual = 175,
ULessThan = 176,
SLessThan = 177,
ULessThanEqual = 178,
SLessThanEqual = 179,
FOrdEqual = 180,
FUnordEqual = 181,
FOrdNotEqual = 182,
FUnordNotEqual = 183,
FOrdLessThan = 184,
FUnordLessThan = 185,
FOrdGreaterThan = 186,
FUnordGreaterThan = 187,
FOrdLessThanEqual = 188,
FUnordLessThanEqual = 189,
FOrdGreaterThanEqual = 190,
FUnordGreaterThanEqual = 191,
ShiftRightLogical = 194,
ShiftRightArithmetic = 195,
ShiftLeftLogical = 196,
BitwiseOr = 197,
BitwiseXor = 198,
BitwiseAnd = 199,
Not = 200,
BitFieldInsert = 201,
BitFieldSExtract = 202,
BitFieldUExtract = 203,
BitReverse = 204,
BitCount = 205,
DPdx = 207,
DPdy = 208,
Fwidth = 209,
DPdxFine = 210,
DPdyFine = 211,
FwidthFine = 212,
DPdxCoarse = 213,
DPdyCoarse = 214,
FwidthCoarse = 215,
EmitVertex = 218,
EndPrimitive = 219,
EmitStreamVertex = 220,
EndStreamPrimitive = 221,
ControlBarrier = 224,
MemoryBarrier = 225,
AtomicLoad = 227,
AtomicStore = 228,
AtomicExchange = 229,
AtomicCompareExchange = 230,
AtomicCompareExchangeWeak = 231,
AtomicIIncrement = 232,
AtomicIDecrement = 233,
AtomicIAdd = 234,
AtomicISub = 235,
AtomicSMin = 236,
AtomicUMin = 237,
AtomicSMax = 238,
AtomicUMax = 239,
AtomicAnd = 240,
AtomicOr = 241,
AtomicXor = 242,
Phi = 245,
LoopMerge = 246,
SelectionMerge = 247,
Label = 248,
Branch = 249,
BranchConditional = 250,
Switch = 251,
Kill = 252,
Return = 253,
ReturnValue = 254,
Unreachable = 255,
LifetimeStart = 256,
LifetimeStop = 257,
GroupAsyncCopy = 259,
GroupWaitEvents = 260,
GroupAll = 261,
GroupAny = 262,
GroupBroadcast = 263,
GroupIAdd = 264,
GroupFAdd = 265,
GroupFMin = 266,
GroupUMin = 267,
GroupSMin = 268,
GroupFMax = 269,
GroupUMax = 270,
GroupSMax = 271,
ReadPipe = 274,
WritePipe = 275,
ReservedReadPipe = 276,
ReservedWritePipe = 277,
ReserveReadPipePackets = 278,
ReserveWritePipePackets = 279,
CommitReadPipe = 280,
CommitWritePipe = 281,
IsValidReserveId = 282,
GetNumPipePackets = 283,
GetMaxPipePackets = 284,
GroupReserveReadPipePackets = 285,
GroupReserveWritePipePackets = 286,
GroupCommitReadPipe = 287,
GroupCommitWritePipe = 288,
EnqueueMarker = 291,
EnqueueKernel = 292,
GetKernelNDrangeSubGroupCount = 293,
GetKernelNDrangeMaxSubGroupSize = 294,
GetKernelWorkGroupSize = 295,
GetKernelPreferredWorkGroupSizeMultiple = 296,
RetainEvent = 297,
ReleaseEvent = 298,
CreateUserEvent = 299,
IsValidEvent = 300,
SetUserEventStatus = 301,
CaptureEventProfilingInfo = 302,
GetDefaultQueue = 303,
BuildNDRange = 304,
ImageSparseSampleImplicitLod = 305,
ImageSparseSampleExplicitLod = 306,
ImageSparseSampleDrefImplicitLod = 307,
ImageSparseSampleDrefExplicitLod = 308,
ImageSparseSampleProjImplicitLod = 309,
ImageSparseSampleProjExplicitLod = 310,
ImageSparseSampleProjDrefImplicitLod = 311,
ImageSparseSampleProjDrefExplicitLod = 312,
ImageSparseFetch = 313,
ImageSparseGather = 314,
ImageSparseDrefGather = 315,
ImageSparseTexelsResident = 316,
NoLine = 317,
AtomicFlagTestAndSet = 318,
AtomicFlagClear = 319,
ImageSparseRead = 320,
SizeOf = 321,
TypePipeStorage = 322,
ConstantPipeStorage = 323,
CreatePipeFromPipeStorage = 324,
GetKernelLocalSizeForSubgroupCount = 325,
GetKernelMaxNumSubgroups = 326,
TypeNamedBarrier = 327,
NamedBarrierInitialize = 328,
MemoryNamedBarrier = 329,
ModuleProcessed = 330,
KnownOpsCount,
}
public static class SpvOpExtensions
{
public static bool OpHasResult(this SpvOp _this)
{
if (_this < 0 || _this >= SpvOp.KnownOpsCount)
{
return false;
}
return OpData.SpirvOpData[(int)_this].hasResult != 0;
}
public static bool OpHasType(this SpvOp _this)
{
if (_this < 0 || _this >= SpvOp.KnownOpsCount)
{
return false;
}
return OpData.SpirvOpData[(int)_this].hasType != 0;
}
public static int OpDeltaFromResult(this SpvOp _this)
{
if (_this < 0 || _this >= SpvOp.KnownOpsCount)
{
return 0;
}
return OpData.SpirvOpData[(int)_this].deltaFromResult;
}
public static bool OpVarRest(this SpvOp _this)
{
if (_this < 0 || _this >= SpvOp.KnownOpsCount)
{
return false;
}
return OpData.SpirvOpData[(int)_this].varrest != 0;
}
public static bool OpDebugInfo(this SpvOp _this)
{
return
_this == SpvOp.SourceContinued ||
_this == SpvOp.Source ||
_this == SpvOp.SourceExtension ||
_this == SpvOp.Name ||
_this == SpvOp.MemberName ||
_this == SpvOp.String ||
_this == SpvOp.Line ||
_this == SpvOp.NoLine ||
_this == SpvOp.ModuleProcessed;
}
}
}

View File

@@ -0,0 +1,74 @@
using Smolv;
using SpirV;
using System;
using System.IO;
using System.Text;
namespace AssetStudio
{
public static class SpirVShaderConverter
{
public static string Convert(byte[] m_ProgramCode)
{
var sb = new StringBuilder();
using (var ms = new MemoryStream(m_ProgramCode))
{
using (var reader = new BinaryReader(ms))
{
int requirements = reader.ReadInt32();
int minOffset = m_ProgramCode.Length;
int snippetCount = 5;
/*if (version[0] > 2019 || (version[0] == 2019 && version[1] >= 3)) //2019.3 and up
{
snippetCount = 6;
}*/
for (int i = 0; i < snippetCount; i++)
{
if (reader.BaseStream.Position >= minOffset)
{
break;
}
int offset = reader.ReadInt32();
int size = reader.ReadInt32();
if (size > 0)
{
if (offset < minOffset)
{
minOffset = offset;
}
var pos = ms.Position;
sb.Append(ExportSnippet(ms, offset, size));
ms.Position = pos;
}
}
}
}
return sb.ToString();
}
private static string ExportSnippet(Stream stream, int offset, int size)
{
stream.Position = offset;
int decodedSize = SmolvDecoder.GetDecodedBufferSize(stream);
if (decodedSize == 0)
{
throw new Exception("Invalid SMOL-V shader header");
}
using (var decodedStream = new MemoryStream(new byte[decodedSize]))
{
if (SmolvDecoder.Decode(stream, size, decodedStream))
{
decodedStream.Position = 0;
var module = Module.ReadFrom(decodedStream);
var disassembler = new Disassembler();
return disassembler.Disassemble(module, DisassemblyOptions.Default).Replace("\r\n", "\n");
}
else
{
throw new Exception("Unable to decode SMOL-V shader");
}
}
}
}
}

View File

@@ -0,0 +1,176 @@
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
namespace AssetStudio
{
public static class SpriteHelper
{
public static Image<Bgra32> GetImage(this Sprite m_Sprite)
{
if (m_Sprite.m_SpriteAtlas != null && m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlas))
{
if (m_SpriteAtlas.m_RenderDataMap.TryGetValue(m_Sprite.m_RenderDataKey, out var spriteAtlasData) && spriteAtlasData.texture.TryGet(out var m_Texture2D))
{
return CutImage(m_Sprite, m_Texture2D, spriteAtlasData.textureRect, spriteAtlasData.textureRectOffset, spriteAtlasData.downscaleMultiplier, spriteAtlasData.settingsRaw);
}
}
else
{
if (m_Sprite.m_RD.texture.TryGet(out var m_Texture2D))
{
return CutImage(m_Sprite, m_Texture2D, m_Sprite.m_RD.textureRect, m_Sprite.m_RD.textureRectOffset, m_Sprite.m_RD.downscaleMultiplier, m_Sprite.m_RD.settingsRaw);
}
}
return null;
}
private static Image<Bgra32> CutImage(Sprite m_Sprite, Texture2D m_Texture2D, Rectf textureRect, Vector2 textureRectOffset, float downscaleMultiplier, SpriteSettings settingsRaw)
{
var originalImage = m_Texture2D.ConvertToImage(false);
if (originalImage != null)
{
using (originalImage)
{
if (downscaleMultiplier > 0f && downscaleMultiplier != 1f)
{
var width = (int)(m_Texture2D.m_Width / downscaleMultiplier);
var height = (int)(m_Texture2D.m_Height / downscaleMultiplier);
originalImage.Mutate(x => x.Resize(width, height));
}
var rectX = (int)Math.Floor(textureRect.x);
var rectY = (int)Math.Floor(textureRect.y);
var rectRight = (int)Math.Ceiling(textureRect.x + textureRect.width);
var rectBottom = (int)Math.Ceiling(textureRect.y + textureRect.height);
rectRight = Math.Min(rectRight, originalImage.Width);
rectBottom = Math.Min(rectBottom, originalImage.Height);
var rect = new Rectangle(rectX, rectY, rectRight - rectX, rectBottom - rectY);
var spriteImage = originalImage.Clone(x => x.Crop(rect));
if (settingsRaw.packed == 1)
{
//RotateAndFlip
switch (settingsRaw.packingRotation)
{
case SpritePackingRotation.FlipHorizontal:
spriteImage.Mutate(x => x.Flip(FlipMode.Horizontal));
break;
case SpritePackingRotation.FlipVertical:
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
break;
case SpritePackingRotation.Rotate180:
spriteImage.Mutate(x => x.Rotate(180));
break;
case SpritePackingRotation.Rotate90:
spriteImage.Mutate(x => x.Rotate(270));
break;
}
}
//Tight
if (settingsRaw.packingMode == SpritePackingMode.Tight)
{
try
{
var triangles = GetTriangles(m_Sprite.m_RD);
var polygons = triangles.Select(x => new Polygon(new LinearLineSegment(x.Select(y => new PointF(y.X, y.Y)).ToArray()))).ToArray();
IPathCollection path = new PathCollection(polygons);
var matrix = Matrix3x2.CreateScale(m_Sprite.m_PixelsToUnits);
matrix *= Matrix3x2.CreateTranslation(textureRect.width * m_Sprite.m_Pivot.X - textureRectOffset.X, textureRect.height * m_Sprite.m_Pivot.Y - textureRectOffset.Y);
path = path.Transform(matrix);
var graphicsOptions = new GraphicsOptions
{
Antialias = false,
AlphaCompositionMode = PixelAlphaCompositionMode.DestOut
};
var options = new DrawingOptions
{
GraphicsOptions = graphicsOptions
};
using (var mask = new Image<Bgra32>(rect.Width, rect.Height, SixLabors.ImageSharp.Color.Black))
{
mask.Mutate(x => x.Fill(options, SixLabors.ImageSharp.Color.Red, path));
var bursh = new ImageBrush(mask);
spriteImage.Mutate(x => x.Fill(graphicsOptions, bursh));
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
return spriteImage;
}
}
catch
{
// ignored
}
}
//Rectangle
spriteImage.Mutate(x => x.Flip(FlipMode.Vertical));
return spriteImage;
}
}
return null;
}
private static Vector2[][] GetTriangles(SpriteRenderData m_RD)
{
if (m_RD.vertices != null) //5.6 down
{
var vertices = m_RD.vertices.Select(x => (Vector2)x.pos).ToArray();
var triangleCount = m_RD.indices.Length / 3;
var triangles = new Vector2[triangleCount][];
for (int i = 0; i < triangleCount; i++)
{
var first = m_RD.indices[i * 3];
var second = m_RD.indices[i * 3 + 1];
var third = m_RD.indices[i * 3 + 2];
var triangle = new[] { vertices[first], vertices[second], vertices[third] };
triangles[i] = triangle;
}
return triangles;
}
else //5.6 and up
{
var triangles = new List<Vector2[]>();
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 indexReader = new BinaryReader(new MemoryStream(m_RD.m_IndexBuffer)))
{
foreach (var subMesh in m_RD.m_SubMeshes)
{
vertexReader.BaseStream.Position = m_Stream.offset + subMesh.firstVertex * m_Stream.stride + m_Channel.offset;
var vertices = new Vector2[subMesh.vertexCount];
for (int v = 0; v < subMesh.vertexCount; v++)
{
vertices[v] = vertexReader.ReadVector3();
vertexReader.BaseStream.Position += m_Stream.stride - 12;
}
indexReader.BaseStream.Position = subMesh.firstByte;
var triangleCount = subMesh.indexCount / 3u;
for (int i = 0; i < triangleCount; i++)
{
var first = indexReader.ReadUInt16() - subMesh.firstVertex;
var second = indexReader.ReadUInt16() - subMesh.firstVertex;
var third = indexReader.ReadUInt16() - subMesh.firstVertex;
var triangle = new[] { vertices[first], vertices[second], vertices[third] };
triangles.Add(triangle);
}
}
}
}
return triangles.ToArray();
}
}
}
}

View File

@@ -0,0 +1,706 @@
using System;
using System.Runtime.CompilerServices;
using Texture2DDecoder;
namespace AssetStudio
{
public class Texture2DConverter
{
private ResourceReader reader;
private int m_Width;
private int m_Height;
private TextureFormat m_TextureFormat;
private int[] version;
private BuildTarget platform;
private int outputSize;
public Texture2DConverter(Texture2D m_Texture2D)
{
reader = m_Texture2D.image_data;
m_Width = m_Texture2D.m_Width;
m_Height = m_Texture2D.m_Height;
m_TextureFormat = m_Texture2D.m_TextureFormat;
version = m_Texture2D.version;
platform = m_Texture2D.platform;
outputSize = m_Width * m_Height * 4;
}
public bool DecodeTexture2D(byte[] bytes)
{
if (reader.Size == 0 || m_Width == 0 || m_Height == 0)
{
return false;
}
var flag = false;
var buff = BigArrayPool<byte>.Shared.Rent(reader.Size);
reader.GetData(buff);
switch (m_TextureFormat)
{
case TextureFormat.Alpha8: //test pass
flag = DecodeAlpha8(buff, bytes);
break;
case TextureFormat.ARGB4444: //test pass
SwapBytesForXbox(buff);
flag = DecodeARGB4444(buff, bytes);
break;
case TextureFormat.RGB24: //test pass
flag = DecodeRGB24(buff, bytes);
break;
case TextureFormat.RGBA32: //test pass
flag = DecodeRGBA32(buff, bytes);
break;
case TextureFormat.ARGB32: //test pass
flag = DecodeARGB32(buff, bytes);
break;
case TextureFormat.RGB565: //test pass
SwapBytesForXbox(buff);
flag = DecodeRGB565(buff, bytes);
break;
case TextureFormat.R16: //test pass
case TextureFormat.R16_2: //test pass
flag = DecodeR16(buff, bytes);
break;
case TextureFormat.DXT1: //test pass
SwapBytesForXbox(buff);
flag = DecodeDXT1(buff, bytes);
break;
case TextureFormat.DXT3:
break;
case TextureFormat.DXT5: //test pass
SwapBytesForXbox(buff);
flag = DecodeDXT5(buff, bytes);
break;
case TextureFormat.RGBA4444: //test pass
flag = DecodeRGBA4444(buff, bytes);
break;
case TextureFormat.BGRA32: //test pass
flag = DecodeBGRA32(buff, bytes);
break;
case TextureFormat.RHalf:
flag = DecodeRHalf(buff, bytes);
break;
case TextureFormat.RGHalf:
flag = DecodeRGHalf(buff, bytes);
break;
case TextureFormat.RGBAHalf: //test pass
flag = DecodeRGBAHalf(buff, bytes);
break;
case TextureFormat.RFloat:
flag = DecodeRFloat(buff, bytes);
break;
case TextureFormat.RGFloat:
flag = DecodeRGFloat(buff, bytes);
break;
case TextureFormat.RGBAFloat:
flag = DecodeRGBAFloat(buff, bytes);
break;
case TextureFormat.YUY2: //test pass
flag = DecodeYUY2(buff, bytes);
break;
case TextureFormat.RGB9e5Float: //test pass
flag = DecodeRGB9e5Float(buff, bytes);
break;
case TextureFormat.BC6H: //test pass
flag = DecodeBC6H(buff, bytes);
break;
case TextureFormat.BC7: //test pass
flag = DecodeBC7(buff, bytes);
break;
case TextureFormat.BC4: //test pass
flag = DecodeBC4(buff, bytes);
break;
case TextureFormat.BC5: //test pass
flag = DecodeBC5(buff, bytes);
break;
case TextureFormat.DXT1Crunched: //test pass
flag = DecodeDXT1Crunched(buff, bytes);
break;
case TextureFormat.DXT5Crunched: //test pass
flag = DecodeDXT5Crunched(buff, bytes);
break;
case TextureFormat.PVRTC_RGB2: //test pass
case TextureFormat.PVRTC_RGBA2: //test pass
flag = DecodePVRTC(buff, bytes, true);
break;
case TextureFormat.PVRTC_RGB4: //test pass
case TextureFormat.PVRTC_RGBA4: //test pass
flag = DecodePVRTC(buff, bytes, false);
break;
case TextureFormat.ETC_RGB4: //test pass
case TextureFormat.ETC_RGB4_3DS:
flag = DecodeETC1(buff, bytes);
break;
case TextureFormat.ATC_RGB4: //test pass
flag = DecodeATCRGB4(buff, bytes);
break;
case TextureFormat.ATC_RGBA8: //test pass
flag = DecodeATCRGBA8(buff, bytes);
break;
case TextureFormat.EAC_R: //test pass
flag = DecodeEACR(buff, bytes);
break;
case TextureFormat.EAC_R_SIGNED:
flag = DecodeEACRSigned(buff, bytes);
break;
case TextureFormat.EAC_RG: //test pass
flag = DecodeEACRG(buff, bytes);
break;
case TextureFormat.EAC_RG_SIGNED:
flag = DecodeEACRGSigned(buff, bytes);
break;
case TextureFormat.ETC2_RGB: //test pass
flag = DecodeETC2(buff, bytes);
break;
case TextureFormat.ETC2_RGBA1: //test pass
flag = DecodeETC2A1(buff, bytes);
break;
case TextureFormat.ETC2_RGBA8: //test pass
case TextureFormat.ETC_RGBA8_3DS:
flag = DecodeETC2A8(buff, bytes);
break;
case TextureFormat.ASTC_RGB_4x4: //test pass
case TextureFormat.ASTC_RGBA_4x4: //test pass
case TextureFormat.ASTC_HDR_4x4: //test pass
flag = DecodeASTC(buff, bytes, 4);
break;
case TextureFormat.ASTC_RGB_5x5: //test pass
case TextureFormat.ASTC_RGBA_5x5: //test pass
case TextureFormat.ASTC_HDR_5x5: //test pass
flag = DecodeASTC(buff, bytes, 5);
break;
case TextureFormat.ASTC_RGB_6x6: //test pass
case TextureFormat.ASTC_RGBA_6x6: //test pass
case TextureFormat.ASTC_HDR_6x6: //test pass
flag = DecodeASTC(buff, bytes, 6);
break;
case TextureFormat.ASTC_RGB_8x8: //test pass
case TextureFormat.ASTC_RGBA_8x8: //test pass
case TextureFormat.ASTC_HDR_8x8: //test pass
flag = DecodeASTC(buff, bytes, 8);
break;
case TextureFormat.ASTC_RGB_10x10: //test pass
case TextureFormat.ASTC_RGBA_10x10: //test pass
case TextureFormat.ASTC_HDR_10x10: //test pass
flag = DecodeASTC(buff, bytes, 10);
break;
case TextureFormat.ASTC_RGB_12x12: //test pass
case TextureFormat.ASTC_RGBA_12x12: //test pass
case TextureFormat.ASTC_HDR_12x12: //test pass
flag = DecodeASTC(buff, bytes, 12);
break;
case TextureFormat.RG16: //test pass
flag = DecodeRG16(buff, bytes);
break;
case TextureFormat.R8: //test pass
flag = DecodeR8(buff, bytes);
break;
case TextureFormat.ETC_RGB4Crunched: //test pass
flag = DecodeETC1Crunched(buff, bytes);
break;
case TextureFormat.ETC2_RGBA8Crunched: //test pass
flag = DecodeETC2A8Crunched(buff, bytes);
break;
case TextureFormat.RG32: //test pass
flag = DecodeRG32(buff, bytes);
break;
case TextureFormat.RGB48: //test pass
flag = DecodeRGB48(buff, bytes);
break;
case TextureFormat.RGBA64: //test pass
flag = DecodeRGBA64(buff, bytes);
break;
}
BigArrayPool<byte>.Shared.Return(buff);
return flag;
}
private void SwapBytesForXbox(byte[] image_data)
{
if (platform == BuildTarget.XBOX360)
{
for (var i = 0; i < reader.Size / 2; i++)
{
var b = image_data[i * 2];
image_data[i * 2] = image_data[i * 2 + 1];
image_data[i * 2 + 1] = b;
}
}
}
private bool DecodeAlpha8(byte[] image_data, byte[] buff)
{
var size = m_Width * m_Height;
var span = new Span<byte>(buff);
span.Fill(0xFF);
for (var i = 0; i < size; i++)
{
buff[i * 4 + 3] = image_data[i];
}
return true;
}
private bool DecodeARGB4444(byte[] image_data, byte[] buff)
{
var size = m_Width * m_Height;
var pixelNew = new byte[4];
for (var i = 0; i < size; i++)
{
var pixelOldShort = BitConverter.ToUInt16(image_data, i * 2);
pixelNew[0] = (byte)(pixelOldShort & 0x000f);
pixelNew[1] = (byte)((pixelOldShort & 0x00f0) >> 4);
pixelNew[2] = (byte)((pixelOldShort & 0x0f00) >> 8);
pixelNew[3] = (byte)((pixelOldShort & 0xf000) >> 12);
for (var j = 0; j < 4; j++)
pixelNew[j] = (byte)((pixelNew[j] << 4) | pixelNew[j]);
pixelNew.CopyTo(buff, i * 4);
}
return true;
}
private bool DecodeRGB24(byte[] image_data, byte[] buff)
{
var size = m_Width * m_Height;
for (var i = 0; i < size; i++)
{
buff[i * 4] = image_data[i * 3 + 2];
buff[i * 4 + 1] = image_data[i * 3 + 1];
buff[i * 4 + 2] = image_data[i * 3 + 0];
buff[i * 4 + 3] = 255;
}
return true;
}
private bool DecodeRGBA32(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
{
buff[i] = image_data[i + 2];
buff[i + 1] = image_data[i + 1];
buff[i + 2] = image_data[i + 0];
buff[i + 3] = image_data[i + 3];
}
return true;
}
private bool DecodeARGB32(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
{
buff[i] = image_data[i + 3];
buff[i + 1] = image_data[i + 2];
buff[i + 2] = image_data[i + 1];
buff[i + 3] = image_data[i + 0];
}
return true;
}
private bool DecodeRGB565(byte[] image_data, byte[] buff)
{
var size = m_Width * m_Height;
for (var i = 0; i < size; i++)
{
var p = BitConverter.ToUInt16(image_data, i * 2);
buff[i * 4] = (byte)((p << 3) | (p >> 2 & 7));
buff[i * 4 + 1] = (byte)((p >> 3 & 0xfc) | (p >> 9 & 3));
buff[i * 4 + 2] = (byte)((p >> 8 & 0xf8) | (p >> 13));
buff[i * 4 + 3] = 255;
}
return true;
}
private bool DecodeR16(byte[] image_data, byte[] buff)
{
var size = m_Width * m_Height;
for (var i = 0; i < size; i++)
{
buff[i * 4] = 0; //b
buff[i * 4 + 1] = 0; //g
buff[i * 4 + 2] = DownScaleFrom16BitTo8Bit(BitConverter.ToUInt16(image_data, i * 2)); //r
buff[i * 4 + 3] = 255; //a
}
return true;
}
private bool DecodeDXT1(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeDXT1(image_data, m_Width, m_Height, buff);
}
private bool DecodeDXT5(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeDXT5(image_data, m_Width, m_Height, buff);
}
private bool DecodeRGBA4444(byte[] image_data, byte[] buff)
{
var size = m_Width * m_Height;
var pixelNew = new byte[4];
for (var i = 0; i < size; i++)
{
var pixelOldShort = BitConverter.ToUInt16(image_data, i * 2);
pixelNew[0] = (byte)((pixelOldShort & 0x00f0) >> 4);
pixelNew[1] = (byte)((pixelOldShort & 0x0f00) >> 8);
pixelNew[2] = (byte)((pixelOldShort & 0xf000) >> 12);
pixelNew[3] = (byte)(pixelOldShort & 0x000f);
for (var j = 0; j < 4; j++)
pixelNew[j] = (byte)((pixelNew[j] << 4) | pixelNew[j]);
pixelNew.CopyTo(buff, i * 4);
}
return true;
}
private bool DecodeBGRA32(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
{
buff[i] = image_data[i];
buff[i + 1] = image_data[i + 1];
buff[i + 2] = image_data[i + 2];
buff[i + 3] = image_data[i + 3];
}
return true;
}
private bool DecodeRHalf(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
{
buff[i] = 0;
buff[i + 1] = 0;
buff[i + 2] = (byte)Math.Round(Half.ToHalf(image_data, i / 2) * 255f);
buff[i + 3] = 255;
}
return true;
}
private bool DecodeRGHalf(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
{
buff[i] = 0;
buff[i + 1] = (byte)Math.Round(Half.ToHalf(image_data, i + 2) * 255f);
buff[i + 2] = (byte)Math.Round(Half.ToHalf(image_data, i) * 255f);
buff[i + 3] = 255;
}
return true;
}
private bool DecodeRGBAHalf(byte[] image_data, byte[] buff)
{
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);
buff[i + 2] = (byte)Math.Round(Half.ToHalf(image_data, i * 2) * 255f);
buff[i + 3] = (byte)Math.Round(Half.ToHalf(image_data, i * 2 + 6) * 255f);
}
return true;
}
private bool DecodeRFloat(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
{
buff[i] = 0;
buff[i + 1] = 0;
buff[i + 2] = (byte)Math.Round(BitConverter.ToSingle(image_data, i) * 255f);
buff[i + 3] = 255;
}
return true;
}
private bool DecodeRGFloat(byte[] image_data, byte[] buff)
{
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);
buff[i + 2] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 2) * 255f);
buff[i + 3] = 255;
}
return true;
}
private bool DecodeRGBAFloat(byte[] image_data, byte[] buff)
{
for (var i = 0; i < buff.Length; 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);
buff[i + 2] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 4) * 255f);
buff[i + 3] = (byte)Math.Round(BitConverter.ToSingle(image_data, i * 4 + 12) * 255f);
}
return true;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte ClampByte(int x)
{
return (byte)(byte.MaxValue < x ? byte.MaxValue : (x > byte.MinValue ? x : byte.MinValue));
}
private bool DecodeYUY2(byte[] image_data, byte[] buff)
{
int p = 0;
int o = 0;
int halfWidth = m_Width / 2;
for (int j = 0; j < m_Height; j++)
{
for (int i = 0; i < halfWidth; ++i)
{
int y0 = image_data[p++];
int u0 = image_data[p++];
int y1 = image_data[p++];
int v0 = image_data[p++];
int c = y0 - 16;
int d = u0 - 128;
int e = v0 - 128;
buff[o++] = ClampByte((298 * c + 516 * d + 128) >> 8); // b
buff[o++] = ClampByte((298 * c - 100 * d - 208 * e + 128) >> 8); // g
buff[o++] = ClampByte((298 * c + 409 * e + 128) >> 8); // r
buff[o++] = 255;
c = y1 - 16;
buff[o++] = ClampByte((298 * c + 516 * d + 128) >> 8); // b
buff[o++] = ClampByte((298 * c - 100 * d - 208 * e + 128) >> 8); // g
buff[o++] = ClampByte((298 * c + 409 * e + 128) >> 8); // r
buff[o++] = 255;
}
}
return true;
}
private bool DecodeRGB9e5Float(byte[] image_data, byte[] buff)
{
for (var i = 0; i < outputSize; i += 4)
{
var n = BitConverter.ToInt32(image_data, i);
var scale = n >> 27 & 0x1f;
var scalef = Math.Pow(2, scale - 24);
var b = n >> 18 & 0x1ff;
var g = n >> 9 & 0x1ff;
var r = n & 0x1ff;
buff[i] = (byte)Math.Round(b * scalef * 255f);
buff[i + 1] = (byte)Math.Round(g * scalef * 255f);
buff[i + 2] = (byte)Math.Round(r * scalef * 255f);
buff[i + 3] = 255;
}
return true;
}
private bool DecodeBC4(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeBC4(image_data, m_Width, m_Height, buff);
}
private bool DecodeBC5(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeBC5(image_data, m_Width, m_Height, buff);
}
private bool DecodeBC6H(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeBC6(image_data, m_Width, m_Height, buff);
}
private bool DecodeBC7(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeBC7(image_data, m_Width, m_Height, buff);
}
private bool DecodeDXT1Crunched(byte[] image_data, byte[] buff)
{
if (UnpackCrunch(image_data, out var result))
{
if (DecodeDXT1(result, buff))
{
return true;
}
}
return false;
}
private bool DecodeDXT5Crunched(byte[] image_data, byte[] buff)
{
if (UnpackCrunch(image_data, out var result))
{
if (DecodeDXT5(result, buff))
{
return true;
}
}
return false;
}
private bool DecodePVRTC(byte[] image_data, byte[] buff, bool is2bpp)
{
return TextureDecoder.DecodePVRTC(image_data, m_Width, m_Height, buff, is2bpp);
}
private bool DecodeETC1(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeETC1(image_data, m_Width, m_Height, buff);
}
private bool DecodeATCRGB4(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeATCRGB4(image_data, m_Width, m_Height, buff);
}
private bool DecodeATCRGBA8(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeATCRGBA8(image_data, m_Width, m_Height, buff);
}
private bool DecodeEACR(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeEACR(image_data, m_Width, m_Height, buff);
}
private bool DecodeEACRSigned(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeEACRSigned(image_data, m_Width, m_Height, buff);
}
private bool DecodeEACRG(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeEACRG(image_data, m_Width, m_Height, buff);
}
private bool DecodeEACRGSigned(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeEACRGSigned(image_data, m_Width, m_Height, buff);
}
private bool DecodeETC2(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeETC2(image_data, m_Width, m_Height, buff);
}
private bool DecodeETC2A1(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeETC2A1(image_data, m_Width, m_Height, buff);
}
private bool DecodeETC2A8(byte[] image_data, byte[] buff)
{
return TextureDecoder.DecodeETC2A8(image_data, m_Width, m_Height, buff);
}
private bool DecodeASTC(byte[] image_data, byte[] buff, int blocksize)
{
return TextureDecoder.DecodeASTC(image_data, m_Width, m_Height, blocksize, blocksize, buff);
}
private bool DecodeRG16(byte[] image_data, byte[] buff)
{
var size = m_Width * m_Height;
for (var i = 0; i < size; i += 2)
{
buff[i * 4] = 0; //B
buff[i * 4 + 1] = image_data[i * 2 + 1];//G
buff[i * 4 + 2] = image_data[i * 2];//R
buff[i * 4 + 3] = 255;//A
}
return true;
}
private bool DecodeR8(byte[] image_data, byte[] buff)
{
var size = m_Width * m_Height;
for (var i = 0; i < size; i++)
{
buff[i * 4] = 0; //B
buff[i * 4 + 1] = 0; //G
buff[i * 4 + 2] = image_data[i];//R
buff[i * 4 + 3] = 255;//A
}
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))
{
if (DecodeETC1(result, buff))
{
return true;
}
}
return false;
}
private bool DecodeETC2A8Crunched(byte[] image_data, byte[] buff)
{
if (UnpackCrunch(image_data, out var result))
{
if (DecodeETC2A8(result, buff))
{
return true;
}
}
return false;
}
private bool UnpackCrunch(byte[] image_data, out byte[] result)
{
if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3) //2017.3 and up
|| m_TextureFormat == TextureFormat.ETC_RGB4Crunched
|| m_TextureFormat == TextureFormat.ETC2_RGBA8Crunched)
{
result = TextureDecoder.UnpackUnityCrunch(image_data);
}
else
{
result = TextureDecoder.UnpackCrunch(image_data);
}
if (result != null)
{
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,46 @@
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System.IO;
namespace AssetStudio
{
public static class Texture2DExtensions
{
public static Image<Bgra32> ConvertToImage(this Texture2D m_Texture2D, bool flip)
{
var converter = new Texture2DConverter(m_Texture2D);
var buff = BigArrayPool<byte>.Shared.Rent(m_Texture2D.m_Width * m_Texture2D.m_Height * 4);
try
{
if (converter.DecodeTexture2D(buff))
{
var image = Image.LoadPixelData<Bgra32>(buff, m_Texture2D.m_Width, m_Texture2D.m_Height);
if (flip)
{
image.Mutate(x => x.Flip(FlipMode.Vertical));
}
return image;
}
return null;
}
finally
{
BigArrayPool<byte>.Shared.Return(buff);
}
}
public static MemoryStream ConvertToStream(this Texture2D m_Texture2D, ImageFormat imageFormat, bool flip)
{
var image = ConvertToImage(m_Texture2D, flip);
if (image != null)
{
using (image)
{
return image.ConvertToStream(imageFormat);
}
}
return null;
}
}
}

View File

@@ -0,0 +1,292 @@
using Mono.Cecil;
using System;
using System.Collections.Generic;
using System.Linq;
using Unity.CecilTools;
using Unity.SerializationLogic;
namespace AssetStudio
{
public class TypeDefinitionConverter
{
private readonly TypeDefinition TypeDef;
private readonly TypeResolver TypeResolver;
private readonly SerializedTypeHelper Helper;
private readonly int Indent;
public TypeDefinitionConverter(TypeDefinition typeDef, SerializedTypeHelper helper, int indent)
{
TypeDef = typeDef;
TypeResolver = new TypeResolver(null);
Helper = helper;
Indent = indent;
}
public List<TypeTreeNode> ConvertToTypeTreeNodes()
{
var nodes = new List<TypeTreeNode>();
var baseTypes = new Stack<TypeReference>();
var lastBaseType = TypeDef.BaseType;
while (!UnitySerializationLogic.IsNonSerialized(lastBaseType))
{
var genericInstanceType = lastBaseType as GenericInstanceType;
if (genericInstanceType != null)
{
TypeResolver.Add(genericInstanceType);
}
baseTypes.Push(lastBaseType);
lastBaseType = lastBaseType.Resolve().BaseType;
}
while (baseTypes.Count > 0)
{
var typeReference = baseTypes.Pop();
var typeDefinition = typeReference.Resolve();
foreach (var fieldDefinition in typeDefinition.Fields.Where(WillUnitySerialize))
{
if (!IsHiddenByParentClass(baseTypes, fieldDefinition, TypeDef))
{
nodes.AddRange(ProcessingFieldRef(ResolveGenericFieldReference(fieldDefinition)));
}
}
var genericInstanceType = typeReference as GenericInstanceType;
if (genericInstanceType != null)
{
TypeResolver.Remove(genericInstanceType);
}
}
foreach (var field in FilteredFields())
{
nodes.AddRange(ProcessingFieldRef(field));
}
return nodes;
}
private bool WillUnitySerialize(FieldDefinition fieldDefinition)
{
try
{
var resolvedFieldType = TypeResolver.Resolve(fieldDefinition.FieldType);
if (UnitySerializationLogic.ShouldNotTryToResolve(resolvedFieldType))
{
return false;
}
if (!UnityEngineTypePredicates.IsUnityEngineObject(resolvedFieldType))
{
if (resolvedFieldType.FullName == fieldDefinition.DeclaringType.FullName)
{
return false;
}
}
return UnitySerializationLogic.WillUnitySerialize(fieldDefinition, TypeResolver);
}
catch (Exception ex)
{
throw new Exception(string.Format("Exception while processing {0} {1}, error {2}", fieldDefinition.FieldType.FullName, fieldDefinition.FullName, ex.Message));
}
}
private static bool IsHiddenByParentClass(IEnumerable<TypeReference> parentTypes, FieldDefinition fieldDefinition, TypeDefinition processingType)
{
return processingType.Fields.Any(f => f.Name == fieldDefinition.Name) || parentTypes.Any(t => t.Resolve().Fields.Any(f => f.Name == fieldDefinition.Name));
}
private IEnumerable<FieldDefinition> FilteredFields()
{
return TypeDef.Fields.Where(WillUnitySerialize).Where(f =>
UnitySerializationLogic.IsSupportedCollection(f.FieldType) ||
!f.FieldType.IsGenericInstance ||
UnitySerializationLogic.ShouldImplementIDeserializable(f.FieldType.Resolve()));
}
private FieldReference ResolveGenericFieldReference(FieldReference fieldRef)
{
var field = new FieldReference(fieldRef.Name, fieldRef.FieldType, ResolveDeclaringType(fieldRef.DeclaringType));
return TypeDef.Module.ImportReference(field);
}
private TypeReference ResolveDeclaringType(TypeReference declaringType)
{
var typeDefinition = declaringType.Resolve();
if (typeDefinition == null || !typeDefinition.HasGenericParameters)
{
return typeDefinition;
}
var genericInstanceType = new GenericInstanceType(typeDefinition);
foreach (var genericParameter in typeDefinition.GenericParameters)
{
genericInstanceType.GenericArguments.Add(genericParameter);
}
return TypeResolver.Resolve(genericInstanceType);
}
private List<TypeTreeNode> ProcessingFieldRef(FieldReference fieldDef)
{
var typeRef = TypeResolver.Resolve(fieldDef.FieldType);
return TypeRefToTypeTreeNodes(typeRef, fieldDef.Name, Indent, false);
}
private static bool IsStruct(TypeReference typeRef)
{
return typeRef.IsValueType && !IsEnum(typeRef) && !typeRef.IsPrimitive;
}
private static bool IsEnum(TypeReference typeRef)
{
return !typeRef.IsArray && typeRef.Resolve().IsEnum;
}
private static bool RequiresAlignment(TypeReference typeRef)
{
switch (typeRef.MetadataType)
{
case MetadataType.Boolean:
case MetadataType.Char:
case MetadataType.SByte:
case MetadataType.Byte:
case MetadataType.Int16:
case MetadataType.UInt16:
return true;
default:
return UnitySerializationLogic.IsSupportedCollection(typeRef) && RequiresAlignment(CecilUtils.ElementTypeOfCollection(typeRef));
}
}
private static bool IsSystemString(TypeReference typeRef)
{
return typeRef.FullName == "System.String";
}
private List<TypeTreeNode> TypeRefToTypeTreeNodes(TypeReference typeRef, string name, int indent, bool isElement)
{
var align = false;
if (!IsStruct(TypeDef) || !UnityEngineTypePredicates.IsUnityEngineValueType(TypeDef))
{
if (IsStruct(typeRef) || RequiresAlignment(typeRef))
{
align = true;
}
}
var nodes = new List<TypeTreeNode>();
if (typeRef.IsPrimitive)
{
var primitiveName = typeRef.Name;
switch (primitiveName)
{
case "Boolean":
primitiveName = "bool";
break;
case "Byte":
primitiveName = "UInt8";
break;
case "SByte":
primitiveName = "SInt8";
break;
case "Int16":
primitiveName = "SInt16";
break;
case "UInt16":
primitiveName = "UInt16";
break;
case "Int32":
primitiveName = "SInt32";
break;
case "UInt32":
primitiveName = "UInt32";
break;
case "Int64":
primitiveName = "SInt64";
break;
case "UInt64":
primitiveName = "UInt64";
break;
case "Char":
primitiveName = "char";
break;
case "Double":
primitiveName = "double";
break;
case "Single":
primitiveName = "float";
break;
default:
throw new NotSupportedException();
}
if (isElement)
{
align = false;
}
nodes.Add(new TypeTreeNode(primitiveName, name, indent, align));
}
else if (IsSystemString(typeRef))
{
Helper.AddString(nodes, name, indent);
}
else if (IsEnum(typeRef))
{
nodes.Add(new TypeTreeNode("SInt32", name, indent, align));
}
else if (CecilUtils.IsGenericList(typeRef))
{
var elementRef = CecilUtils.ElementTypeOfCollection(typeRef);
nodes.Add(new TypeTreeNode(typeRef.Name, name, indent, align));
Helper.AddArray(nodes, indent + 1);
nodes.AddRange(TypeRefToTypeTreeNodes(elementRef, "data", indent + 2, true));
}
else if (typeRef.IsArray)
{
var elementRef = typeRef.GetElementType();
nodes.Add(new TypeTreeNode(typeRef.Name, name, indent, align));
Helper.AddArray(nodes, indent + 1);
nodes.AddRange(TypeRefToTypeTreeNodes(elementRef, "data", indent + 2, true));
}
else if (UnityEngineTypePredicates.IsUnityEngineObject(typeRef))
{
Helper.AddPPtr(nodes, typeRef.Name, name, indent);
}
else if (UnityEngineTypePredicates.IsSerializableUnityClass(typeRef) || UnityEngineTypePredicates.IsSerializableUnityStruct(typeRef))
{
switch (typeRef.FullName)
{
case "UnityEngine.AnimationCurve":
Helper.AddAnimationCurve(nodes, name, indent);
break;
case "UnityEngine.Gradient":
Helper.AddGradient(nodes, name, indent);
break;
case "UnityEngine.GUIStyle":
Helper.AddGUIStyle(nodes, name, indent);
break;
case "UnityEngine.RectOffset":
Helper.AddRectOffset(nodes, name, indent);
break;
case "UnityEngine.Color32":
Helper.AddColor32(nodes, name, indent);
break;
case "UnityEngine.Matrix4x4":
Helper.AddMatrix4x4(nodes, name, indent);
break;
case "UnityEngine.Rendering.SphericalHarmonicsL2":
Helper.AddSphericalHarmonicsL2(nodes, name, indent);
break;
case "UnityEngine.PropertyName":
Helper.AddPropertyName(nodes, name, indent);
break;
}
}
else
{
nodes.Add(new TypeTreeNode(typeRef.Name, name, indent, align));
var typeDef = typeRef.Resolve();
var typeDefinitionConverter = new TypeDefinitionConverter(typeDef, Helper, indent + 1);
nodes.AddRange(typeDefinitionConverter.ConvertToTypeTreeNodes());
}
return nodes;
}
}
}

View File

@@ -0,0 +1,65 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Unity.CecilTools.Extensions;
namespace Unity.CecilTools
{
public static class CecilUtils
{
public static MethodDefinition FindInTypeExplicitImplementationFor(MethodDefinition interfaceMethod, TypeDefinition typeDefinition)
{
return typeDefinition.Methods.SingleOrDefault(m => m.Overrides.Any(o => o.CheckedResolve().SameAs(interfaceMethod)));
}
public static IEnumerable<TypeDefinition> AllInterfacesImplementedBy(TypeDefinition typeDefinition)
{
return TypeAndBaseTypesOf(typeDefinition).SelectMany(t => t.Interfaces).Select(i => i.InterfaceType.CheckedResolve()).Distinct();
}
public static IEnumerable<TypeDefinition> TypeAndBaseTypesOf(TypeReference typeReference)
{
while (typeReference != null)
{
var typeDefinition = typeReference.CheckedResolve();
yield return typeDefinition;
typeReference = typeDefinition.BaseType;
}
}
public static IEnumerable<TypeDefinition> BaseTypesOf(TypeReference typeReference)
{
return TypeAndBaseTypesOf(typeReference).Skip(1);
}
public static bool IsGenericList(TypeReference type)
{
return type.Name == "List`1" && type.SafeNamespace() == "System.Collections.Generic";
}
public static bool IsGenericDictionary(TypeReference type)
{
if (type is GenericInstanceType)
type = ((GenericInstanceType)type).ElementType;
return type.Name == "Dictionary`2" && type.SafeNamespace() == "System.Collections.Generic";
}
public static TypeReference ElementTypeOfCollection(TypeReference type)
{
var at = type as ArrayType;
if (at != null)
return at.ElementType;
if (IsGenericList(type))
return ((GenericInstanceType)type).GenericArguments.Single();
throw new ArgumentException();
}
}
}

View File

@@ -0,0 +1,21 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System;
using Mono.Cecil;
namespace Unity.CecilTools
{
static public class ElementType
{
public static TypeReference For(TypeReference byRefType)
{
var refType = byRefType as TypeSpecification;
if (refType != null)
return refType.ElementType;
throw new ArgumentException(string.Format("TypeReference isn't a TypeSpecification {0} ", byRefType));
}
}
}

View File

@@ -0,0 +1,50 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using Mono.Cecil;
namespace Unity.CecilTools.Extensions
{
static class MethodDefinitionExtensions
{
public static bool SameAs(this MethodDefinition self, MethodDefinition other)
{
// FIXME: should be able to compare MethodDefinition references directly
return self.FullName == other.FullName;
}
public static string PropertyName(this MethodDefinition self)
{
return self.Name.Substring(4);
}
public static bool IsConversionOperator(this MethodDefinition method)
{
if (!method.IsSpecialName)
return false;
return method.Name == "op_Implicit" || method.Name == "op_Explicit";
}
public static bool IsSimpleSetter(this MethodDefinition original)
{
return original.IsSetter && original.Parameters.Count == 1;
}
public static bool IsSimpleGetter(this MethodDefinition original)
{
return original.IsGetter && original.Parameters.Count == 0;
}
public static bool IsSimplePropertyAccessor(this MethodDefinition method)
{
return method.IsSimpleGetter() || method.IsSimpleSetter();
}
public static bool IsDefaultConstructor(MethodDefinition m)
{
return m.IsConstructor && !m.IsStatic && m.Parameters.Count == 0;
}
}
}

View File

@@ -0,0 +1,36 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System;
using Mono.Cecil;
namespace Unity.CecilTools.Extensions
{
public static class ResolutionExtensions
{
public static TypeDefinition CheckedResolve(this TypeReference type)
{
return Resolve(type, reference => reference.Resolve());
}
public static MethodDefinition CheckedResolve(this MethodReference method)
{
return Resolve(method, reference => reference.Resolve());
}
private static TDefinition Resolve<TReference, TDefinition>(TReference reference, Func<TReference, TDefinition> resolve)
where TReference : MemberReference
where TDefinition : class, IMemberDefinition
{
if (reference.Module == null)
throw new ResolutionException(reference);
var definition = resolve(reference);
if (definition == null)
throw new ResolutionException(reference);
return definition;
}
}
}

View File

@@ -0,0 +1,47 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mono.Cecil;
namespace Unity.CecilTools.Extensions
{
public static class TypeDefinitionExtensions
{
public static bool IsSubclassOf(this TypeDefinition type, string baseTypeName)
{
var baseType = type.BaseType;
if (baseType == null)
return false;
if (baseType.FullName == baseTypeName)
return true;
var baseTypeDef = baseType.Resolve();
if (baseTypeDef == null)
return false;
return IsSubclassOf(baseTypeDef, baseTypeName);
}
public static bool IsSubclassOf(this TypeDefinition type, params string[] baseTypeNames)
{
var baseType = type.BaseType;
if (baseType == null)
return false;
for (int i = 0; i < baseTypeNames.Length; i++)
if (baseType.FullName == baseTypeNames[i])
return true;
var baseTypeDef = baseType.Resolve();
if (baseTypeDef == null)
return false;
return IsSubclassOf(baseTypeDef, baseTypeNames);
}
}
}

View File

@@ -0,0 +1,53 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using Mono.Cecil;
namespace Unity.CecilTools.Extensions
{
public static class TypeReferenceExtensions
{
public static string SafeNamespace(this TypeReference type)
{
if (type.IsGenericInstance)
return ((GenericInstanceType)type).ElementType.SafeNamespace();
if (type.IsNested)
return type.DeclaringType.SafeNamespace();
return type.Namespace;
}
public static bool IsAssignableTo(this TypeReference typeRef, string typeName)
{
try
{
if (typeRef.IsGenericInstance)
return ElementType.For(typeRef).IsAssignableTo(typeName);
if (typeRef.FullName == typeName)
return true;
return typeRef.CheckedResolve().IsSubclassOf(typeName);
}
catch (AssemblyResolutionException) // If we can't resolve our typeref or one of its base types,
{ // let's assume it is not assignable to our target type
return false;
}
}
public static bool IsEnum(this TypeReference type)
{
return type.IsValueType && !type.IsPrimitive && type.CheckedResolve().IsEnum;
}
public static bool IsStruct(this TypeReference type)
{
return type.IsValueType && !type.IsPrimitive && !type.IsEnum() && !IsSystemDecimal(type);
}
private static bool IsSystemDecimal(TypeReference type)
{
return type.FullName == "System.Decimal";
}
}
}

View File

@@ -0,0 +1,168 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System.Collections.Generic;
using Unity.CecilTools.Extensions;
using Mono.Cecil;
namespace Unity.SerializationLogic
{
public class UnityEngineTypePredicates
{
private static readonly HashSet<string> TypesThatShouldHaveHadSerializableAttribute = new HashSet<string>
{
"Vector3",
"Vector2",
"Vector4",
"Rect",
"RectInt",
"Quaternion",
"Matrix4x4",
"Color",
"Color32",
"LayerMask",
"Bounds",
"BoundsInt",
"Vector3Int",
"Vector2Int",
};
private const string Gradient = "UnityEngine.Gradient";
private const string GUIStyle = "UnityEngine.GUIStyle";
private const string RectOffset = "UnityEngine.RectOffset";
protected const string UnityEngineObject = "UnityEngine.Object";
public const string MonoBehaviour = "UnityEngine.MonoBehaviour";
public const string ScriptableObject = "UnityEngine.ScriptableObject";
protected const string Matrix4x4 = "UnityEngine.Matrix4x4";
protected const string Color32 = "UnityEngine.Color32";
private const string SerializeFieldAttribute = "UnityEngine.SerializeField";
private const string SerializeReferenceAttribute = "UnityEngine.SerializeReference";
private static string[] serializableClasses = new[]
{
"UnityEngine.AnimationCurve",
"UnityEngine.Gradient",
"UnityEngine.GUIStyle",
"UnityEngine.RectOffset"
};
private static string[] serializableStructs = new[]
{
// NOTE: assumes all types here are NOT interfaces
"UnityEngine.Color32",
"UnityEngine.Matrix4x4",
"UnityEngine.Rendering.SphericalHarmonicsL2",
"UnityEngine.PropertyName",
};
public static bool IsMonoBehaviour(TypeReference type)
{
return IsMonoBehaviour(type.CheckedResolve());
}
private static bool IsMonoBehaviour(TypeDefinition typeDefinition)
{
return typeDefinition.IsSubclassOf(MonoBehaviour);
}
public static bool IsScriptableObject(TypeReference type)
{
return IsScriptableObject(type.CheckedResolve());
}
private static bool IsScriptableObject(TypeDefinition temp)
{
return temp.IsSubclassOf(ScriptableObject);
}
public static bool IsColor32(TypeReference type)
{
return type.IsAssignableTo(Color32);
}
//Do NOT remove these, cil2as still depends on these in 4.x
public static bool IsMatrix4x4(TypeReference type)
{
return type.IsAssignableTo(Matrix4x4);
}
public static bool IsGradient(TypeReference type)
{
return type.IsAssignableTo(Gradient);
}
public static bool IsGUIStyle(TypeReference type)
{
return type.IsAssignableTo(GUIStyle);
}
public static bool IsRectOffset(TypeReference type)
{
return type.IsAssignableTo(RectOffset);
}
public static bool IsSerializableUnityClass(TypeReference type)
{
foreach (var unityClasses in serializableClasses)
{
if (type.IsAssignableTo(unityClasses))
return true;
}
return false;
}
public static bool IsSerializableUnityStruct(TypeReference type)
{
foreach (var unityStruct in serializableStructs)
{
// NOTE: structs cannot inherit from structs, and can only inherit from interfaces
// since we know all types in serializableStructs are not interfaces,
// we can just do a direct comparison.
if (type.FullName == unityStruct)
return true;
}
if (type.FullName.IndexOf("UnityEngine.LazyLoadReference`1") == 0)
return true;
return false;
}
public static bool IsUnityEngineObject(TypeReference type)
{
//todo: somehow solve this elegantly. CheckedResolve() drops the [] of a type.
if (type.IsArray)
return false;
if (type.FullName == UnityEngineObject)
return true;
var typeDefinition = type.Resolve();
if (typeDefinition == null)
return false;
return typeDefinition.IsSubclassOf(UnityEngineObject);
}
public static bool ShouldHaveHadSerializableAttribute(TypeReference type)
{
return IsUnityEngineValueType(type);
}
public static bool IsUnityEngineValueType(TypeReference type)
{
return type.SafeNamespace() == "UnityEngine" && TypesThatShouldHaveHadSerializableAttribute.Contains(type.Name);
}
public static bool IsSerializeFieldAttribute(TypeReference attributeType)
{
return attributeType.FullName == SerializeFieldAttribute;
}
public static bool IsSerializeReferenceAttribute(TypeReference attributeType)
{
return attributeType.FullName == SerializeReferenceAttribute;
}
}
}

View File

@@ -0,0 +1,612 @@
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Collections.Generic;
using Unity.CecilTools;
using Unity.CecilTools.Extensions;
namespace Unity.SerializationLogic
{
internal class GenericInstanceHolder
{
public int Count;
public IGenericInstance GenericInstance;
}
public class TypeResolver
{
private readonly IGenericInstance _typeDefinitionContext;
private readonly IGenericInstance _methodDefinitionContext;
private readonly Dictionary<string, GenericInstanceHolder> _context = new Dictionary<string, GenericInstanceHolder>();
public TypeResolver()
{
}
public TypeResolver(IGenericInstance typeDefinitionContext)
{
_typeDefinitionContext = typeDefinitionContext;
}
public TypeResolver(GenericInstanceMethod methodDefinitionContext)
{
_methodDefinitionContext = methodDefinitionContext;
}
public TypeResolver(IGenericInstance typeDefinitionContext, IGenericInstance methodDefinitionContext)
{
_typeDefinitionContext = typeDefinitionContext;
_methodDefinitionContext = methodDefinitionContext;
}
public void Add(GenericInstanceType genericInstanceType)
{
Add(ElementTypeFor(genericInstanceType).FullName, genericInstanceType);
}
public void Remove(GenericInstanceType genericInstanceType)
{
Remove(genericInstanceType.ElementType.FullName, genericInstanceType);
}
public void Add(GenericInstanceMethod genericInstanceMethod)
{
Add(ElementTypeFor(genericInstanceMethod).FullName, genericInstanceMethod);
}
private static MemberReference ElementTypeFor(TypeSpecification genericInstanceType)
{
return genericInstanceType.ElementType;
}
private static MemberReference ElementTypeFor(MethodSpecification genericInstanceMethod)
{
return genericInstanceMethod.ElementMethod;
}
public void Remove(GenericInstanceMethod genericInstanceMethod)
{
Remove(genericInstanceMethod.ElementMethod.FullName, genericInstanceMethod);
}
public TypeReference Resolve(TypeReference typeReference)
{
var genericParameter = typeReference as GenericParameter;
if (genericParameter != null)
{
var resolved = ResolveGenericParameter(genericParameter);
if (genericParameter == resolved) // Resolving failed, return what we have.
return resolved;
return Resolve(resolved);
}
var arrayType = typeReference as ArrayType;
if (arrayType != null)
return new ArrayType(Resolve(arrayType.ElementType), arrayType.Rank);
var pointerType = typeReference as PointerType;
if (pointerType != null)
return new PointerType(Resolve(pointerType.ElementType));
var byReferenceType = typeReference as ByReferenceType;
if (byReferenceType != null)
return new ByReferenceType(Resolve(byReferenceType.ElementType));
var genericInstanceType = typeReference as GenericInstanceType;
if (genericInstanceType != null)
{
var newGenericInstanceType = new GenericInstanceType(Resolve(genericInstanceType.ElementType));
foreach (var genericArgument in genericInstanceType.GenericArguments)
newGenericInstanceType.GenericArguments.Add(Resolve(genericArgument));
return newGenericInstanceType;
}
var pinnedType = typeReference as PinnedType;
if (pinnedType != null)
return new PinnedType(Resolve(pinnedType.ElementType));
var reqModifierType = typeReference as RequiredModifierType;
if (reqModifierType != null)
return Resolve(reqModifierType.ElementType);
var optModifierType = typeReference as OptionalModifierType;
if (optModifierType != null)
return new OptionalModifierType(Resolve(optModifierType.ModifierType), Resolve(optModifierType.ElementType));
var sentinelType = typeReference as SentinelType;
if (sentinelType != null)
return new SentinelType(Resolve(sentinelType.ElementType));
var funcPtrType = typeReference as FunctionPointerType;
if (funcPtrType != null)
throw new NotSupportedException("Function pointer types are not supported by the SerializationWeaver");
if (typeReference is TypeSpecification)
throw new NotSupportedException();
return typeReference;
}
private TypeReference ResolveGenericParameter(GenericParameter genericParameter)
{
if (genericParameter.Owner == null)
throw new NotSupportedException();
var memberReference = genericParameter.Owner as MemberReference;
if (memberReference == null)
throw new NotSupportedException();
var key = memberReference.FullName;
if (!_context.ContainsKey(key))
{
if (genericParameter.Type == GenericParameterType.Type)
{
if (_typeDefinitionContext != null)
return _typeDefinitionContext.GenericArguments[genericParameter.Position];
return genericParameter;
}
if (_methodDefinitionContext != null)
return _methodDefinitionContext.GenericArguments[genericParameter.Position];
return genericParameter;
}
return GenericArgumentAt(key, genericParameter.Position);
}
private TypeReference GenericArgumentAt(string key, int position)
{
return _context[key].GenericInstance.GenericArguments[position];
}
private void Add(string key, IGenericInstance value)
{
GenericInstanceHolder oldValue;
if (_context.TryGetValue(key, out oldValue))
{
var memberReference = value as MemberReference;
if (memberReference == null)
throw new NotSupportedException();
var storedValue = (MemberReference)oldValue.GenericInstance;
if (storedValue.FullName != memberReference.FullName)
throw new ArgumentException("Duplicate key!", "key");
oldValue.Count++;
return;
}
_context.Add(key, new GenericInstanceHolder { Count = 1, GenericInstance = value });
}
private void Remove(string key, IGenericInstance value)
{
GenericInstanceHolder oldValue;
if (_context.TryGetValue(key, out oldValue))
{
var memberReference = value as MemberReference;
if (memberReference == null)
throw new NotSupportedException();
var storedValue = (MemberReference)oldValue.GenericInstance;
if (storedValue.FullName != memberReference.FullName)
throw new ArgumentException("Invalid value!", "value");
oldValue.Count--;
if (oldValue.Count == 0)
_context.Remove(key);
return;
}
throw new ArgumentException("Invalid key!", "key");
}
}
public static class UnitySerializationLogic
{
public static bool WillUnitySerialize(FieldDefinition fieldDefinition)
{
return WillUnitySerialize(fieldDefinition, new TypeResolver(null));
}
public static bool WillUnitySerialize(FieldDefinition fieldDefinition, TypeResolver typeResolver)
{
if (fieldDefinition == null)
return false;
//skip static, const and NotSerialized fields before even checking the type
if (fieldDefinition.IsStatic || IsConst(fieldDefinition) || fieldDefinition.IsNotSerialized || fieldDefinition.IsInitOnly)
return false;
// The field must have correct visibility/decoration to be serialized.
if (!fieldDefinition.IsPublic &&
!ShouldHaveHadAllFieldsPublic(fieldDefinition) &&
!HasSerializeFieldAttribute(fieldDefinition) &&
!HasSerializeReferenceAttribute(fieldDefinition))
return false;
// Don't try to resolve types that come from Windows assembly,
// as serialization weaver will fail to resolve that (due to it being in platform specific SDKs)
if (ShouldNotTryToResolve(fieldDefinition.FieldType))
return false;
if (IsFixedBuffer(fieldDefinition))
return true;
// Resolving types is more complex and slower than checking their names or attributes,
// thus keep those checks below
var typeReference = typeResolver.Resolve(fieldDefinition.FieldType);
//the type of the field must be serializable in the first place.
if (typeReference.MetadataType == MetadataType.String)
return true;
if (typeReference.IsValueType)
return IsValueTypeSerializable(typeReference);
if (typeReference is ArrayType || CecilUtils.IsGenericList(typeReference))
{
if (!HasSerializeReferenceAttribute(fieldDefinition))
return IsSupportedCollection(typeReference);
}
if (!IsReferenceTypeSerializable(typeReference) && !HasSerializeReferenceAttribute(fieldDefinition))
return false;
if (IsDelegate(typeReference))
return false;
return true;
}
private static bool IsDelegate(TypeReference typeReference)
{
return typeReference.IsAssignableTo("System.Delegate");
}
public static bool ShouldFieldBePPtrRemapped(FieldDefinition fieldDefinition)
{
return ShouldFieldBePPtrRemapped(fieldDefinition, new TypeResolver(null));
}
public static bool ShouldFieldBePPtrRemapped(FieldDefinition fieldDefinition, TypeResolver typeResolver)
{
if (!WillUnitySerialize(fieldDefinition, typeResolver))
return false;
return CanTypeContainUnityEngineObjectReference(typeResolver.Resolve(fieldDefinition.FieldType));
}
private static bool CanTypeContainUnityEngineObjectReference(TypeReference typeReference)
{
if (IsUnityEngineObject(typeReference))
return true;
if (typeReference.IsEnum())
return false;
if (IsSerializablePrimitive(typeReference))
return false;
if (IsSupportedCollection(typeReference))
return CanTypeContainUnityEngineObjectReference(CecilUtils.ElementTypeOfCollection(typeReference));
var definition = typeReference.Resolve();
if (definition == null)
return false;
return HasFieldsThatCanContainUnityEngineObjectReferences(definition, new TypeResolver(typeReference as GenericInstanceType));
}
private static bool HasFieldsThatCanContainUnityEngineObjectReferences(TypeDefinition definition, TypeResolver typeResolver)
{
return AllFieldsFor(definition, typeResolver).Where(kv => kv.Value.Resolve(kv.Key.FieldType).Resolve() != definition).Any(kv => CanFieldContainUnityEngineObjectReference(definition, kv.Key, kv.Value));
}
private static IEnumerable<KeyValuePair<FieldDefinition, TypeResolver>> AllFieldsFor(TypeDefinition definition, TypeResolver typeResolver)
{
var baseType = definition.BaseType;
if (baseType != null)
{
var genericBaseInstanceType = baseType as GenericInstanceType;
if (genericBaseInstanceType != null)
typeResolver.Add(genericBaseInstanceType);
foreach (var kv in AllFieldsFor(baseType.Resolve(), typeResolver))
yield return kv;
if (genericBaseInstanceType != null)
typeResolver.Remove(genericBaseInstanceType);
}
foreach (var fieldDefinition in definition.Fields)
yield return new KeyValuePair<FieldDefinition, TypeResolver>(fieldDefinition, typeResolver);
}
private static bool CanFieldContainUnityEngineObjectReference(TypeReference typeReference, FieldDefinition t, TypeResolver typeResolver)
{
if (typeResolver.Resolve(t.FieldType) == typeReference)
return false;
if (!WillUnitySerialize(t, typeResolver))
return false;
if (UnityEngineTypePredicates.IsUnityEngineValueType(typeReference))
return false;
return true;
}
private static bool IsConst(FieldDefinition fieldDefinition)
{
return fieldDefinition.IsLiteral && !fieldDefinition.IsInitOnly;
}
public static bool HasSerializeFieldAttribute(FieldDefinition field)
{
//return FieldAttributes(field).Any(UnityEngineTypePredicates.IsSerializeFieldAttribute);
foreach (var attribute in FieldAttributes(field))
if (UnityEngineTypePredicates.IsSerializeFieldAttribute(attribute))
return true;
return false;
}
public static bool HasSerializeReferenceAttribute(FieldDefinition field)
{
foreach (var attribute in FieldAttributes(field))
if (UnityEngineTypePredicates.IsSerializeReferenceAttribute(attribute))
return true;
return false;
}
private static IEnumerable<TypeReference> FieldAttributes(FieldDefinition field)
{
return field.CustomAttributes.Select(_ => _.AttributeType);
}
public static bool ShouldNotTryToResolve(TypeReference typeReference)
{
var typeReferenceScopeName = typeReference.Scope.Name;
if (typeReferenceScopeName == "Windows")
{
return true;
}
if (typeReferenceScopeName == "mscorlib")
{
var resolved = typeReference.Resolve();
return resolved == null;
}
try
{ // This will throw an exception if typereference thinks it's referencing a .dll,
// but actually there's .winmd file in the current directory. RRW will fix this
// at a later step, so we will not try to resolve this type. This is OK, as any
// type defined in a winmd cannot be serialized.
typeReference.Resolve();
}
catch
{
return true;
}
return false;
}
private static bool IsFieldTypeSerializable(TypeReference typeReference, FieldDefinition fieldDefinition)
{
return IsTypeSerializable(typeReference) || IsSupportedCollection(typeReference) || IsFixedBuffer(fieldDefinition);
}
private static bool IsValueTypeSerializable(TypeReference typeReference)
{
if (typeReference.IsPrimitive)
return IsSerializablePrimitive(typeReference);
return UnityEngineTypePredicates.IsSerializableUnityStruct(typeReference) ||
typeReference.IsEnum() ||
ShouldImplementIDeserializable(typeReference);
}
private static bool IsReferenceTypeSerializable(TypeReference typeReference)
{
if (typeReference.MetadataType == MetadataType.String)
return IsSerializablePrimitive(typeReference);
if (IsGenericDictionary(typeReference))
return false;
if (IsUnityEngineObject(typeReference) ||
ShouldImplementIDeserializable(typeReference) ||
UnityEngineTypePredicates.IsSerializableUnityClass(typeReference))
return true;
return false;
}
private static bool IsTypeSerializable(TypeReference typeReference)
{
if (typeReference.MetadataType == MetadataType.String)
return true;
if (typeReference.IsValueType)
return IsValueTypeSerializable(typeReference);
return IsReferenceTypeSerializable(typeReference);
}
private static bool IsGenericDictionary(TypeReference typeReference)
{
var current = typeReference;
if (current != null)
{
if (CecilUtils.IsGenericDictionary(current))
return true;
}
return false;
}
public static bool IsFixedBuffer(FieldDefinition fieldDefinition)
{
return GetFixedBufferAttribute(fieldDefinition) != null;
}
public static CustomAttribute GetFixedBufferAttribute(FieldDefinition fieldDefinition)
{
if (!fieldDefinition.HasCustomAttributes)
return null;
return fieldDefinition.CustomAttributes.SingleOrDefault(a => a.AttributeType.FullName == "System.Runtime.CompilerServices.FixedBufferAttribute");
}
public static int GetFixedBufferLength(FieldDefinition fieldDefinition)
{
var fixedBufferAttribute = GetFixedBufferAttribute(fieldDefinition);
if (fixedBufferAttribute == null)
throw new ArgumentException(string.Format("Field '{0}' is not a fixed buffer field.", fieldDefinition.FullName));
var size = (Int32)fixedBufferAttribute.ConstructorArguments[1].Value;
return size;
}
public static int PrimitiveTypeSize(TypeReference type)
{
switch (type.MetadataType)
{
case MetadataType.Boolean:
case MetadataType.Byte:
case MetadataType.SByte:
return 1;
case MetadataType.Char:
case MetadataType.Int16:
case MetadataType.UInt16:
return 2;
case MetadataType.Int32:
case MetadataType.UInt32:
case MetadataType.Single:
return 4;
case MetadataType.Int64:
case MetadataType.UInt64:
case MetadataType.Double:
return 8;
default:
throw new ArgumentException(string.Format("Unsupported {0}", type.MetadataType));
}
}
private static bool IsSerializablePrimitive(TypeReference typeReference)
{
switch (typeReference.MetadataType)
{
case MetadataType.SByte:
case MetadataType.Byte:
case MetadataType.Char:
case MetadataType.Int16:
case MetadataType.UInt16:
case MetadataType.Int64:
case MetadataType.UInt64:
case MetadataType.Int32:
case MetadataType.UInt32:
case MetadataType.Single:
case MetadataType.Double:
case MetadataType.Boolean:
case MetadataType.String:
return true;
}
return false;
}
public static bool IsSupportedCollection(TypeReference typeReference)
{
if (!(typeReference is ArrayType || CecilUtils.IsGenericList(typeReference)))
return false;
// We don't support arrays like byte[,] etc
if (typeReference.IsArray && ((ArrayType)typeReference).Rank > 1)
return false;
return IsTypeSerializable(CecilUtils.ElementTypeOfCollection(typeReference));
}
private static bool ShouldHaveHadAllFieldsPublic(FieldDefinition field)
{
return UnityEngineTypePredicates.IsUnityEngineValueType(field.DeclaringType);
}
private static bool IsUnityEngineObject(TypeReference typeReference)
{
return UnityEngineTypePredicates.IsUnityEngineObject(typeReference);
}
public static bool IsNonSerialized(TypeReference typeDeclaration)
{
if (typeDeclaration == null)
return true;
if (typeDeclaration.HasGenericParameters)
return true;
if (typeDeclaration.MetadataType == MetadataType.Object)
return true;
var fullName = typeDeclaration.FullName;
if (fullName.StartsWith("System.")) //can this be done better?
return true;
if (typeDeclaration.IsArray)
return true;
if (typeDeclaration.FullName == UnityEngineTypePredicates.MonoBehaviour)
return true;
if (typeDeclaration.FullName == UnityEngineTypePredicates.ScriptableObject)
return true;
if (typeDeclaration.IsEnum())
return true;
return false;
}
public static bool ShouldImplementIDeserializable(TypeReference typeDeclaration)
{
if (typeDeclaration.FullName == "UnityEngine.ExposedReference`1")
return true;
if (IsNonSerialized(typeDeclaration))
return false;
try
{
if (UnityEngineTypePredicates.ShouldHaveHadSerializableAttribute(typeDeclaration))
return true;
var resolvedTypeDeclaration = typeDeclaration.CheckedResolve();
if (resolvedTypeDeclaration.IsValueType)
{
return resolvedTypeDeclaration.IsSerializable && !resolvedTypeDeclaration.CustomAttributes.Any(a => a.AttributeType.FullName.Contains("System.Runtime.CompilerServices.CompilerGenerated"));
}
else
{
return (resolvedTypeDeclaration.IsSerializable && !resolvedTypeDeclaration.CustomAttributes.Any(a => a.AttributeType.FullName.Contains("System.Runtime.CompilerServices.CompilerGenerated"))) ||
resolvedTypeDeclaration.IsSubclassOf(UnityEngineTypePredicates.MonoBehaviour, UnityEngineTypePredicates.ScriptableObject);
}
}
catch (Exception)
{
return false;
}
}
}
}