internal void ResolveReferences(ScriptExpressionTable allExpressions) { if (_nextIndex.IsValid) { Next = allExpressions.FindExpression(_nextIndex); } }
private bool GenerateVariableReference(ScriptExpression expression, IndentedTextWriter output, bool islocal = false) { _onNewLine = false; string varDesc = islocal ? ("var_" + expression.Value.ToString() + "#") : ""; output.Write(varDesc + expression.StringValue); return(true); }
private bool GenerateScriptReference(ScriptExpression expression, IndentedTextWriter output) { var expressionIndex = new DatumIndex(expression.Value.UintValue); _nextFunctionIsScript = true; GenerateCode(_scripts.Expressions.FindExpression(expressionIndex), output); _nextFunctionIsScript = false; return(true); }
/// <summary> /// Determines whether an expression is a multiline construct. /// </summary> /// <param name="expression"></param> /// <returns>Returns true if the expression is a multiline construct.</returns> private bool IsMultilineExpression(ScriptExpression expression) { if (expression.Type == ScriptExpressionType.Group) { FunctionInfo info = _opcodes.GetFunctionInfo(expression.Opcode); return(info.Name == "if" || info.Name == "or" || info.Name == "and" || info.Name.StartsWith("begin")); } return(false); }
private uint GetValue(ScriptExpression expression, ScriptValueType type, Endian endian) { if (endian == Endian.BigEndian) { return(expression.Value.UintValue >> (32 - (type.Size * 8))); } else { return(expression.Value.UintValue); } }
private bool GenerateGroup(ScriptExpression expression, IndentedTextWriter output) { var childIndex = new DatumIndex(expression.Value.UintValue); if (!childIndex.IsValid) { throw new InvalidOperationException("Group expression has no child"); } GenerateCode(_scripts.Expressions.FindExpression(childIndex), output); return(true); }
/// <summary> /// The decompiler generates a cond construct based on an expression. /// </summary> /// <param name="output">The output.</param> /// <param name="expression">The cond expression.</param> /// <param name="newLine">Indicates whether the expressions of the branch start on a new line.</param> /// <returns>Returns true if code was generated.</returns> private bool GenerateCond(IndentedTextWriter output, ScriptExpression expression, bool newLine) { int startIndent = output.Indent; // Handle the initial con group. if (!_cond) { // indicate that the following expressions are part of a cond construct. _cond = true; _condIndent = output.Indent + 1; output.WriteLine("(cond"); // increase indent for the first conditional and write the opening parenthesis. output.Indent++; output.Write("("); // generate code. DatumIndex nameIndex = new DatumIndex(expression.Value.UintValue); FollowRootIndex(output, nameIndex, false, BranchType.Cond); // write the closing parenthesis of the last conditional. output.Indent = startIndent + 1; output.WriteLine(")"); // write the closing parenthesis of the cond call. output.Indent = startIndent; output.Write(")"); _cond = false; } // handle all following conditionals. else { output.Indent = _condIndent; // close the previous cond group and open the current one. output.WriteLine(")"); output.Write("("); // generate code. DatumIndex nameIndex = new DatumIndex(expression.Value.UintValue); FollowRootIndex(output, nameIndex, false, BranchType.Cond); output.Indent = startIndent; } HandleNewLine(output, newLine); return(true); }
private bool GenerateVariableDecl(ScriptExpression expression, IndentedTextWriter output) { _onNewLine = false; output.Write("(local "); var expressionIndex = new DatumIndex(expression.Value.UintValue); _nextExpressionIsVar = true; _varTypeWritten = false; GenerateCode(_scripts.Expressions.FindExpression(expressionIndex), output); _nextExpressionIsVar = false; localVarCounter++; output.Write(")"); return(true); }
private bool HandleExpression(ScriptExpression expression, IndentedTextWriter output) { short realtype = (short)expression.Type; short clippedtype = (short)((short)expression.Type & 0xFF); switch ((ScriptExpressionType)clippedtype) { case ScriptExpressionType.Expression: case ScriptExpressionType.Expression4: return(GenerateExpressionCode(expression, output)); case ScriptExpressionType.GlobalsReference: case ScriptExpressionType.GlobalsReference4: { if ((realtype & 0xFF00) > 0) { return(GenerateVariableReference(expression, output, true)); } else { return(GenerateGlobalsReference(expression, output)); } } case ScriptExpressionType.ParameterReference: case ScriptExpressionType.ParameterReference4: return(GenerateParameterReference(expression, output)); case ScriptExpressionType.ScriptReference: case ScriptExpressionType.ScriptReference4: return(GenerateScriptReference(expression, output)); case ScriptExpressionType.Group: case ScriptExpressionType.Group4: return(GenerateGroup(expression, output)); case ScriptExpressionType.VariableReference4: return(GenerateVariableReference(expression, output)); case ScriptExpressionType.VariableDecl4: return(GenerateVariableDecl(expression, output)); default: throw new InvalidOperationException("Unknown script expression type"); } }
/// <summary> /// The decompiler generates a function group based on an expression. /// </summary> /// <param name="output">The output.</param> /// <param name="expression">The group expression.</param> /// <param name="newLine">Indicates whether the expressions of the branch start on a new line.</param> /// <returns>Returns true if code was generated.</returns> private bool GenerateGroup(IndentedTextWriter output, ScriptExpression expression, bool newLine) { DatumIndex nameIndex = new DatumIndex(expression.Value.UintValue); FunctionInfo info = _opcodes.GetFunctionInfo(expression.Opcode); // filter out begin groups which were added by the compiler. if (expression.LineNumber == 0) { if (info.Name == "if") { throw new Exception("The decompiler failed to catch a cond call."); } FollowRootIndex(output, nameIndex, true, BranchType.CompilerBegin); return(true); } int startIndent = output.Indent; // write the call's opening parenthesis and name. output.Write($"({info.Name}"); // handle regular begin calls. if (info.Name.StartsWith("begin") || info.Name == "or" || info.Name == "and" || info.Name == "branch") { FollowRootIndex(output, nameIndex, true, BranchType.Multiline); output.Indent = startIndent; } // handle if calls. else if (info.Name == "if") { FollowRootIndex(output, nameIndex, false, BranchType.If); output.Indent = startIndent; } // handle all other (normal) calls. else { FollowRootIndex(output, nameIndex, false, BranchType.Call); } // write the call's closing parenthesis. output.Write(")"); HandleNewLine(output, newLine); return(true); }
/// <summary> /// The decompiler generates a script reference based on an expression. /// </summary> /// <param name="output">The output.</param> /// <param name="expression">The script reference expression.</param> /// <param name="newLine">Indicates whether the expressions of the branch start on a new line.</param> /// <returns>Returns true if code was generated.</returns> private bool GenerateScriptReference(IndentedTextWriter output, ScriptExpression expression, bool newLine) { // we need to retrieve the script's name from its function_name expression. Otherwise branch calls will become corrupt. DatumIndex nameIndex = new DatumIndex(expression.Value.UintValue); var nameExp = _scripts.Expressions.FindExpression(nameIndex); // write the call's opening parenthesis and name. output.Write($"({nameExp.StringValue}"); // write code FollowRootIndex(output, nameIndex, false, BranchType.Call); // write the call's closing parenthesis. output.Write(")"); HandleNewLine(output, newLine); return(true); }
/// <summary> /// Retrieves an expression's value. Takes endianness into account. /// </summary> /// <param name="expression">The expression from which the value is being retrieved.</param> /// <param name="type">The value type of the expression.</param> /// <returns></returns> private uint GetValue(ScriptExpression expression, ScriptValueType type) { if (_endian == Endian.BigEndian) { return(expression.Value.UintValue >> (32 - (type.Size * 8))); } else { var bytes = BitConverter.GetBytes(expression.Value.UintValue); uint result = type.Size switch { 4 => expression.Value.UintValue, 2 => BitConverter.ToUInt16(bytes, 0), 1 => bytes[0], 0 => 0, _ => throw new ArgumentException($"Script value types can only have a size of 0, 1, 2 or 4 bytes. The size {type.Size} is invalid.") }; return(result); } }
/// <summary> /// Decides how an expression will be handled and which actions to perform, based on the expression type. /// </summary> /// <param name="output">The output.</param> /// <param name="expression">The expression, which is being handled.</param> /// <param name="newLine">Indicates whether the expressions of the branch start on a new line.</param> private void HandleExpression(IndentedTextWriter output, ScriptExpression expression, bool newLine) { ushort ifOp = _opcodes.GetFunctionInfo("if")[0].Opcode; ushort funcNameOp = _opcodes.GetTypeInfo("function_name").Opcode; short clippedtype = (short)((short)expression.Type & 0xFF); bool _ = (ScriptExpressionType)clippedtype switch { ScriptExpressionType.Group when expression.Opcode == ifOp && expression.LineNumber == 0 => GenerateCond(output, expression, newLine), ScriptExpressionType.Group => GenerateGroup(output, expression, newLine), ScriptExpressionType.Expression when expression.ReturnType == funcNameOp => true, ScriptExpressionType.Expression => GenerateExpression(output, expression, newLine), ScriptExpressionType.ScriptReference => GenerateScriptReference(output, expression, newLine), ScriptExpressionType.GlobalsReference => GenerateGlobalsReference(output, expression, newLine), ScriptExpressionType.ParameterReference => GenerateParameterReference(output, expression, newLine), _ => throw new NotImplementedException($"The Decompiler encountered an unknown Expression Type: \"{(ScriptExpressionType)clippedtype}\".") }; }
private bool HandleExpression(ScriptExpression expression, IndentedTextWriter output) { switch (expression.Type) { case ScriptExpressionType.Expression: return(GenerateExpressionCode(expression, output)); case ScriptExpressionType.GlobalsReference: return(GenerateGlobalsReference(expression, output)); case ScriptExpressionType.ParameterReference: return(GenerateParameterReference(expression, output)); case ScriptExpressionType.ScriptReference: return(GenerateScriptReference(expression, output)); case ScriptExpressionType.Group: return(GenerateGroup(expression, output)); default: throw new InvalidOperationException("Unknown script expression type"); } }
private uint GetValue(ScriptExpression expression, ScriptValueType type) { //var valBytes = new byte[4]; uint newVal = 0; byte[] valBytes = new byte[4]; switch (type.Size) { case 1: valBytes[0] = 0; valBytes[1] = 0; valBytes[2] = 0; valBytes[3] = (byte)(expression.Value); break; case 2: valBytes[0] = 0; valBytes[1] = 0; valBytes[2] = (byte)(expression.Value); valBytes[3] = (byte)(expression.Value >> 8); break; default: case 4: valBytes[0] = (byte)(expression.Value); valBytes[1] = (byte)(expression.Value >> 8); valBytes[2] = (byte)(expression.Value >> 16); valBytes[3] = (byte)(expression.Value >> 24); break; } newVal = BitConverter.ToUInt32(valBytes, 0); return(newVal >> (32 - (type.Size * 8))); //return expression.Value >> (32 - (type.Size*8)); }
internal void ResolveReferences(ScriptExpressionTable allExpressions) { if (_nextIndex.IsValid) Next = allExpressions.FindExpression(_nextIndex); }
private void GenerateCode(ScriptExpression expression, IndentedTextWriter output) { int firstIndentedArg = int.MaxValue; bool isFunctionCall = false; if (expression.Type == ScriptExpressionType.Expression) { ScriptValueType type = _opcodes.GetTypeInfo((ushort) expression.ReturnType); if (type.Name == "function_name") { isFunctionCall = true; if (!_nextFunctionIsScript) { ScriptFunctionInfo info = _opcodes.GetFunctionInfo(expression.Opcode); if (info != null) { if (info.Name.StartsWith("begin")) { firstIndentedArg = 0; if (expression.LineNumber > 0 && !_onNewLine) { output.Indent++; output.WriteLine(); } } else if (info.Name == "if") { firstIndentedArg = 1; } } } if (expression.LineNumber > 0) output.Write("("); } } bool wroteAnything = HandleExpression(expression, output); int startIndent = output.Indent; int currentArg = 0; ScriptExpression sibling = expression.Next; while (sibling != null) { if (wroteAnything) { if (currentArg == firstIndentedArg) output.Indent++; if (currentArg >= firstIndentedArg || output.Indent != startIndent) { output.WriteLine(); _onNewLine = true; } else { output.Write(" "); } } wroteAnything = HandleExpression(sibling, output); sibling = sibling.Next; currentArg++; } if (isFunctionCall && expression.LineNumber > 0) { if (output.Indent != startIndent) { output.Indent = startIndent; if (wroteAnything) output.WriteLine(); output.Write(")"); _onNewLine = true; } else { output.Write(")"); } } }
private uint GetValue(ScriptExpression expression, ScriptValueType type) { //var valBytes = new byte[4]; uint newVal = 0; byte[] valBytes = new byte[4]; switch(type.Size) { case 1: valBytes[0] = 0; valBytes[1] = 0; valBytes[2] = 0; valBytes[3] = (byte)(expression.Value); break; case 2: valBytes[0] = 0; valBytes[1] = 0; valBytes[2] = (byte)(expression.Value); valBytes[3] = (byte)(expression.Value >> 8); break; default: case 4: valBytes[0] = (byte)(expression.Value); valBytes[1] = (byte)(expression.Value >> 8); valBytes[2] = (byte)(expression.Value >> 16); valBytes[3] = (byte)(expression.Value >> 24); break; } newVal = BitConverter.ToUInt32(valBytes, 0); return newVal >> (32 - (type.Size * 8)); //return expression.Value >> (32 - (type.Size*8)); }
private bool GenerateGroup(ScriptExpression expression, IndentedTextWriter output) { var childIndex = new DatumIndex(expression.Value); if (!childIndex.IsValid) throw new InvalidOperationException("Group expression has no child"); GenerateCode(_scripts.Expressions.FindExpression(childIndex), output); return true; }
private bool GenerateParameterReference(ScriptExpression expression, IndentedTextWriter output) { _onNewLine = false; output.Write(expression.StringValue); return(true); }
/// <summary> /// The decompiler generates a literal (expression) on an expression. /// </summary> /// <param name="output">The output.</param> /// <param name="expression">The expression.</param> /// <param name="newLine">Indicates whether the expressions of the branch start on a new line.</param> /// <returns>Returns true if code was generated.</returns> private bool GenerateExpression(IndentedTextWriter output, ScriptExpression expression, bool newLine) { ScriptValueType type = _opcodes.GetTypeInfo(expression.ReturnType); ScriptValueType actualType = type; // Check if a typecast is occurring if (expression.Opcode != 0xFFFF) { actualType = _opcodes.GetTypeInfo(expression.Opcode); // Simply write the string for quoted expressions. if (actualType.Quoted) { // Don't quote the keyword none. if (expression.Value.IsNull || expression.StringValue == "none") { output.Write("none"); } else { output.Write("\"{0}\"", ScriptStringHelpers.Escape(expression.StringValue)); } return(false); } } uint value = GetValue(expression, actualType); byte[] val = BitConverter.GetBytes(value); string text; switch (actualType.Name) { case "void": text = ""; break; case "ai_command_script": case "script": short index = BitConverter.ToInt16(val, 0); text = _scripts.Scripts[index].Name; break; case "boolean": text = BitConverter.ToBoolean(val, 0) ? "true" : "false"; break; case "short": text = BitConverter.ToInt16(val, 0).ToString(); break; case "long": // Signed integer int signed = (int)value; text = signed.ToString(); break; case "real": float fl = BitConverter.ToSingle(val, 0); text = fl.ToString("0.0#######", CultureInfo.InvariantCulture); break; case "ai_line": text = expression.StringValue == "" ? "\"\"" : expression.StringValue; break; case "unit_seat_mapping": text = expression.Value.IsNull ? "none" : expression.StringValue; break; default: if (expression.Value.IsNull) { text = "none"; } else if (actualType.IsEnum) { string enumValue = actualType.GetEnumValue(value); if (enumValue != null) { text = enumValue; } else { throw new NotImplementedException("Unknown Enum Value."); } } else { //throw new NotImplementedException($"Unhandled Return Type: \"{actualType.Name}\"."); text = expression.StringValue; } break; } output.Write(text); HandleNewLine(output, newLine); return(false); }
private uint GetValue(ScriptExpression expression, ScriptValueType type) { return expression.Value >> (32 - (type.Size*8)); }
private void GenerateCode(ScriptExpression expression, IndentedTextWriter output) { int firstIndentedArg = int.MaxValue; bool isFunctionCall = false; if (expression.Type == ScriptExpressionType.Expression) { ScriptValueType type = _opcodes.GetTypeInfo((ushort)expression.ReturnType); if (type.Name == "function_name") { isFunctionCall = true; if (!_nextFunctionIsScript) { ScriptFunctionInfo info = _opcodes.GetFunctionInfo(expression.Opcode); if (info != null) { if (info.Name.StartsWith("begin")) { firstIndentedArg = 0; if (expression.LineNumber > 0 && !_onNewLine) { output.Indent++; output.WriteLine(); } } else if (info.Name == "if") { firstIndentedArg = 1; } } } if (expression.LineNumber > 0) { output.Write("("); } } } bool wroteAnything = HandleExpression(expression, output); int startIndent = output.Indent; int currentArg = 0; ScriptExpression sibling = expression.Next; while (sibling != null) { if (wroteAnything) { if (currentArg == firstIndentedArg) { output.Indent++; } if (currentArg >= firstIndentedArg || output.Indent != startIndent) { output.WriteLine(); _onNewLine = true; } else { output.Write(" "); } } wroteAnything = HandleExpression(sibling, output); sibling = sibling.Next; currentArg++; } if (isFunctionCall && expression.LineNumber > 0) { if (output.Indent != startIndent) { output.Indent = startIndent; if (wroteAnything) { output.WriteLine(); } output.Write(")"); _onNewLine = true; } else { output.Write(")"); } } }
public void WriteExpression(ScriptExpression expression, IndentedTextWriter output) { _onNewLine = true; GenerateCode(expression, output); }
private bool GenerateExpressionCode(ScriptExpression expression, IndentedTextWriter output) { if (expression.LineNumber == 0) { return(false); } _onNewLine = false; ScriptValueType type = _opcodes.GetTypeInfo((ushort)expression.ReturnType); ScriptValueType actualType = type; if (type.Name != "function_name") { // Check if a typecast is occurring actualType = _opcodes.GetTypeInfo(expression.Opcode); if (actualType.Quoted) { if (expression.Value != 0xFFFFFFFF) { output.Write("\"{0}\"", expression.StringValue); } else { output.Write("none"); } return(true); } } uint value = GetValue(expression, type); switch (type.Name) { case "void": return(false); case "boolean": if (value > 0) { output.Write("true"); } else { output.Write("false"); } break; case "short": case "long": // Signed integer output.Write((int)value); break; case "real": // Eww var floatBytes = new byte[4]; floatBytes[0] = (byte)(value & 0xFF); floatBytes[1] = (byte)((value >> 8) & 0xFF); floatBytes[2] = (byte)((value >> 16) & 0xFF); floatBytes[3] = (byte)((value >> 24) & 0xFF); output.Write(BitConverter.ToSingle(floatBytes, 0)); break; case "function_name": if (_nextFunctionIsScript) { output.Write(expression.StringValue); // halo online seems weird in that the "opcode" value that is actually a script index in this case is greater than the number of scripts //output.Write(_scripts.Scripts[expression.Opcode].Name); _nextFunctionIsScript = false; } else { ScriptFunctionInfo info = _opcodes.GetFunctionInfo(expression.Opcode); if (info == null) { output.Write("unknown_" + expression.Opcode.ToString("X") + "(" + expression.StringValue + ")"); //throw new InvalidOperationException("Unrecognized function opcode 0x" + expression.Opcode.ToString("X")); } else { output.Write(info.Name); } } break; case "unit_seat_mapping": // This isn't the technical way of doing this, // but since seat mapping names aren't stored anywhere, // it would be tricky to resolve them unless we just use an index for now if (expression.Value != 0xFFFFFFFF) { output.Write(expression.Value & 0xFFFF); } else { output.Write("none"); } break; default: string enumValue = actualType.GetEnumValue(value); if (enumValue != null) { output.Write(enumValue); } else if (expression.Value == 0xFFFFFFFF) { output.Write("none"); } else { enumValue = expression.StringValue; if (enumValue != null) { output.Write(enumValue); } else { output.Write("0x{0:X}", value); } } break; } return(true); }
private void WriteExpression(ScriptExpression expression, IndentedTextWriter output) { _onNewLine = true; localVarCounter = 0; GenerateCode(expression, output, true); }
private void GenerateCode(ScriptExpression expression, IndentedTextWriter output, bool firstrun = false) { int firstIndentedArg = int.MaxValue; bool isFunctionCall = false; if (expression.Type == ScriptExpressionType.Expression || expression.Type == ScriptExpressionType.Expression4) { ScriptValueType type = _opcodes.GetTypeInfo((ushort)expression.ReturnType); if (type.Name == "function_name") { isFunctionCall = true; if (!_nextFunctionIsScript) { FunctionInfo info = _opcodes.GetFunctionInfo(expression.Opcode); if (info != null) { if (info.Name.StartsWith("begin")) { firstIndentedArg = 0; if (expression.LineNumber > 0 && !_onNewLine) { output.Indent++; output.WriteLine(); } } else if (info.Name == "if") { firstIndentedArg = 1; } } } if (expression.LineNumber > 0) { output.Write("("); } } } bool wroteAnything = HandleExpression(expression, output); int startIndent = output.Indent; int currentArg = 0; if (_h4 && firstrun) { firstIndentedArg = 0; currentArg = 1; _h4 = false; } ScriptExpression sibling = expression.NextExpression; while (sibling != null) { if (wroteAnything && !_nextExpressionIsVar) { if (currentArg == firstIndentedArg) { output.Indent++; } if (currentArg >= firstIndentedArg) { output.WriteLine(); _onNewLine = true; } else if (output.Indent != startIndent) { output.WriteLine(); _onNewLine = true; } else { output.Write(" "); } } if (!_nextExpressionIsVar) { wroteAnything = HandleExpression(sibling, output); } else if ((_nextExpressionIsVar && sibling.Opcode != 0xFFFF)) { if (!_varTypeWritten) { ScriptValueType type = _opcodes.GetTypeInfo((ushort)sibling.ReturnType); output.Write(type.Name + " var_" + localVarCounter.ToString() + " "); _varTypeWritten = true; } wroteAnything = HandleExpression(sibling, output); } sibling = sibling.NextExpression; currentArg++; } if (isFunctionCall && expression.LineNumber > 0) { if (output.Indent != startIndent) { output.Indent = startIndent; if (wroteAnything) { output.WriteLine(); } output.Write(")"); _onNewLine = true; } else { output.Write(")"); } } }
/// <summary> /// The decompiler generates a script parameter reference based on an expression. /// </summary> /// <param name="output">The output.</param> /// <param name="expression">The parameter reference expression.</param> /// <param name="newLine">Indicates whether the expressions of the branch start on a new line.</param> /// <returns>Returns true if code was generated.</returns> private bool GenerateParameterReference(IndentedTextWriter output, ScriptExpression expression, bool newLine) { output.Write(expression.StringValue); HandleNewLine(output, newLine); return(true); }
private bool GenerateExpressionCode(ScriptExpression expression, IndentedTextWriter output) { if (expression.LineNumber == 0) { return(false); } _onNewLine = false; ScriptValueType type = _opcodes.GetTypeInfo((ushort)expression.ReturnType); ScriptValueType actualType = type; if (type.Name != "function_name") { // Check if a typecast is occurring if (expression.Opcode != 0xFFFF) { actualType = _opcodes.GetTypeInfo(expression.Opcode); if (actualType.Quoted) { if (expression.Value.UintValue != 0xFFFFFFFF) { output.Write("\"{0}\"", expression.StringValue); } else { output.Write("none"); } return(true); } } } uint value = GetValue(expression, type, _endian); byte[] val = BitConverter.GetBytes(value); switch (type.Name) { case "void": return(false); case "boolean": if (BitConverter.ToBoolean(val, 0)) { output.Write("true"); } else { output.Write("false"); } break; case "short": output.Write(BitConverter.ToInt16(val, 0)); break; case "long": // Signed integer output.Write((int)value); break; case "real": // Eww //var floatBytes = new byte[4]; //floatBytes[0] = (byte) (value & 0xFF); //floatBytes[1] = (byte) ((value >> 8) & 0xFF); //floatBytes[2] = (byte) ((value >> 16) & 0xFF); //floatBytes[3] = (byte) ((value >> 24) & 0xFF); var fl = BitConverter.ToSingle(val, 0); output.Write(fl.ToString("0.0#######", CultureInfo.InvariantCulture)); break; case "function_name": if (_nextFunctionIsScript) { if (expression.Opcode >= _scripts.Scripts.Count) { output.Write("import#" + expression.StringValue); } else { output.Write(expression.StringValue); // todo: there are cases (h3 xbox mainmenu's campaign_cam specifically) where the function_name expression's opcode value is +1 from what it should be, and the expression prior has the right index. } // the current state of this script code doesnt seem to be good enough to step back so here is the hacky fix implemented in the HO fork. //output.Write(_scripts.Scripts[expression.Opcode].Name); _nextFunctionIsScript = false; } else { FunctionInfo info = _opcodes.GetFunctionInfo(expression.Opcode); if (info == null) { output.Write("UNNAMED_OPCODE_" + expression.Opcode.ToString("X4") + "#" + expression.StringValue); } else { output.Write(info.Name); } //throw new InvalidOperationException("Unrecognized function opcode 0x" + expression.Opcode.ToString("X")); } break; case "unit_seat_mapping": // This isn't the technical way of doing this, // but since seat mapping names aren't stored anywhere, // it would be tricky to resolve them unless we just use an index for now if (expression.Value.UintValue != 0xFFFFFFFF) { output.Write(expression.StringValue); } else { output.Write("none"); } break; case "unparsed": break; default: string enumValue = actualType.GetEnumValue(value); if (enumValue != null) { output.Write(enumValue); } else if (expression.Value.IsNull) { output.Write("none"); } else { enumValue = expression.StringValue; if (enumValue != null) { output.Write(enumValue); } else { output.Write("0x{0:X}", value); } } break; } return(true); }
private bool GenerateParameterReference(ScriptExpression expression, IndentedTextWriter output) { _onNewLine = false; output.Write(expression.StringValue); return true; }
/// <summary> /// The decompiler follows a branch based on a datum index and generates code. Also handles most of the text formatting. /// </summary> /// <param name="output">The output.</param> /// <param name="root">The datumn index to follow.</param> /// <param name="newLine">Indicates whether the expressions of the branch start on a new line.</param> /// <param name="type">The type of the branch.</param> private void FollowRootIndex(IndentedTextWriter output, DatumIndex root, bool newLine, BranchType type) { ScriptExpression exp = _scripts.Expressions.FindExpression(root); int index = 0; // iterate through the branch. while (exp != null) { int startIndent = output.Indent; bool endOfExpression = exp.NextExpression is null; bool endOfInlineMultiline = false; // Remove the last expression in cond calls, which were added by the compiler. if (type == BranchType.Cond && endOfExpression && exp.Type == ScriptExpressionType.Expression && exp.LineNumber == 0) { return; } // if: indent the condition if it is a multiline expression. else if (type == BranchType.If && index == 1 && IsMultilineExpression(exp)) { output.WriteLine(); output.Indent++; } // if: indent after the condition. else if (type == BranchType.If && index == 2) { output.WriteLine(); output.Indent++; } // begin, and, or (multiline): indent after the function name. else if (type == BranchType.Multiline && index == 1) { output.WriteLine(); output.Indent++; } // cond: indent after the condition. else if (type == BranchType.Cond && index == 2) { output.WriteLine(); output.Indent++; } // make begin, or and if calls always start on a new line. else if (type == BranchType.Call && IsMultilineExpression(exp)) { output.WriteLine(); output.Indent++; endOfInlineMultiline = true; } // write code. HandleExpression(output, exp, newLine); // insert space between the parameters of calls and script references. if ((type == BranchType.Call || type == BranchType.If) && !endOfExpression && !newLine) { output.Write(" "); } // handle the line break after inline multiline expressions and reset the indent. if (endOfInlineMultiline && !endOfExpression) { output.WriteLine(); output.Indent = startIndent; } // handle the line break after if statements which end on a multiline expression. else if (type == BranchType.If && IsMultilineExpression(exp) && endOfExpression) { output.WriteLine(); } // If a regular call ends on a multiline call, insert a line break and reset the indent. Mostly applies to sleep_until in combination with or. else if (type == BranchType.Call && IsMultilineExpression(exp) && endOfExpression) { output.WriteLine(); output.Indent = startIndent; } index++; exp = exp.NextExpression; } }
private bool GenerateScriptReference(ScriptExpression expression, IndentedTextWriter output) { var expressionIndex = new DatumIndex(expression.Value); _nextFunctionIsScript = true; GenerateCode(_scripts.Expressions.FindExpression(expressionIndex), output); _nextFunctionIsScript = false; return true; }
private bool HandleExpression(ScriptExpression expression, IndentedTextWriter output) { switch (expression.Type) { case ScriptExpressionType.Expression: return GenerateExpressionCode(expression, output); case ScriptExpressionType.GlobalsReference: return GenerateGlobalsReference(expression, output); case ScriptExpressionType.ParameterReference: return GenerateParameterReference(expression, output); case ScriptExpressionType.ScriptReference: return GenerateScriptReference(expression, output); case ScriptExpressionType.Group: return GenerateGroup(expression, output); default: throw new InvalidOperationException("Unknown script expression type"); } }
private bool GenerateExpressionCode(ScriptExpression expression, IndentedTextWriter output) { if (expression.LineNumber == 0) return false; _onNewLine = false; ScriptValueType type = _opcodes.GetTypeInfo((ushort) expression.ReturnType); ScriptValueType actualType = type; if (type.Name != "function_name") { // Check if a typecast is occurring actualType = _opcodes.GetTypeInfo(expression.Opcode); if (actualType.Quoted) { if (expression.Value != 0xFFFFFFFF) output.Write("\"{0}\"", expression.StringValue); else output.Write("none"); return true; } } uint value = GetValue(expression, type); switch (type.Name) { case "void": return false; case "boolean": if (value > 0) output.Write("true"); else output.Write("false"); break; case "short": case "long": // Signed integer output.Write((int) value); break; case "real": // Eww var floatBytes = new byte[4]; floatBytes[0] = (byte) (value & 0xFF); floatBytes[1] = (byte) ((value >> 8) & 0xFF); floatBytes[2] = (byte) ((value >> 16) & 0xFF); floatBytes[3] = (byte) ((value >> 24) & 0xFF); output.Write(BitConverter.ToSingle(floatBytes, 0)); break; case "function_name": if (_nextFunctionIsScript) { output.Write(expression.StringValue); // halo online seems weird in that the "opcode" value that is actually a script index in this case is greater than the number of scripts //output.Write(_scripts.Scripts[expression.Opcode].Name); _nextFunctionIsScript = false; } else { ScriptFunctionInfo info = _opcodes.GetFunctionInfo(expression.Opcode); if (info == null) output.Write("unknown_" + expression.Opcode.ToString("X")+"("+expression.StringValue+")");//throw new InvalidOperationException("Unrecognized function opcode 0x" + expression.Opcode.ToString("X")); else output.Write(info.Name); } break; case "unit_seat_mapping": // This isn't the technical way of doing this, // but since seat mapping names aren't stored anywhere, // it would be tricky to resolve them unless we just use an index for now if (expression.Value != 0xFFFFFFFF) output.Write(expression.Value & 0xFFFF); else output.Write("none"); break; default: string enumValue = actualType.GetEnumValue(value); if (enumValue != null) { output.Write(enumValue); } else if (expression.Value == 0xFFFFFFFF) { output.Write("none"); } else { enumValue = expression.StringValue; if (enumValue != null) output.Write(enumValue); else output.Write("0x{0:X}", value); } break; } return true; }
private uint GetValue(ScriptExpression expression, ScriptValueType type) { return(expression.Value >> (32 - (type.Size * 8))); }