private string GenerateCode(string namespaceName) { StringBuilder result = new StringBuilder(); result.AppendLine("using System;"); result.AppendLine("using System.Collections.Generic;"); result.AppendLine(); result.AppendLine("namespace " + namespaceName); result.AppendLine("{"); result.AppendLine(" partial class NativeScript"); result.AppendLine(" {"); result.AppendLine(" public class Script_" + NativeScript.GetVersionName(version) + " : Script"); result.AppendLine(" {"); result.AppendLine(" public Script_" + NativeScript.GetVersionName(version) + "()"); result.AppendLine(" {"); result.AppendLine(" ScriptHash = \"" + GetScriptHash() + "\";"); result.AppendLine(" GlobalsAddressOffset = " + GlobalsAddressOffset + ";"); result.AppendLine(" Functions = new Dictionary<string, int>()"); result.AppendLine(" {"); foreach (KeyValuePair <Function, int> functionOffset in FunctionOffsets) { result.AppendLine(" { \"" + functionOffset.Key.Name + "\", " + functionOffset.Value + " },"); } result.AppendLine(" };"); result.AppendLine(" Buffer = new byte[]"); result.AppendLine(" {"); result.Append(" "); for (int i = 0; i < Buffer.Length; i++) { result.Append("0x" + Buffer[i].ToString("X2")); if (i < Buffer.Length - 1) { result.Append(","); } if ((i + 1) % 16 == 0) { result.AppendLine(); result.Append(" "); } } result.AppendLine(); result.AppendLine(" };"); result.AppendLine(" }"); result.AppendLine(" }"); result.AppendLine(" }"); result.AppendLine("}"); return(result.ToString()); }
public static void Compile(GameVersion version, bool optimize) { string scriptPath = GetScriptPath(); if (string.IsNullOrEmpty(scriptPath) || !File.Exists(scriptPath)) { return; } string versionScriptPath = Path.ChangeExtension(scriptPath, NativeScript.GetVersionName(version) + ".c"); if (string.IsNullOrEmpty(versionScriptPath) || !File.Exists(versionScriptPath)) { return; } string text = File.ReadAllText(scriptPath); text = text.Replace(scriptReplaceStr, File.ReadAllText(versionScriptPath)); string tempScriptPath = Path.ChangeExtension(scriptPath, "temp.c"); File.WriteAllText(tempScriptPath, text); string[] lines = text.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); if (lines.Length < 2) { return; } string compiler = lines[0].Trim('/', ' '); string dumpbin = lines[1].Trim('/', ' '); if (string.IsNullOrEmpty(compiler) || string.IsNullOrEmpty(dumpbin)) { return; } if (!File.Exists(compiler)) { compiler = Path.Combine(Path.GetDirectoryName(scriptPath), compiler); } if (!File.Exists(dumpbin)) { dumpbin = Path.Combine(Path.GetDirectoryName(scriptPath), dumpbin); } if (!File.Exists(compiler) || !File.Exists(dumpbin)) { return; } string scriptDir = Path.GetDirectoryName(scriptPath); string objPath = Path.ChangeExtension(tempScriptPath, ".obj"); string additionalArgs = "/GS- ";// disable buffer security check (security cookie stuff) if (optimize) { additionalArgs += "/O2"; } string compileOutput, compileError; if (RunProcess(compiler, "/c " + additionalArgs + " \"" + tempScriptPath + "\"", scriptDir, out compileOutput, out compileError) && !compileOutput.Contains("error") && File.Exists(objPath)) { // Compile was successful, get the disasm string disasmOutput, disasmError; if (RunProcess(dumpbin, "\"" + objPath + "\" /disasm", scriptDir, out disasmOutput, out disasmError) && !disasmOutput.Contains("fatal error")) { string lastLine = string.Empty; Module module = new Module(version); Function currentFunction = null; string instructionStr = string.Empty; string instructionBytes = string.Empty; string[] splitted = disasmOutput.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string line in splitted) { if (line.StartsWith(" 0")) { if (lastLine.EndsWith(":") && !lastLine.StartsWith(" ")) { Debug.Assert(currentFunction == null); currentFunction = new Function(lastLine.Substring(0, lastLine.Length - 1)); module.Functions.Add(currentFunction.Name, currentFunction); } if (!string.IsNullOrEmpty(instructionStr)) { currentFunction.AddInstruction(instructionBytes, instructionStr); } instructionBytes = string.Empty; instructionStr = string.Empty; string str = line.Substring(line.IndexOf(':') + 1); int bytesEnd = str.IndexOf(" "); instructionBytes = str.Substring(0, bytesEnd).Trim(); instructionStr = str.Substring(bytesEnd).Trim(); // Remove additional spacing between the mnemonic and the rest of the instruction int firstSpace = instructionStr.IndexOf(' '); while (true) { if (instructionStr[firstSpace + 1] == ' ') { instructionStr = instructionStr.Remove(firstSpace + 1, 1); } else { break; } } } else if (line == " Summary") { break; } else if (line.StartsWith(" ")) { instructionBytes += line.Trim(); } else { if (!string.IsNullOrEmpty(instructionStr) && currentFunction != null) { currentFunction.AddInstruction(instructionBytes, instructionStr); } currentFunction = null; instructionBytes = string.Empty; instructionStr = string.Empty; } lastLine = line; } if (!string.IsNullOrEmpty(instructionStr)) { currentFunction.AddInstruction(instructionBytes, instructionStr); } module.Build(); } } else { Debug.WriteLine("Compile failed " + compileOutput); Debugger.Break(); } }
public void Build() { List <byte> buffer = new List <byte>(); FunctionOffsets.Clear(); GetGlobalsFunction = null; GlobalsAddressOffset = -1; int offset = 0; foreach (Function function in Functions.Values) { if (function.Name == getGlobalsFunctionName) { GetGlobalsFunction = function; GlobalsAddressOffset = offset + 2; Debug.Assert(function.Instructions[0].CompleteInstruction == "mov rax,0AAAAAAAAAAAAAAAAh"); for (int i = 0; i < 8; i++) { function.Instructions[0].Bytes[i + 2] = 0; } } int functionLen = 0; foreach (Instruction instruction in function.Instructions) { functionLen += instruction.Bytes.Length; } FunctionOffsets.Add(function, offset); offset += functionLen; } foreach (Function function in Functions.Values) { foreach (Instruction instruction in function.Instructions) { if (instruction.IsCall && instruction.Bytes[0] == 0xE8) { string functionName = instruction.CompleteInstruction.Substring( instruction.CompleteInstruction.IndexOf(' ') + 1).Trim(); int instructionOffset = FunctionOffsets[function] + instruction.Offset; int targetFuncOffset = FunctionOffsets[Functions[functionName]]; byte[] relativeAddr = BitConverter.GetBytes(targetFuncOffset - instructionOffset - 5); instruction.Bytes[1] = relativeAddr[0]; instruction.Bytes[2] = relativeAddr[1]; instruction.Bytes[3] = relativeAddr[2]; instruction.Bytes[4] = relativeAddr[3]; } } function.BuildBytes(); buffer.AddRange(function.Bytes); } Buffer = buffer.ToArray(); Code = GenerateCode(); string outputFilePath = GetSourceFilePath(Path.ChangeExtension(outputFileName, NativeScript.GetVersionName(version) + ".cs")); if (File.Exists(outputFilePath)) { string oldCode = File.ReadAllText(outputFilePath); if (oldCode != Code) { File.WriteAllText(outputFilePath, Code); } } else { Debug.WriteLine(Code); Debugger.Break(); } }