mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2024-11-22 23:12:59 +08:00
Synth disassembly in XML instead of bytecode
This commit is contained in:
parent
8d257f6045
commit
e76e854ead
@ -6647,7 +6647,6 @@ namespace CodeWalker.GameFiles
|
|||||||
TrackCount = (uint)(Tracks?.Length ?? 0);
|
TrackCount = (uint)(Tracks?.Length ?? 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class Dat151WeaponAudioItem : Dat151RelData
|
[TC(typeof(EXP))] public class Dat151WeaponAudioItem : Dat151RelData
|
||||||
{
|
{
|
||||||
public MetaHash FallBackWeapon { get; set; }
|
public MetaHash FallBackWeapon { get; set; }
|
||||||
@ -6707,8 +6706,7 @@ namespace CodeWalker.GameFiles
|
|||||||
return offsets.ToArray();
|
return offsets.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[TC(typeof(EXP))]
|
[TC(typeof(EXP))] public struct Dat151WeaponAudioItemItem : IMetaXmlItem
|
||||||
public struct Dat151WeaponAudioItemItem : IMetaXmlItem
|
|
||||||
{
|
{
|
||||||
public MetaHash Category { get; set; }
|
public MetaHash Category { get; set; }
|
||||||
public MetaHash Weapon { get; set; }
|
public MetaHash Weapon { get; set; }
|
||||||
@ -6738,7 +6736,6 @@ namespace CodeWalker.GameFiles
|
|||||||
return Category.ToString() + ": " + Weapon.ToString();
|
return Category.ToString() + ": " + Weapon.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class Dat151StartTrackAction : Dat151RelData
|
[TC(typeof(EXP))] public class Dat151StartTrackAction : Dat151RelData
|
||||||
{
|
{
|
||||||
public FlagsUint Unk0 { get; set; }
|
public FlagsUint Unk0 { get; set; }
|
||||||
@ -6859,9 +6856,7 @@ namespace CodeWalker.GameFiles
|
|||||||
return new uint[] { 12, 16, 28 };
|
return new uint[] { 12, 16, 28 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
[TC(typeof(EXP))] public struct Dat151StartTrackActionItem : IMetaXmlItem
|
||||||
[TC(typeof(EXP))]
|
|
||||||
public struct Dat151StartTrackActionItem : IMetaXmlItem
|
|
||||||
{
|
{
|
||||||
public MetaHash Track { get; set; }
|
public MetaHash Track { get; set; }
|
||||||
public FlagsUint Flags { get; set; }
|
public FlagsUint Flags { get; set; }
|
||||||
@ -6891,7 +6886,6 @@ namespace CodeWalker.GameFiles
|
|||||||
return Track.ToString() + ": " + Flags.ToString();
|
return Track.ToString() + ": " + Flags.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class Dat151StopTrackAction : Dat151RelData
|
[TC(typeof(EXP))] public class Dat151StopTrackAction : Dat151RelData
|
||||||
{
|
{
|
||||||
public FlagsUint Unk0 { get; set; }
|
public FlagsUint Unk0 { get; set; }
|
||||||
@ -9237,7 +9231,6 @@ namespace CodeWalker.GameFiles
|
|||||||
return offsets.ToArray();
|
return offsets.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class Dat151ShoreLinePool : Dat151RelData
|
[TC(typeof(EXP))] public class Dat151ShoreLinePool : Dat151RelData
|
||||||
{
|
{
|
||||||
public FlagsUint Unk01 { get; set; }
|
public FlagsUint Unk01 { get; set; }
|
||||||
@ -9678,7 +9671,6 @@ namespace CodeWalker.GameFiles
|
|||||||
ShoreLineCount = (uint)(ShoreLines?.Length ?? 0);
|
ShoreLineCount = (uint)(ShoreLines?.Length ?? 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class Dat151RadioTrackEvents : Dat151RelData
|
[TC(typeof(EXP))] public class Dat151RadioTrackEvents : Dat151RelData
|
||||||
{
|
{
|
||||||
public uint EventCount { get; set; }
|
public uint EventCount { get; set; }
|
||||||
@ -11800,7 +11792,6 @@ namespace CodeWalker.GameFiles
|
|||||||
Unk07 = (byte)Xml.GetChildUIntAttribute(node, "Unk07", "value");
|
Unk07 = (byte)Xml.GetChildUIntAttribute(node, "Unk07", "value");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class Dat151EntityEmitter : Dat151RelData
|
[TC(typeof(EXP))] public class Dat151EntityEmitter : Dat151RelData
|
||||||
{
|
{
|
||||||
public FlagsUint Flags { get; set; }
|
public FlagsUint Flags { get; set; }
|
||||||
@ -13525,7 +13516,6 @@ namespace CodeWalker.GameFiles
|
|||||||
Unk12 = Xml.GetChildFloatAttribute(node, "Unk12", "value");
|
Unk12 = Xml.GetChildFloatAttribute(node, "Unk12", "value");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class Dat151Train : Dat151RelData
|
[TC(typeof(EXP))] public class Dat151Train : Dat151RelData
|
||||||
{
|
{
|
||||||
public MetaHash Unk01 { get; set; }
|
public MetaHash Unk01 { get; set; }
|
||||||
@ -14012,7 +14002,6 @@ namespace CodeWalker.GameFiles
|
|||||||
Unk07 = Xml.GetChildIntAttribute(node, "Unk07", "value");
|
Unk07 = Xml.GetChildIntAttribute(node, "Unk07", "value");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TC(typeof(EXP))] public class Dat151MeleeCombat : Dat151RelData
|
[TC(typeof(EXP))] public class Dat151MeleeCombat : Dat151RelData
|
||||||
{
|
{
|
||||||
public MetaHash Unk01 { get; set; }
|
public MetaHash Unk01 { get; set; }
|
||||||
@ -19375,9 +19364,7 @@ namespace CodeWalker.GameFiles
|
|||||||
ItemCount = (Items?.Length ?? 0);
|
ItemCount = (Items?.Length ?? 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
[TC(typeof(EXP))] public class Dat151Unk124 : Dat151RelData
|
||||||
[TC(typeof(EXP))]
|
|
||||||
public class Dat151Unk124 : Dat151RelData
|
|
||||||
{
|
{
|
||||||
public FlagsUint Flags { get; set; }
|
public FlagsUint Flags { get; set; }
|
||||||
public uint ItemCount { get; set; }
|
public uint ItemCount { get; set; }
|
||||||
@ -20515,68 +20502,191 @@ namespace CodeWalker.GameFiles
|
|||||||
public override void WriteXml(StringBuilder sb, int indent)
|
public override void WriteXml(StringBuilder sb, int indent)
|
||||||
{
|
{
|
||||||
RelXml.ValueTag(sb, indent, "Flags", "0x" + Flags.Hex);
|
RelXml.ValueTag(sb, indent, "Flags", "0x" + Flags.Hex);
|
||||||
RelXml.ValueTag(sb, indent, "BuffersCount", BuffersCount.ToString());
|
|
||||||
RelXml.ValueTag(sb, indent, "RegistersCount", RegistersCount.ToString());
|
|
||||||
RelXml.ValueTag(sb, indent, "OutputsCount", OutputsCount.ToString());
|
|
||||||
RelXml.WriteRawArray(sb, OutputsIndices, indent, "OutputsIndices", "", arrRowSize: 4);
|
|
||||||
RelXml.ValueTag(sb, indent, "StateBlocksCount", StateBlocksCount.ToString());
|
|
||||||
RelXml.ValueTag(sb, indent, "RuntimeCost", RuntimeCost.ToString());
|
|
||||||
|
|
||||||
//string disassembly = Disassemble(Bytecode, Constants, true);
|
|
||||||
|
|
||||||
//RelXml.OpenTag(sb, indent, "Assembly");
|
|
||||||
//var reader = new StringReader(disassembly);
|
|
||||||
//string line;
|
|
||||||
//while ((line = reader.ReadLine()) != null)
|
|
||||||
//{
|
|
||||||
// RelXml.Indent(sb, indent + 1);
|
|
||||||
// sb.AppendLine(line);
|
|
||||||
//}
|
|
||||||
//RelXml.CloseTag(sb, indent, "Assembly");
|
|
||||||
|
|
||||||
RelXml.WriteRawArray(sb, Bytecode, indent, "Bytecode", "", RelXml.FormatHexByte, 16);
|
|
||||||
RelXml.WriteRawArray(sb, Constants, indent, "Constants", "", FloatUtil.ToString, 1);
|
|
||||||
RelXml.WriteItemArray(sb, Variables, indent, "Variables");
|
RelXml.WriteItemArray(sb, Variables, indent, "Variables");
|
||||||
|
|
||||||
|
var disasm = Disassemble(Bytecode, Constants, Variables, false);
|
||||||
|
var reasm = Assemble(disasm.Disassembly, Variables);//TODO: adjust disassembly code to make this not necessary
|
||||||
|
RelXml.OpenTag(sb, indent, "Assembly");
|
||||||
|
var reader = new StringReader(disasm.Disassembly);
|
||||||
|
string line;
|
||||||
|
while ((line = reader.ReadLine()) != null)
|
||||||
|
{
|
||||||
|
RelXml.Indent(sb, indent + 1);
|
||||||
|
sb.AppendLine(line);
|
||||||
|
}
|
||||||
|
RelXml.CloseTag(sb, indent, "Assembly");
|
||||||
|
|
||||||
|
var outputs = new List<byte>();
|
||||||
|
for (int i = 0; i < OutputsCount; i++)
|
||||||
|
{
|
||||||
|
if (i >= OutputsIndices.Length) break;
|
||||||
|
outputs.Add(OutputsIndices[i]);
|
||||||
|
}
|
||||||
|
RelXml.WriteRawArray(sb, outputs.ToArray(), indent, "OutputBuffers", "", arrRowSize: 4);
|
||||||
|
|
||||||
|
var extraConstants = new List<float>();
|
||||||
|
for (int i = reasm.Constants.Count; i < ConstantsCount; i++)
|
||||||
|
{
|
||||||
|
extraConstants.Add(Constants[i]);
|
||||||
|
}
|
||||||
|
if (extraConstants.Count > 0)
|
||||||
|
{
|
||||||
|
RelXml.WriteRawArray(sb, extraConstants.ToArray(), indent, "ExtraConstants", "", FloatUtil.ToString, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
var extraRegisters = RegistersCount - reasm.RegistersCount;
|
||||||
|
if (extraRegisters > 0)
|
||||||
|
{
|
||||||
|
RelXml.ValueTag(sb, indent, "ExtraRegisters", extraRegisters.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
RelXml.ValueTag(sb, indent, "RuntimeCost", RuntimeCost.ToString());//TODO: calculate this during reassembly
|
||||||
|
|
||||||
}
|
}
|
||||||
public override void ReadXml(XmlNode node)
|
public override void ReadXml(XmlNode node)
|
||||||
{
|
{
|
||||||
Flags = Xml.GetChildUIntAttribute(node, "Flags", "value");
|
Flags = Xml.GetChildUIntAttribute(node, "Flags", "value");
|
||||||
BuffersCount = Xml.GetChildIntAttribute(node, "BuffersCount", "value");
|
|
||||||
RegistersCount = Xml.GetChildIntAttribute(node, "RegistersCount", "value");
|
|
||||||
OutputsCount = Xml.GetChildIntAttribute(node, "OutputsCount", "value");
|
|
||||||
OutputsIndices = Xml.GetChildRawByteArray(node, "OutputsIndices", fromBase: 10);
|
|
||||||
StateBlocksCount = Xml.GetChildIntAttribute(node, "StateBlocksCount", "value");
|
|
||||||
RuntimeCost = Xml.GetChildIntAttribute(node, "RuntimeCost", "value");
|
|
||||||
|
|
||||||
//var assembly = Xml.GetChildInnerText(node, "Assembly");
|
|
||||||
//var assembled = Assemble(assembly);
|
|
||||||
//Bytecode = assembled.Bytecode;
|
|
||||||
//Constants = assembled.Constants.ToArray();
|
|
||||||
|
|
||||||
Bytecode = Xml.GetChildRawByteArray(node, "Bytecode");
|
|
||||||
BytecodeLength = (Bytecode?.Length ?? 0);
|
|
||||||
Constants = Xml.GetChildRawFloatArray(node, "Constants");
|
|
||||||
ConstantsCount = (Constants?.Length ?? 0);
|
|
||||||
Variables = XmlRel.ReadItemArray<Dat10SynthVariable>(node, "Variables");
|
Variables = XmlRel.ReadItemArray<Dat10SynthVariable>(node, "Variables");
|
||||||
VariablesCount = (Variables?.Length ?? 0);
|
VariablesCount = (Variables?.Length ?? 0);
|
||||||
|
|
||||||
|
var assembly = Xml.GetChildInnerText(node, "Assembly");
|
||||||
|
var assembled = Assemble(assembly, Variables);
|
||||||
|
Bytecode = assembled.Bytecode;
|
||||||
|
BytecodeLength = (Bytecode?.Length ?? 0);
|
||||||
|
BuffersCount = assembled.BuffersCount;
|
||||||
|
StateBlocksCount = assembled.StateBlocksCount;
|
||||||
|
|
||||||
|
var outputs = Xml.GetChildRawByteArray(node, "OutputBuffers", fromBase: 10).ToList();
|
||||||
|
OutputsCount = outputs.Count;
|
||||||
|
for (int i = OutputsCount; i < 4; i++)
|
||||||
|
{
|
||||||
|
outputs.Add(0);
|
||||||
|
}
|
||||||
|
OutputsIndices = outputs.ToArray();
|
||||||
|
|
||||||
|
|
||||||
|
var extraConstants = Xml.GetChildRawFloatArray(node, "ExtraConstants");
|
||||||
|
foreach (var extraConstant in extraConstants)
|
||||||
|
{
|
||||||
|
assembled.Constants.Add(extraConstant);
|
||||||
|
}
|
||||||
|
Constants = assembled.Constants.ToArray();
|
||||||
|
ConstantsCount = (Constants?.Length ?? 0);
|
||||||
|
|
||||||
|
var extraRegisters = Xml.GetChildIntAttribute(node, "ExtraRegisters", "value");
|
||||||
|
RegistersCount = assembled.RegistersCount + extraRegisters;
|
||||||
|
|
||||||
|
RuntimeCost = Xml.GetChildIntAttribute(node, "RuntimeCost", "value");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string Disassemble(byte[] bytecode, float[] constants, bool includeBytecode)
|
|
||||||
|
public void TestDisassembly()
|
||||||
{
|
{
|
||||||
|
var errors = new List<string>();
|
||||||
|
var disasm = Disassemble(this.Bytecode, this.Constants, this.Variables, true);
|
||||||
|
var reasm = Assemble(disasm.Disassembly, this.Variables, (s,i) => errors.Add(s));
|
||||||
|
var disasm2 = Disassemble(reasm.Bytecode, reasm.Constants.ToArray(), this.Variables, true);
|
||||||
|
|
||||||
|
if (errors.Count > 0)
|
||||||
|
{ }//no hit
|
||||||
|
if (disasm.Disassembly != disasm2.Disassembly)
|
||||||
|
{ } //no hit!
|
||||||
|
if (reasm.Bytecode.Length != Bytecode.Length)
|
||||||
|
{ }//no hit
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < reasm.Bytecode.Length; i++)
|
||||||
|
{
|
||||||
|
if (reasm.Bytecode[i] != Bytecode[i])
|
||||||
|
{ break; }//no hit!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (reasm.BuffersCount != this.BuffersCount)
|
||||||
|
{ }//no hit
|
||||||
|
if (reasm.StateBlocksCount != this.StateBlocksCount)
|
||||||
|
{ }//no hit
|
||||||
|
if (reasm.Constants.Count <= Constants.Length)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < reasm.Constants.Count; i++)
|
||||||
|
{
|
||||||
|
if (reasm.Constants[i] != Constants[i])
|
||||||
|
{ break; }//no hit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (reasm.Constants.Count != Constants.Length)
|
||||||
|
{
|
||||||
|
if (reasm.Constants.Count != Constants.Length - 1)
|
||||||
|
{ }//no hit
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var lastc = Constants[Constants.Length - 1];
|
||||||
|
switch (lastc) //what is this?
|
||||||
|
{
|
||||||
|
case 0.0015f:
|
||||||
|
case 0.3595f:
|
||||||
|
case 0.323215f:
|
||||||
|
case 0.4435f:
|
||||||
|
case 0.5095f:
|
||||||
|
case 12000f:
|
||||||
|
case 3.590499f:
|
||||||
|
case 0.829645f:
|
||||||
|
case 0.5745f:
|
||||||
|
case -1f:
|
||||||
|
case 0.01f:
|
||||||
|
case 0.561f:
|
||||||
|
case 1.0754323f:
|
||||||
|
case 0.1845f:
|
||||||
|
case 0.508f:
|
||||||
|
case 0.006f:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;//no hit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (reasm.VariablesCount != this.VariablesCount)
|
||||||
|
{ }//lots of hits.......
|
||||||
|
if (reasm.RegistersCount != this.RegistersCount)
|
||||||
|
{//quite a few hits
|
||||||
|
var extregs = this.RegistersCount - reasm.RegistersCount;
|
||||||
|
switch (extregs)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;//no hits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class DisassembleResult
|
||||||
|
{
|
||||||
|
public string Disassembly { get; set; }
|
||||||
|
public List<Instruction> Instructions { get; set; } = new List<Instruction>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DisassembleResult Disassemble(byte[] bytecode, float[] constants, Dat10SynthVariable[] variables, bool includeBytecode)
|
||||||
|
{
|
||||||
|
// TODO(alexguirre): THE NEW bytecodeReader DOESN'T LOOK QUITE RIGHT YET
|
||||||
|
var bytecodeReader = new BinaryReader(new MemoryStream(bytecode));
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
|
var dr = new DisassembleResult();
|
||||||
for (int i = 0; i < bytecode.Length;)
|
for (int i = 0; i < bytecode.Length;)
|
||||||
{
|
{
|
||||||
var inputsOutputs = bytecode[i + 0];
|
|
||||||
var outputs = inputsOutputs & 0xF;
|
|
||||||
var inputs = inputsOutputs >> 4;
|
|
||||||
var opcode = bytecode[i + 1];
|
var opcode = bytecode[i + 1];
|
||||||
|
|
||||||
if (Enum.IsDefined(typeof(Opcode), (Opcode)opcode))
|
if (Enum.IsDefined(typeof(Opcode), (Opcode)opcode))
|
||||||
{
|
{
|
||||||
var size = SizeOf((Opcode)opcode, inputs, outputs);
|
var inst = Decode(bytecodeReader);
|
||||||
var inst = Decode(new BinaryReader(new MemoryStream(bytecode, i, size)));
|
var size = inst.Size;
|
||||||
|
|
||||||
sb.Append(inst.ToString(constants));
|
sb.Append(inst.ToString(constants, variables));
|
||||||
if (includeBytecode)
|
if (includeBytecode)
|
||||||
{
|
{
|
||||||
sb.Append(" ; ");
|
sb.Append(" ; ");
|
||||||
@ -20588,6 +20698,8 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
i += size;
|
i += size;
|
||||||
|
|
||||||
|
dr.Instructions.Add(inst);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -20607,7 +20719,38 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.ToString();
|
dr.Disassembly = sb.ToString();
|
||||||
|
|
||||||
|
return dr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Instruction[] DisassembleGetInstructions(byte[] bytecode, float[] constants, Dat10SynthVariable[] variables)
|
||||||
|
{
|
||||||
|
// TODO(alexguirre): THE NEW bytecodeReader DOESN'T LOOK QUITE RIGHT YET
|
||||||
|
var bytecodeReader = new BinaryReader(new MemoryStream(bytecode));
|
||||||
|
var instructions = new List<Instruction>(128);
|
||||||
|
for (int i = 0; i < bytecode.Length;)
|
||||||
|
{
|
||||||
|
var opcode = bytecode[i + 1];
|
||||||
|
|
||||||
|
if (Enum.IsDefined(typeof(Opcode), (Opcode)opcode))
|
||||||
|
{
|
||||||
|
var inst = Decode(bytecodeReader);
|
||||||
|
instructions.Add(inst);
|
||||||
|
i += inst.Size;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception($"Unknown opcode {opcode:X2}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((Opcode)opcode == Opcode.FINISH)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return instructions.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int SizeOf(Opcode opcode, int inputs, int outputs)
|
public static int SizeOf(Opcode opcode, int inputs, int outputs)
|
||||||
@ -20753,10 +20896,10 @@ namespace CodeWalker.GameFiles
|
|||||||
case Opcode.AWProcess: return 10;
|
case Opcode.AWProcess: return 10;
|
||||||
case Opcode.LERP_BUFFER_BUFFER: return 8;
|
case Opcode.LERP_BUFFER_BUFFER: return 8;
|
||||||
|
|
||||||
case Opcode.BiquadCoefficients_HighSelf_1:
|
case Opcode.BiquadCoefficients_HighShelf_1:
|
||||||
case Opcode.BiquadCoefficients_HighSelf_2:
|
case Opcode.BiquadCoefficients_HighShelf_2:
|
||||||
case Opcode.BiquadCoefficients_LowSelf_1:
|
case Opcode.BiquadCoefficients_LowShelf_1:
|
||||||
case Opcode.BiquadCoefficients_LowSelf_2:
|
case Opcode.BiquadCoefficients_LowShelf_2:
|
||||||
return 18;
|
return 18;
|
||||||
|
|
||||||
case Opcode.SWITCH_NORM_BUFFER:
|
case Opcode.SWITCH_NORM_BUFFER:
|
||||||
@ -20790,7 +20933,7 @@ namespace CodeWalker.GameFiles
|
|||||||
public List<float> Constants { get; set; } = new List<float>();
|
public List<float> Constants { get; set; } = new List<float>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AssembleResult Assemble(string assembly, Action<string> onError = null)
|
public static AssembleResult Assemble(string assembly, Dat10SynthVariable[] variables, Action<string, int> onError = null)
|
||||||
{
|
{
|
||||||
var result = new AssembleResult();
|
var result = new AssembleResult();
|
||||||
|
|
||||||
@ -20799,10 +20942,12 @@ namespace CodeWalker.GameFiles
|
|||||||
using (var bw = new BinaryWriter(mem))
|
using (var bw = new BinaryWriter(mem))
|
||||||
{
|
{
|
||||||
string line;
|
string line;
|
||||||
|
int lineNumber = 1;
|
||||||
while ((line = reader.ReadLine()) != null)
|
while ((line = reader.ReadLine()) != null)
|
||||||
{
|
{
|
||||||
var inst = Parse(line, result, onError ?? (_ => {}));
|
var inst = Parse(line, result, variables, msg => onError?.Invoke(msg, lineNumber));
|
||||||
inst?.Encode(bw);
|
inst?.Encode(bw);
|
||||||
|
lineNumber++;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int Align = 4;
|
const int Align = 4;
|
||||||
@ -20814,7 +20959,7 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Instruction? Parse(string line, AssembleResult result, Action<string> onError)
|
private static Instruction? Parse(string line, AssembleResult result, Dat10SynthVariable[] variables, Action<string> onError)
|
||||||
{
|
{
|
||||||
var commentStart = line.IndexOf(';');
|
var commentStart = line.IndexOf(';');
|
||||||
if (commentStart != -1)
|
if (commentStart != -1)
|
||||||
@ -20830,7 +20975,7 @@ namespace CodeWalker.GameFiles
|
|||||||
}
|
}
|
||||||
|
|
||||||
var opcodeName = line.Substring(0, line.TakeWhile(c => !char.IsWhiteSpace(c)).Count());
|
var opcodeName = line.Substring(0, line.TakeWhile(c => !char.IsWhiteSpace(c)).Count());
|
||||||
|
|
||||||
if (!Enum.TryParse<Opcode>(opcodeName, out var opcode))
|
if (!Enum.TryParse<Opcode>(opcodeName, out var opcode))
|
||||||
{
|
{
|
||||||
onError($"Unknown opcode '{opcodeName}'");
|
onError($"Unknown opcode '{opcodeName}'");
|
||||||
@ -20855,25 +21000,25 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
var stateBlockEnd = parts[1].LastIndexOf(']');
|
var stateBlockEnd = parts[1].LastIndexOf(']');
|
||||||
var stateBlockStart = parts[1].IndexOf('[');
|
var stateBlockStart = parts[1].IndexOf('[');
|
||||||
string stateBlockStr = null;
|
|
||||||
var hasStateBlock = stateBlockStart != -1;
|
|
||||||
var stateBlockUsed = false;
|
|
||||||
|
|
||||||
if ((stateBlockStart == -1) != (stateBlockEnd == -1))
|
if (stateBlockStart > stateBlockEnd)
|
||||||
{
|
{
|
||||||
onError("Mismatched brackets");
|
onError("Mismatched state block brackets");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (stateBlockEnd != -1 && stateBlockEnd != parts[1].Length - 1)
|
||||||
{
|
{
|
||||||
stateBlockStr = hasStateBlock ? parts[1].Substring(stateBlockStart + 1, stateBlockEnd - stateBlockStart - 1) : null;
|
onError($"Stuff after state block... '{parts[1].Substring(stateBlockEnd + 1)}'");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var stateBlockUsed = false;
|
||||||
|
var hasStateBlock = stateBlockStart != -1;
|
||||||
|
var stateBlockStr = hasStateBlock ? parts[1].Substring(stateBlockStart + 1, stateBlockEnd - stateBlockStart - 1) : null;
|
||||||
|
|
||||||
var outputs = parts[1].Substring(0, hasStateBlock ? stateBlockStart : parts[1].Length)
|
var outputs = parts[1].Substring(0, hasStateBlock ? stateBlockStart : parts[1].Length)
|
||||||
.Split(',')
|
.Split(',').Select(s => s.Trim()).Where(s => !string.IsNullOrWhiteSpace(s)).ToArray();
|
||||||
.Select(s => s.Trim())
|
|
||||||
.Where(s => !string.IsNullOrWhiteSpace(s))
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
var currInputIndex = 0;
|
var currInputIndex = 0;
|
||||||
var currOutputIndex = 0;
|
var currOutputIndex = 0;
|
||||||
@ -20881,55 +21026,74 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
void Param(ParameterType type)
|
void Param(ParameterType type)
|
||||||
{
|
{
|
||||||
var currInput = Parameter.IsInputType(type) ? inputs[currInputIndex] : null;
|
var currInput = Parameter.IsInputType(type) && currInputIndex < inputs.Length ? inputs[currInputIndex] : null;
|
||||||
var currOutput = Parameter.IsOutputType(type) ? outputs[currOutputIndex] : null;
|
var currOutput = Parameter.IsOutputType(type) && currOutputIndex < outputs.Length ? outputs[currOutputIndex] : null;
|
||||||
ushort paramValue = 0xFFFF;
|
ushort paramValue = 0xFFFF;
|
||||||
|
|
||||||
switch (type)
|
bool parseParameter = true;
|
||||||
|
if (Parameter.IsInputType(type) && currInput == null)
|
||||||
{
|
{
|
||||||
case ParameterType.InputBuffer: paramValue = ParseBuffer(currInput, result, onError); break;
|
onError($"Missing input parameter #{currInputIndex}, expected {Parameter.TypeToStringReadable(type)}");
|
||||||
case ParameterType.InputRegister: paramValue = ParseRegister(currInput, result, onError); break;
|
parseParameter = false;
|
||||||
case ParameterType.InputScalar:
|
}
|
||||||
paramValue = currInput[0] == 'R' ?
|
|
||||||
ParseRegister(currInput, result, onError) :
|
|
||||||
ParseConstant(currInput, result, onError);
|
|
||||||
break;
|
|
||||||
case ParameterType.InputVariable: paramValue = ParseVariable(currInput, result, onError); break;
|
|
||||||
case ParameterType.OutputBuffer: paramValue = ParseBuffer(currOutput, result, onError); break;
|
|
||||||
case ParameterType.OutputRegister: paramValue = ParseRegister(currOutput, result, onError); break;
|
|
||||||
case ParameterType.InputOutputBuffer:
|
|
||||||
if (currInput != currOutput)
|
|
||||||
{
|
|
||||||
onError($"Expected same input/output buffer: input = '{currInput}', output = '{currOutput}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
paramValue = ParseBuffer(currInput, result, onError);
|
if (Parameter.IsOutputType(type) && currOutput == null)
|
||||||
break;
|
{
|
||||||
case ParameterType.StateBlock:
|
onError($"Missing output parameter #{currOutputIndex}, expected {Parameter.TypeToStringReadable(type)}");
|
||||||
if (!hasStateBlock)
|
parseParameter = false;
|
||||||
{
|
}
|
||||||
onError($"Opcode '{opcodeName}' requires a state block");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!byte.TryParse(stateBlockStr, out var stateBlockIndex))
|
if (parseParameter)
|
||||||
{
|
{
|
||||||
onError($"Invalid state block: '{stateBlockStr}' cannot be parsed as an integer");
|
switch (type)
|
||||||
}
|
{
|
||||||
|
case ParameterType.InputBuffer: paramValue = ParseBuffer(currInput, result, onError); break;
|
||||||
|
case ParameterType.InputRegister: paramValue = ParseRegister(currInput, result, onError); break;
|
||||||
|
case ParameterType.InputScalar:
|
||||||
|
paramValue = currInput[0] == 'R' ?
|
||||||
|
ParseRegister(currInput, result, onError) :
|
||||||
|
ParseConstant(currInput, result, onError);
|
||||||
|
break;
|
||||||
|
case ParameterType.InputVariable: paramValue = ParseVariable(currInput, variables, result, onError); break;
|
||||||
|
case ParameterType.OutputBuffer: paramValue = ParseBuffer(currOutput, result, onError); break;
|
||||||
|
case ParameterType.OutputRegister: paramValue = ParseRegister(currOutput, result, onError); break;
|
||||||
|
case ParameterType.InputOutputBuffer:
|
||||||
|
if (currInput != currOutput)
|
||||||
|
{
|
||||||
|
onError($"Expected same input/output buffer: input = '{currInput}', output = '{currOutput}'");
|
||||||
|
}
|
||||||
|
|
||||||
if (stateBlockIndex >= MaxStateBlocks)
|
paramValue = ParseBuffer(currInput, result, onError);
|
||||||
{
|
break;
|
||||||
onError($"Invalid state block: {stateBlockIndex} is out of bounds (max: {MaxStateBlocks})");
|
case ParameterType.StateBlock:
|
||||||
}
|
byte stateBlockIndex = 0;
|
||||||
|
if (!hasStateBlock)
|
||||||
|
{
|
||||||
|
onError($"Opcode '{opcodeName}' requires a state block");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!byte.TryParse(stateBlockStr, out stateBlockIndex))
|
||||||
|
{
|
||||||
|
onError($"Invalid state block: '{stateBlockStr}' cannot be parsed as an 8-bit integer");
|
||||||
|
}
|
||||||
|
|
||||||
result.StateBlocksCount = Math.Max(stateBlockIndex + 1, result.StateBlocksCount);
|
if (stateBlockIndex >= MaxStateBlocks)
|
||||||
paramValue = stateBlockIndex;
|
{
|
||||||
stateBlockUsed = true;
|
onError($"Invalid state block: {stateBlockIndex} is out of bounds (valid range: 0 to {MaxStateBlocks - 1})");
|
||||||
break;
|
}
|
||||||
case ParameterType.Ignored:
|
}
|
||||||
// the game doesn't use this value, but existing synths use the same value as the first param
|
|
||||||
paramValue = parameters[0].Value;
|
result.StateBlocksCount = Math.Max(stateBlockIndex + 1, result.StateBlocksCount);
|
||||||
break;
|
paramValue = stateBlockIndex;
|
||||||
default: throw new NotImplementedException();
|
stateBlockUsed = true;
|
||||||
|
break;
|
||||||
|
case ParameterType.Ignored:
|
||||||
|
// the game doesn't use this value, but existing synths use the same value as the first param
|
||||||
|
paramValue = parameters[0].Value;
|
||||||
|
break;
|
||||||
|
default: throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Parameter.IsInputType(type))
|
if (Parameter.IsInputType(type))
|
||||||
@ -20945,7 +21109,7 @@ namespace CodeWalker.GameFiles
|
|||||||
parameters.Add(new Parameter(type, paramValue));
|
parameters.Add(new Parameter(type, paramValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsInputScalar() => inputs[currInputIndex][0] != 'B';
|
bool IsInputScalar() => currInputIndex < inputs.Length && inputs[currInputIndex][0] != 'B';
|
||||||
|
|
||||||
var numInputs = inputs.Length;
|
var numInputs = inputs.Length;
|
||||||
var numOutputs = outputs.Length;
|
var numOutputs = outputs.Length;
|
||||||
@ -20981,22 +21145,51 @@ namespace CodeWalker.GameFiles
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ushort ParseVariable(string str, AssembleResult result, Action<string> onError)
|
private static ushort ParseVariable(string str, Dat10SynthVariable[] variables, AssembleResult result, Action<string> onError)
|
||||||
{
|
{
|
||||||
if (str[0] != 'V')
|
bool foundByName = false;
|
||||||
|
byte varIndex = 0xFF;
|
||||||
|
if (variables != null && variables.Length > 0)
|
||||||
{
|
{
|
||||||
onError($"Expected a variable, found '{str}'");
|
try
|
||||||
return 0;
|
{
|
||||||
|
var nameHash = XmlMeta.GetHash(str);
|
||||||
|
|
||||||
|
for (int i = 0; i < variables.Length; i++)
|
||||||
|
{
|
||||||
|
if (variables[i].Name == nameHash)
|
||||||
|
{
|
||||||
|
foundByName = true;
|
||||||
|
varIndex = (byte)i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) // XmlMeta.GetHash throws an exception if the hash is not a valid hex number
|
||||||
|
{
|
||||||
|
onError($"Invalid variable name '{str}': {e.Message}");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var indexStr = str.Substring(1);
|
if (!foundByName)
|
||||||
if (!byte.TryParse(indexStr, out var index))
|
|
||||||
{
|
{
|
||||||
onError($"Invalid variable: '{indexStr}' cannot be parsed as an integer");
|
if (str[0] != 'V')
|
||||||
|
{
|
||||||
|
onError($"Unknown variable '{str}'");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var indexStr = str.Substring(1);
|
||||||
|
if (!byte.TryParse(indexStr, out varIndex))
|
||||||
|
{
|
||||||
|
onError($"Invalid variable: '{indexStr}' cannot be parsed as an 8-bit integer");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.VariablesCount = Math.Max(index + 1, result.VariablesCount);
|
result.VariablesCount = Math.Max(varIndex + 1, result.VariablesCount);
|
||||||
return (ushort)(0x300 | index);
|
return (ushort)(0x300 | varIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ushort ParseConstant(string str, AssembleResult result, Action<string> onError)
|
private static ushort ParseConstant(string str, AssembleResult result, Action<string> onError)
|
||||||
@ -21004,6 +21197,7 @@ namespace CodeWalker.GameFiles
|
|||||||
if (!FloatUtil.TryParse(str, out var constantValue))
|
if (!FloatUtil.TryParse(str, out var constantValue))
|
||||||
{
|
{
|
||||||
onError($"Invalid constant: '{str}' cannot be parsed as a float");
|
onError($"Invalid constant: '{str}' cannot be parsed as a float");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte constantId;
|
byte constantId;
|
||||||
@ -21054,7 +21248,8 @@ namespace CodeWalker.GameFiles
|
|||||||
var indexStr = str.Substring(1);
|
var indexStr = str.Substring(1);
|
||||||
if (!byte.TryParse(indexStr, out var index))
|
if (!byte.TryParse(indexStr, out var index))
|
||||||
{
|
{
|
||||||
onError($"Invalid register: '{indexStr}' cannot be parsed as an integer");
|
onError($"Invalid register: '{indexStr}' cannot be parsed as an 8-bit integer");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index >= MaxRegisters)
|
if (index >= MaxRegisters)
|
||||||
@ -21080,12 +21275,13 @@ namespace CodeWalker.GameFiles
|
|||||||
var indexStr = str.Substring(1);
|
var indexStr = str.Substring(1);
|
||||||
if (!byte.TryParse(indexStr, out var index))
|
if (!byte.TryParse(indexStr, out var index))
|
||||||
{
|
{
|
||||||
onError($"Invalid buffer: '{indexStr}' cannot be parsed as an integer");
|
onError($"Invalid buffer: '{indexStr}' cannot be parsed as an 8-bit integer");
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index >= MaxBuffers)
|
if (index >= MaxBuffers)
|
||||||
{
|
{
|
||||||
onError($"Invalid buffer: {index} is out of bounds (max: {MaxBuffers})");
|
onError($"Invalid buffer: B{index} is out of bounds (valid range: B0 to B{MaxBuffers - 1})");
|
||||||
}
|
}
|
||||||
|
|
||||||
result.BuffersCount = Math.Max(index + 1, result.BuffersCount);
|
result.BuffersCount = Math.Max(index + 1, result.BuffersCount);
|
||||||
@ -21109,10 +21305,8 @@ namespace CodeWalker.GameFiles
|
|||||||
return (inputVal & 0xFF00) != 0;
|
return (inputVal & 0xFF00) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
HandleOpcode(result.Opcode, inputs, outputs, Param, IsInputScalar);
|
HandleOpcode(result.Opcode, result.NumberOfInputs, result.NumberOfOutputs, Param, IsInputScalar);
|
||||||
|
|
||||||
if (bytecode.BaseStream.Position != bytecode.BaseStream.Length)
|
|
||||||
{ }
|
|
||||||
result.Parameters = parameters.ToArray();
|
result.Parameters = parameters.ToArray();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -21333,40 +21527,40 @@ namespace CodeWalker.GameFiles
|
|||||||
cb(ParameterType.StateBlock);
|
cb(ParameterType.StateBlock);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Opcode.BiquadCoefficients_LowPass_1:
|
case Opcode.BiquadCoefficients_LowPass_1: // bool = false
|
||||||
case Opcode.BiquadCoefficients_HighPass_1:
|
case Opcode.BiquadCoefficients_HighPass_1: // bool = false
|
||||||
case Opcode.BiquadCoefficients_BandPass:
|
case Opcode.BiquadCoefficients_BandPass:
|
||||||
case Opcode.BiquadCoefficients_BandStop:
|
case Opcode.BiquadCoefficients_BandStop:
|
||||||
case Opcode.BiquadCoefficients_LowPass_2:
|
case Opcode.BiquadCoefficients_LowPass_2: // bool = true
|
||||||
case Opcode.BiquadCoefficients_HighPass_2:
|
case Opcode.BiquadCoefficients_HighPass_2: // bool = true
|
||||||
cb(ParameterType.OutputRegister);
|
cb(ParameterType.OutputRegister); // b0
|
||||||
cb(ParameterType.OutputRegister);
|
cb(ParameterType.OutputRegister); // b1
|
||||||
cb(ParameterType.OutputRegister);
|
cb(ParameterType.OutputRegister); // b2
|
||||||
cb(ParameterType.OutputRegister);
|
cb(ParameterType.OutputRegister); // a1
|
||||||
cb(ParameterType.OutputRegister);
|
cb(ParameterType.OutputRegister); // a2
|
||||||
cb(ParameterType.InputScalar);
|
cb(ParameterType.InputScalar);
|
||||||
cb(ParameterType.InputScalar);
|
cb(ParameterType.InputScalar);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Opcode.BiquadCoefficients_PeakingEQ:
|
case Opcode.BiquadCoefficients_PeakingEQ:
|
||||||
cb(ParameterType.OutputRegister);
|
cb(ParameterType.OutputRegister); // b0
|
||||||
cb(ParameterType.OutputRegister);
|
cb(ParameterType.OutputRegister); // b1
|
||||||
cb(ParameterType.OutputRegister);
|
cb(ParameterType.OutputRegister); // b2
|
||||||
cb(ParameterType.OutputRegister);
|
cb(ParameterType.OutputRegister); // a1
|
||||||
cb(ParameterType.OutputRegister);
|
cb(ParameterType.OutputRegister); // a2
|
||||||
cb(ParameterType.InputScalar);
|
cb(ParameterType.InputScalar); // centerFrequency
|
||||||
cb(ParameterType.InputScalar);
|
cb(ParameterType.InputScalar); // unkFrequency
|
||||||
cb(ParameterType.InputScalar);
|
cb(ParameterType.InputScalar); // gain
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Opcode.BiquadProcess_2Pole:
|
case Opcode.BiquadProcess_2Pole:
|
||||||
case Opcode.BiquadProcess_4Pole:
|
case Opcode.BiquadProcess_4Pole:
|
||||||
cb(ParameterType.InputOutputBuffer);
|
cb(ParameterType.InputOutputBuffer);
|
||||||
cb(ParameterType.InputScalar);
|
cb(ParameterType.InputScalar); // b0
|
||||||
cb(ParameterType.InputScalar);
|
cb(ParameterType.InputScalar); // b1
|
||||||
cb(ParameterType.InputScalar);
|
cb(ParameterType.InputScalar); // b2
|
||||||
cb(ParameterType.InputScalar);
|
cb(ParameterType.InputScalar); // a1
|
||||||
cb(ParameterType.InputScalar);
|
cb(ParameterType.InputScalar); // a2
|
||||||
cb(ParameterType.StateBlock);
|
cb(ParameterType.StateBlock);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -21399,7 +21593,7 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
case Opcode.OSC_RAMP_BUFFER_SCALAR:
|
case Opcode.OSC_RAMP_BUFFER_SCALAR:
|
||||||
cb(ParameterType.OutputBuffer);
|
cb(ParameterType.OutputBuffer);
|
||||||
cb(ParameterType.InputScalar);
|
cb(ParameterType.InputScalar); // frequency
|
||||||
cb(ParameterType.StateBlock);
|
cb(ParameterType.StateBlock);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -21564,7 +21758,7 @@ namespace CodeWalker.GameFiles
|
|||||||
{
|
{
|
||||||
// the game ignores this value and in the files it is always 0x200,
|
// the game ignores this value and in the files it is always 0x200,
|
||||||
// but keep it to generate the exact same bytecode when reassembling
|
// but keep it to generate the exact same bytecode when reassembling
|
||||||
cb(ParameterType.InputScalar);
|
cb(ParameterType.InputScalar);
|
||||||
}
|
}
|
||||||
cb(ParameterType.StateBlock);
|
cb(ParameterType.StateBlock);
|
||||||
break;
|
break;
|
||||||
@ -21609,10 +21803,10 @@ namespace CodeWalker.GameFiles
|
|||||||
cb(ParameterType.InputBuffer); // max
|
cb(ParameterType.InputBuffer); // max
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Opcode.BiquadCoefficients_HighSelf_1:
|
case Opcode.BiquadCoefficients_HighShelf_1:
|
||||||
case Opcode.BiquadCoefficients_HighSelf_2:
|
case Opcode.BiquadCoefficients_HighShelf_2:
|
||||||
case Opcode.BiquadCoefficients_LowSelf_1:
|
case Opcode.BiquadCoefficients_LowShelf_1:
|
||||||
case Opcode.BiquadCoefficients_LowSelf_2:
|
case Opcode.BiquadCoefficients_LowShelf_2:
|
||||||
cb(ParameterType.OutputRegister);
|
cb(ParameterType.OutputRegister);
|
||||||
cb(ParameterType.OutputRegister);
|
cb(ParameterType.OutputRegister);
|
||||||
cb(ParameterType.OutputRegister);
|
cb(ParameterType.OutputRegister);
|
||||||
@ -21670,6 +21864,10 @@ namespace CodeWalker.GameFiles
|
|||||||
public Opcode Opcode { get; set; }
|
public Opcode Opcode { get; set; }
|
||||||
public Parameter[] Parameters { get; set; }
|
public Parameter[] Parameters { get; set; }
|
||||||
|
|
||||||
|
public int NumberOfInputs => InputsOutputs >> 4;
|
||||||
|
public int NumberOfOutputs => InputsOutputs & 0xF;
|
||||||
|
public int Size => SizeOf(Opcode, NumberOfInputs, NumberOfOutputs);
|
||||||
|
|
||||||
public void Encode(BinaryWriter bw)
|
public void Encode(BinaryWriter bw)
|
||||||
{
|
{
|
||||||
bw.Write(InputsOutputs);
|
bw.Write(InputsOutputs);
|
||||||
@ -21682,16 +21880,16 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return ToString(null);
|
return ToString(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ToString(float[] constants)
|
public string ToString(float[] constants, Dat10SynthVariable[] variables)
|
||||||
{
|
{
|
||||||
var stateBlock = Parameters.Cast<Parameter?>().SingleOrDefault(p => p.Value.IsStateBlock);
|
var stateBlock = Parameters.Cast<Parameter?>().SingleOrDefault(p => p.Value.IsStateBlock);
|
||||||
var inputsStr = string.Join(", ", Parameters.Where(p => p.IsInput).Select(p => p.ToString(constants)));
|
var inputsStr = string.Join(", ", Parameters.Where(p => p.IsInput).Select(p => p.ToString(constants, variables)));
|
||||||
var outputsStr = string.Join(", ", Parameters.Where(p => p.IsOutput).Select(p => p.ToString(constants))) +
|
var outputsStr = string.Join(", ", Parameters.Where(p => p.IsOutput).Select(p => p.ToString(constants, variables))) +
|
||||||
(stateBlock.HasValue ? " " + stateBlock.Value.ToString(constants) : "");
|
(stateBlock.HasValue ? " " + stateBlock.Value.ToString(constants, variables) : "");
|
||||||
return $"{Opcode,-34} {inputsStr,-16} => {outputsStr,-16}";
|
return $"{Opcode,-34} {inputsStr,-16} => {outputsStr}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21712,10 +21910,10 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return ToString(null);
|
return ToString(null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ToString(float[] constants)
|
public string ToString(float[] constants, Dat10SynthVariable[] variables)
|
||||||
{
|
{
|
||||||
switch (Type)
|
switch (Type)
|
||||||
{
|
{
|
||||||
@ -21737,7 +21935,7 @@ namespace CodeWalker.GameFiles
|
|||||||
case 1: return "1";
|
case 1: return "1";
|
||||||
default:
|
default:
|
||||||
var index = v - 2;
|
var index = v - 2;
|
||||||
return constants == null ?
|
return constants == null || index >= constants.Length ?
|
||||||
$"C{index}" :
|
$"C{index}" :
|
||||||
FloatUtil.ToString(constants[index]);
|
FloatUtil.ToString(constants[index]);
|
||||||
}
|
}
|
||||||
@ -21746,7 +21944,10 @@ namespace CodeWalker.GameFiles
|
|||||||
case ParameterType.OutputRegister:
|
case ParameterType.OutputRegister:
|
||||||
return $"R{Value & 0xFF}";
|
return $"R{Value & 0xFF}";
|
||||||
case ParameterType.InputVariable:
|
case ParameterType.InputVariable:
|
||||||
return $"V{Value & 0xFF}";
|
int varIndex = Value & 0xFF;
|
||||||
|
return variables == null || varIndex >= variables.Length ?
|
||||||
|
$"V{varIndex}" :
|
||||||
|
RelXml.HashString(variables[varIndex].Name);
|
||||||
case ParameterType.StateBlock:
|
case ParameterType.StateBlock:
|
||||||
return $"[{Value}]";
|
return $"[{Value}]";
|
||||||
case ParameterType.Ignored:
|
case ParameterType.Ignored:
|
||||||
@ -21756,17 +21957,35 @@ namespace CodeWalker.GameFiles
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsInputType(ParameterType Type)
|
public static bool IsInputType(ParameterType type)
|
||||||
=> Type == ParameterType.InputBuffer ||
|
=> type == ParameterType.InputBuffer ||
|
||||||
Type == ParameterType.InputRegister ||
|
type == ParameterType.InputRegister ||
|
||||||
Type == ParameterType.InputScalar ||
|
type == ParameterType.InputScalar ||
|
||||||
Type == ParameterType.InputVariable ||
|
type == ParameterType.InputVariable ||
|
||||||
Type == ParameterType.InputOutputBuffer;
|
type == ParameterType.InputOutputBuffer;
|
||||||
|
|
||||||
public static bool IsOutputType(ParameterType Type)
|
public static bool IsOutputType(ParameterType type)
|
||||||
=> Type == ParameterType.OutputBuffer ||
|
=> type == ParameterType.OutputBuffer ||
|
||||||
Type == ParameterType.OutputRegister ||
|
type == ParameterType.OutputRegister ||
|
||||||
Type == ParameterType.InputOutputBuffer;
|
type == ParameterType.InputOutputBuffer;
|
||||||
|
|
||||||
|
// Returns a readable string representing the ParameterType, used in error messages
|
||||||
|
public static string TypeToStringReadable(ParameterType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ParameterType.InputBuffer: return "input buffer";
|
||||||
|
case ParameterType.InputRegister: return "input register";
|
||||||
|
case ParameterType.InputScalar: return "input scalar";
|
||||||
|
case ParameterType.InputVariable: return "input variable";
|
||||||
|
case ParameterType.OutputBuffer: return "output buffer";
|
||||||
|
case ParameterType.OutputRegister: return "output register";
|
||||||
|
case ParameterType.InputOutputBuffer: return "input/output buffer";
|
||||||
|
case ParameterType.StateBlock: return "state block";
|
||||||
|
case ParameterType.Ignored: return "ignored";
|
||||||
|
default: return "unknown parameter type";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ParameterType
|
public enum ParameterType
|
||||||
@ -21908,10 +22127,10 @@ namespace CodeWalker.GameFiles
|
|||||||
FILL_BUFFER = 0x77,
|
FILL_BUFFER = 0x77,
|
||||||
AWProcess = 0x78,
|
AWProcess = 0x78,
|
||||||
LERP_BUFFER_BUFFER = 0x79,
|
LERP_BUFFER_BUFFER = 0x79,
|
||||||
BiquadCoefficients_HighSelf_1 = 0x7A,
|
BiquadCoefficients_HighShelf_1 = 0x7A,
|
||||||
BiquadCoefficients_HighSelf_2 = 0x7B,
|
BiquadCoefficients_HighShelf_2 = 0x7B,
|
||||||
BiquadCoefficients_LowSelf_1 = 0x7C,
|
BiquadCoefficients_LowShelf_1 = 0x7C,
|
||||||
BiquadCoefficients_LowSelf_2 = 0x7D,
|
BiquadCoefficients_LowShelf_2 = 0x7D,
|
||||||
SWITCH_NORM_BUFFER = 0x7E,
|
SWITCH_NORM_BUFFER = 0x7E,
|
||||||
SWITCH_INDEX_BUFFER = 0x7F,
|
SWITCH_INDEX_BUFFER = 0x7F,
|
||||||
SWITCH_LERP_BUFFER = 0x80,
|
SWITCH_LERP_BUFFER = 0x80,
|
||||||
|
@ -2753,6 +2753,7 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
bool savetest = true;
|
bool savetest = true;
|
||||||
bool xmltest = true;
|
bool xmltest = true;
|
||||||
|
bool asmtest = true;
|
||||||
|
|
||||||
foreach (RpfFile rpf in RpfMan.AllRpfs)
|
foreach (RpfFile rpf in RpfMan.AllRpfs)
|
||||||
{
|
{
|
||||||
@ -2814,7 +2815,7 @@ namespace CodeWalker.GameFiles
|
|||||||
if (rel3.RelDatasSorted?.Length != rel.RelDatasSorted?.Length)
|
if (rel3.RelDatasSorted?.Length != rel.RelDatasSorted?.Length)
|
||||||
{ } //check nothing went missing...
|
{ } //check nothing went missing...
|
||||||
|
|
||||||
|
|
||||||
data = rel3.Save(); //full roundtrip!
|
data = rel3.Save(); //full roundtrip!
|
||||||
if (data != null)
|
if (data != null)
|
||||||
{
|
{
|
||||||
@ -2847,6 +2848,19 @@ namespace CodeWalker.GameFiles
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (asmtest)
|
||||||
|
{
|
||||||
|
if (rel.RelType == RelDatFileType.Dat10ModularSynth)
|
||||||
|
{
|
||||||
|
foreach (var d in rel.RelDatasSorted)
|
||||||
|
{
|
||||||
|
if (d is Dat10Synth synth)
|
||||||
|
{
|
||||||
|
synth.TestDisassembly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user