// Interpreting public static string[] ParseScript(uint[] cmd, int sanity = -1) { // sub_148CBC Moon v1.0 List <string> parse = new List <string>(); const int sanityMode = 0; // todo string ErrorNear(int line, string error) { var start = Math.Max(line - 6, 0); var end = Math.Min(line + 6, cmd.Length - 1); var toPrint = cmd.Skip(start).Take(end - start); var message = $"Error at line {line}:" + Environment.NewLine; message += string.Join(" ", toPrint.Select(b => $"{b:X2}")) + Environment.NewLine; for (var x = 0; x < line - start; x++) { message += " "; } message += "^^" + Environment.NewLine; message += error; return(message); } int i = 0; // Current Offset of decompressed instructions while (i < cmd.Length) // read away { // Read a Command string instrLine; var line = i; var opcodeval = cmd[i++]; var opcodesafe = opcodeval & 0xFFFF; if (!Enum.IsDefined(typeof(AmxOpCode), opcodesafe)) { throw new ArgumentException(ErrorNear(line, $"Invalid command ID: {opcodesafe:X4} ({opcodesafe})")); } var opcode = (AmxOpCode)opcodesafe; if (!OpCodeTypes.TryGetValue(opcode, out var optype)) { throw new ArgumentException(ErrorNear(line, $"Unknown OpCode: {opcodesafe:X4} ({opcodesafe})")); } switch (optype) { default: throw new ArgumentException("Invalid Command Type"); case AmxOpCodeType.NoParams: { instrLine = EchoIntCommand(opcode); break; } case AmxOpCodeType.OneParam: { var param = (int)cmd[i++]; instrLine = EchoIntCommand(opcode, param); break; } case AmxOpCodeType.TwoParams: { var param1 = (int)cmd[i++]; var param2 = (int)cmd[i++]; instrLine = EchoIntCommand(opcode, param1, param2); break; } case AmxOpCodeType.ThreeParams: { var param1 = (int)cmd[i++]; var param2 = (int)cmd[i++]; var param3 = (int)cmd[i++]; instrLine = EchoIntCommand(opcode, param1, param2, param3); break; } case AmxOpCodeType.FourParams: { var param1 = (int)cmd[i++]; var param2 = (int)cmd[i++]; var param3 = (int)cmd[i++]; var param4 = (int)cmd[i++]; instrLine = EchoIntCommand(opcode, param1, param2, param3, param4); break; } case AmxOpCodeType.FiveParams: { var param1 = (int)cmd[i++]; var param2 = (int)cmd[i++]; var param3 = (int)cmd[i++]; var param4 = (int)cmd[i++]; var param5 = (int)cmd[i++]; instrLine = EchoIntCommand(opcode, param1, param2, param3, param4, param5); break; } case AmxOpCodeType.Jump: { var jumpOffset = (int)cmd[i++]; var jumpDest = (line * 4) + jumpOffset; instrLine = $"{Commands[opcode].PadRight(MaxCommandLength, ' ')} => 0x{jumpDest:X4} ({jumpOffset})"; break; } case AmxOpCodeType.Packed: { var param = (short)(opcodeval >> 16); instrLine = EchoIntCommand(opcode, param); break; } case AmxOpCodeType.CaseTable: { //var jOffset = (i * 4) - 4; // this may be the correct jump start point... var count = cmd[i++]; // switch case table // sanity check // Populate Switch-Case Tree var tree = new List <string>(); // Cases for (int j = 0; j < count; j++) { var jmp = (int)cmd[i++]; var toOffset = ((i - 2) * 4) + jmp; var ifValue = (int)cmd[i++]; tree.Add($"\t{ifValue} => 0x{toOffset:X4} ({jmp})"); } // Default { int jmp = (int)cmd[i++]; var toOffset = ((i - 2) * 4) + jmp; tree.Add($"\t* => 0x{toOffset:X4} ({jmp})"); } instrLine = Commands[opcode] + Environment.NewLine + string.Join(Environment.NewLine, tree); break; } } if (opcode == AmxOpCode.RET || opcode == AmxOpCode.RETN || opcode == AmxOpCode.IRETN) { // Newline after return instrLine += Environment.NewLine; } if (parse.Count == 0 && opcode == AmxOpCode.HALT_P) { // Newline after 0x0000 HALT.P instrLine += Environment.NewLine; } parse.Add($"0x{line * 4:X4}: [{opcodeval & 0x7FF:X2}] {instrLine}"); } if (sanity >= 0 && sanity != sanityMode) { throw new ArgumentException(); } return(parse.ToArray()); }