diff --git a/IFPSLib/Emit/BytecodeOperandType.cs b/IFPSLib/Emit/BytecodeOperandType.cs index d336e95..72e40ce 100644 --- a/IFPSLib/Emit/BytecodeOperandType.cs +++ b/IFPSLib/Emit/BytecodeOperandType.cs @@ -24,6 +24,10 @@ namespace IFPSLib.Emit /// /// Operand refers to an element of an indexed variable, where the index is specified as a local or global variable, or an argument. /// - IndexedVariable + IndexedVariable, + /// + /// Operand is invalid. Stops execution. + /// + Invalid = 0xff } } diff --git a/IFPSLib/Emit/Instruction.cs b/IFPSLib/Emit/Instruction.cs index cb11f82..161968e 100644 --- a/IFPSLib/Emit/Instruction.cs +++ b/IFPSLib/Emit/Instruction.cs @@ -5,6 +5,8 @@ using System.IO; using System.Text; using System.Linq; using System.Collections; +using System.Net; +using System.Data; namespace IFPSLib.Emit { @@ -380,6 +382,31 @@ namespace IFPSLib.Emit return FixBranchOffset(br, br.Read()); } + private static T TryReadIndex(BinaryReader br, IList list) + { + var index = (int)br.Read(); + if (index < list.Count) return list[index]; + return default; + } + + private static Operand TryReadFunction(BinaryReader br, Script script) + { + var func = TryReadIndex(br, script.Functions); + if (func == null) func = new ScriptFunction() + { + Name = "INVALID", + Arguments = new List() + }; + return Operand.Create(func); + } + + private static Operand TryReadType(BinaryReader br, Script script) + { + var type = TryReadIndex(br, script.Types); + if (type == null) type = UnknownType.Instance; + return Operand.Create(type); + } + internal static Instruction Load(BinaryReader br, Script script, ScriptFunction function) { var ret = new Instruction(); @@ -437,10 +464,10 @@ namespace IFPSLib.Emit ret.m_Operands = new List(2) { new Operand(new TypedData(InstructionType.Instance, FixBranchOffset(br, brOffset))), valOp }; break; case OperandType.InlineFunction: - ret.m_Operands = new List(1) { Operand.Create(script.Functions[(int)br.Read()]) }; + ret.m_Operands = new List(1) { TryReadFunction(br, script) }; break; case OperandType.InlineType: - ret.m_Operands = new List(1) { Operand.Create(script.Types[(int)br.Read()]) }; + ret.m_Operands = new List(1) { TryReadType(br, script) }; break; case OperandType.InlineCmpValue: ret.m_Operands = new List(3) { Operand.LoadValue(br, script, function), Operand.LoadValue(br, script, function), Operand.LoadValue(br, script, function) }; @@ -449,7 +476,7 @@ namespace IFPSLib.Emit ret.m_Operands = new List(3) { Operand.LoadValue(br, script, function), Operand.LoadValue(br, script, function), - Operand.Create(script.Types[(int)br.Read()]) + TryReadType(br, script) }; break; case OperandType.InlineEH: @@ -466,7 +493,7 @@ namespace IFPSLib.Emit break; case OperandType.InlineTypeVariable: ret.m_Operands = new List(2) { - Operand.Create(script.Types[(int)br.Read()]), + TryReadType(br, script), new Operand(VariableBase.Load(br, script, function)) }; return ret; diff --git a/IFPSLib/Emit/Operand.cs b/IFPSLib/Emit/Operand.cs index bb29da1..769afd8 100644 --- a/IFPSLib/Emit/Operand.cs +++ b/IFPSLib/Emit/Operand.cs @@ -105,6 +105,12 @@ namespace IFPSLib.Emit m_Value = op.m_Value; } + private Operand(Exception ex = null) + { + m_Type = BytecodeOperandType.Invalid; + if (ex != null) m_Value.Immediate = new TypedData(Types.UnknownType.Instance, ex); + } + public static Operand Create(Types.PrimitiveType type, TType value) { return new Operand(TypedData.Create(type, value)); @@ -156,31 +162,39 @@ namespace IFPSLib.Emit return new Operand(arr, varIdx); } + public static Operand CreateInvalid(Exception ex = null) + { + return new Operand(ex); + } + internal static Operand LoadValue(BinaryReader br, Script script, ScriptFunction function) { var type = (BytecodeOperandType)br.ReadByte(); - switch (type) + try { - case BytecodeOperandType.Variable: - return new Operand(VariableBase.Load(br, script, function)); - case BytecodeOperandType.Immediate: - return new Operand(TypedData.Load(br, script)); - case BytecodeOperandType.IndexedImmediate: - { - var variable = VariableBase.Load(br, script, function); - var idx = br.Read(); - return Create(variable, idx); - } - case BytecodeOperandType.IndexedVariable: - { - var variable = VariableBase.Load(br, script, function); - var idx = VariableBase.Load(br, script, function); - return new Operand(variable, idx); - } - default: - throw new ArgumentOutOfRangeException(string.Format("Invalid operand type {0}", (byte)type)); - } + switch (type) + { + case BytecodeOperandType.Variable: + return new Operand(VariableBase.Load(br, script, function)); + case BytecodeOperandType.Immediate: + return new Operand(TypedData.Load(br, script)); + case BytecodeOperandType.IndexedImmediate: + { + var variable = VariableBase.Load(br, script, function); + var idx = br.Read(); + return Create(variable, idx); + } + case BytecodeOperandType.IndexedVariable: + { + var variable = VariableBase.Load(br, script, function); + var idx = VariableBase.Load(br, script, function); + return new Operand(variable, idx); + } + default: + throw new ArgumentOutOfRangeException(string.Format("Invalid operand type {0}", (byte)type)); + } + } catch (Exception ex) { return new Operand(ex); } } public override string ToString() @@ -195,6 +209,8 @@ namespace IFPSLib.Emit return String.Format("{0}[{1}]", IndexedVariable.Name, IndexImmediate); case BytecodeOperandType.IndexedVariable: return String.Format("{0}[{1}]", IndexedVariable.Name, IndexVariable.Name); + case BytecodeOperandType.Invalid: + return "$INVALID"; default: return ""; } diff --git a/IFPSLib/TypedData.cs b/IFPSLib/TypedData.cs index 2c3dcf3..25d7d6c 100644 --- a/IFPSLib/TypedData.cs +++ b/IFPSLib/TypedData.cs @@ -313,7 +313,11 @@ namespace IFPSLib switch (Type.BaseType) { case PascalTypeCode.Type: - return ValueAs().Name; + { + var type = ValueAs(); + if (type is UnknownType) return "$UNKNOWN"; + return type.Name; + } case PascalTypeCode.Instruction: if (Value == null) return "null"; return string.Format("loc_{0}", ValueAs().Offset.ToString("x")); diff --git a/readme.md b/readme.md index 324e34b..adcd8aa 100644 --- a/readme.md +++ b/readme.md @@ -19,7 +19,7 @@ Library implementing an assembler for IFPS scripts. Depends on IFPSLib. -Assembling the output of IFPSLib's disassembler is expected to output an identical binary, not doing so is considered a bug. +Assembling the output of IFPSLib's disassembler (for sane bytecode with no invalid instructions/operands) is expected to output an identical binary, not doing so is considered a bug. ## ifpsdasm