AssetRipper_AssetRipper/Source/SpirV/ParsedInstruction.cs
2025-06-23 11:42:47 -07:00

265 lines
5.6 KiB
C#

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 uint GetBitEnumValue()
{
IBitEnumOperandValue v = (IBitEnumOperandValue)Value;
uint result = 0;
foreach (uint k in v.Values.Keys)
{
result |= k;
}
return 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();
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
{
string? GetEnumName(uint value);
}
public interface IBitEnumOperandValue : IEnumOperandValue
{
IReadOnlyDictionary<uint, IReadOnlyList<object>> Values { get; }
}
public interface IValueEnumOperandValue : IEnumOperandValue
{
object Key { get; }
IReadOnlyList<object> Value { get; }
}
public abstract class EnumOperandValue<T> : IEnumOperandValue
where T : unmanaged, Enum
{
public string? GetEnumName(uint value) => Enum.GetName<T>(value.AsEnum<T>());
}
public class ValueEnumOperandValue<T> : EnumOperandValue<T>, IValueEnumOperandValue
where T : unmanaged, Enum
{
public ValueEnumOperandValue(T key, IReadOnlyList<object> value)
{
Key = key;
Value = value;
}
public object Key { get; }
public IReadOnlyList<object> Value { get; }
}
public class BitEnumOperandValue<T> : EnumOperandValue<T>, IBitEnumOperandValue
where T : unmanaged, Enum
{
public BitEnumOperandValue(Dictionary<uint, IReadOnlyList<object>> values)
{
Values = values;
}
public IReadOnlyDictionary<uint, IReadOnlyList<object>> Values { get; }
}
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();
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 (ParsedOperand 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; }
}