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 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); }
/// <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); }