diff --git a/DebugCompiler/ConditionalBlocks.cs b/DebugCompiler/ConditionalBlocks.cs index 470ac07..a58e691 100644 --- a/DebugCompiler/ConditionalBlocks.cs +++ b/DebugCompiler/ConditionalBlocks.cs @@ -22,7 +22,12 @@ public void LoadConditionalTokens(List tokens) { CompileTimeTokens.Clear(); foreach (var token in tokens) - CompileTimeTokens.Add(token.ToLower().Trim()); + AddConditionalTokens(token); + } + + public void AddConditionalTokens(string token) + { + CompileTimeTokens.Add(token.ToLower().Trim()); } private enum RBCBlockState diff --git a/DebugCompiler/DebugCompiler.csproj b/DebugCompiler/DebugCompiler.csproj index 152b9ae..3d938e0 100644 --- a/DebugCompiler/DebugCompiler.csproj +++ b/DebugCompiler/DebugCompiler.csproj @@ -62,6 +62,29 @@ true true + + bin\Release_Advanced\ + TRACE + true + true + pdbonly + x64 + 7.3 + prompt + true + + + bin\x64\Release_Advanced\ + TRACE + true + true + pdbonly + x64 + 7.3 + prompt + MinimumRecommendedRules.ruleset + true + @@ -126,7 +149,7 @@ - xcopy /y "$(SolutionDir)$(PlatformName)\Release\t7cinternal.dll" "$(TargetDir)" -xcopy /y "$(SolutionDir)$(PlatformName)\Release\t8cinternal.dll" "$(TargetDir)" + xcopy /y "$(SolutionDir)$(PlatformName)\Release_Advanced\t7cinternal.dll" "$(TargetDir)" +xcopy /y "$(SolutionDir)$(PlatformName)\Release_Advanced\t8cinternal.dll" "$(TargetDir)" \ No newline at end of file diff --git a/DebugCompiler/Root.cs b/DebugCompiler/Root.cs index c027e9d..defe3d4 100644 --- a/DebugCompiler/Root.cs +++ b/DebugCompiler/Root.cs @@ -307,13 +307,17 @@ private int cmd_Inject(string[] args, string[] opts) { return Error("Invalid arguments. Specified file does not exist."); } + var cfg = new CompilerConfig(); - Games game = Games.T7; if (args.Length > 1) { - if (!Enum.TryParse(args[1], true, out game)) + if (!Enum.TryParse(args[1], true, out Games game)) { - game = Games.T7; + cfg.Game = Games.T7; + } + else + { + cfg.Game = game; } } @@ -326,8 +330,8 @@ private int cmd_Inject(string[] args, string[] opts) { return Error("Failed to read the file specified"); } - var path = args.Length > 2 ? args[2] : (game == Games.T7 ? @"scripts/shared/duplicaterender_mgr.gsc" : @"scripts/zm_common/load.gsc"); - PointerEx injresult = InjectScript(path, buffer, game, hotmode.none, false); + var path = args.Length > 2 ? args[2] : (cfg.Game == Games.T7 ? @"scripts/shared/duplicaterender_mgr.gsc" : @"scripts/zm_common/load.gsc"); + PointerEx injresult = InjectScript(path, buffer, cfg, false); Console.WriteLine(); Console.ForegroundColor = !injresult ? ConsoleColor.Green : ConsoleColor.Red; Console.WriteLine($"\t[{path}]: {(!injresult ? "Injected" : $"Failed to Inject ({injresult:X})")}\n"); @@ -506,33 +510,118 @@ private class SourceTokenDef public Dictionary LineMappings = new Dictionary(); } - private int cmd_Compile(string[] args, string[] opts) + private struct InjectionPoint { + public byte[] ByteCode; + public bool Client; + } - List conditionalSymbols = new List(); - string replaceScript = null; - string scriptLocation = args.Length > 0 ? args[0] : "scripts"; + private class CompilerConfig + { + internal List ConditionalSymbols { get; set; } = new List(); + internal Platforms Platform { get; set; } = Platforms.PC; + internal Games Game { get; set; } = Games.T7; + internal hotmode Hot { get; set; } = hotmode.none; + internal bool Noruntime { get; set; } = false; + internal bool BuildScript { get; set; } = false; + internal bool CompileOnly { get; set; } = false; + internal bool InjectClient { get; set; } = false; + internal bool InjectServer { get; set; } = true; + internal bool InjectDLL { get; set; } = false; + internal bool DllBuiltins { get; set; } = false; + internal bool DllDetours { get; set; } = false; + internal bool DllLazyLink { get; set; } = false; + internal string ReplaceScript { get; set; } = null; + internal string ReplaceScriptClient { get; set; } = null; + internal string ScriptLocation { get; set; } = "scripts"; + + internal void ReadConfig(string line) + { + var split = line.Trim().Split('='); + if (split.Length < 2) return; + switch (split[0].ToLower().Trim()) + { + case "symbols": + foreach (string token in split[1].Trim().Split(',')) + { + ConditionalSymbols.Add(token); + } + break; + case "script": + ReplaceScript = split[1].ToLower().Trim().Replace("\\", "/"); + break; + case "script_client": + ReplaceScriptClient = split[1].ToLower().Trim().Replace("\\", "/"); + break; + case "scriptlocation": + ScriptLocation = split[1]; + break; + case "client": + InjectClient = split[1].ToLower().Trim() == "true"; + break; + case "server": + InjectServer = split[1].ToLower().Trim() == "true"; + break; + case "dll": + InjectDLL = split[1].ToLower().Trim() == "true"; + break; + case "dll.lazylink": + DllLazyLink = split[1].ToLower().Trim() == "true"; + break; + case "dll.detours": + DllDetours = split[1].ToLower().Trim() == "true"; + break; + case "dll.builtins": + DllBuiltins = split[1].ToLower().Trim() == "true"; + break; + case "game": + if (!Enum.TryParse(split[1].ToLower().Trim().Replace("\\", "/"), true, out Games game)) + { + Game = Games.T7; + } else + { + Game = game; + } + break; + case "hot": + if (!Enum.TryParse(split[1].ToLower().Trim(), true, out hotmode hot)) + { + Hot = hotmode.none; + } else + { + Hot = hot; + } + break; + case "noruntime": + Noruntime = split[1].ToLower().Trim() == "true"; + break; + } + } + } + + private int cmd_Compile(string[] args, string[] opts) + { + CompilerConfig cfg = new CompilerConfig(); + if (args.Length > 0) + { + cfg.ScriptLocation = args[0]; + } - Platforms platform = Platforms.PC; - Games game = Games.T7; - hotmode hot = hotmode.none; - bool noruntime = false; - bool buildScript = false; - bool compileOnly = false; foreach (string opt in opts) { - if (opt == "--build") + if (opt == "--build" || opt == "-b") { - buildScript = true; - } - else if (opt == "--compile") + cfg.BuildScript = true; + } else if (opt == "--compile" || opt == "-c") { - compileOnly = true; - } - else if (opt.Length > 2 && opt[1] == 'D') + cfg.CompileOnly = true; + } else if (opt.Length > 2 && opt[1] == 'D') { // -Dsomething - conditionalSymbols.Add(opt.Substring(2)); + cfg.ConditionalSymbols.Add(opt.Substring(2)); + } else if (opt.Length > 2 && opt[1] == 'C') + { // -Coption=value + cfg.ReadConfig(opt.Substring(2)); } } @@ -540,9 +629,8 @@ private int cmd_Compile(string[] args, string[] opts) { try { - game = (Games)Enum.Parse(typeof(Games), args[1], true); - } - catch { } + cfg.Game = (Games)Enum.Parse(typeof(Games), args[1], true); + } catch { } } if (File.Exists("gsc.conf")) @@ -550,179 +638,210 @@ private int cmd_Compile(string[] args, string[] opts) foreach (string line in File.ReadAllLines("gsc.conf")) { if (line.Trim().StartsWith("#")) continue; - var split = line.Trim().Split('='); - if (split.Length < 2) continue; - switch (split[0].ToLower().Trim()) - { - case "symbols": - foreach (string token in split[1].Trim().Split(',')) - { - conditionalSymbols.Add(token); - } - break; - case "script": - replaceScript = split[1].ToLower().Trim().Replace("\\", "/"); - break; - case "scriptlocation": - scriptLocation = split[1]; - break; - case "game": - if (!Enum.TryParse(split[1].ToLower().Trim().Replace("\\", "/"), true, out game)) - { - game = Games.T7; - } - break; - case "hot": - if (!Enum.TryParse(split[1].ToLower().Trim(), true, out hot)) - { - hot = hotmode.none; - } - break; - case "noruntime": - noruntime = split[1].ToLower().Trim() == "true"; - break; - } + cfg.ReadConfig(line); } } - if (!Directory.Exists(scriptLocation)) + + if (!Directory.Exists(cfg.ScriptLocation)) return Error($"Script location is either not a directory or does not exist {args[0]}"); - bool isT7 = game == Games.T7; - - //if (args.Length > 1) - //{ - // if(!Enum.TryParse(args[1], true, out game)) - // { - // game = Games.T7; - // } - //} - - string source = ""; - CompiledCode code; - List SourceTokens = new List(); - StringBuilder sb = new StringBuilder(); - int CurrentLineCount = 0; - int CurrentCharCount = 0; - foreach (string f in Directory.GetFiles(scriptLocation, "*.gsc", SearchOption.AllDirectories)) - { - var CurrentSource = new SourceTokenDef(); - CurrentSource.FilePath = f.Replace(scriptLocation, "").Substring(1).Replace("\\", "/"); - CurrentSource.LineStart = CurrentLineCount; - CurrentSource.CharStart = CurrentCharCount; - foreach (var line in File.ReadAllLines(f)) - { - CurrentSource.LineMappings[CurrentLineCount] = (CurrentCharCount, CurrentCharCount + line.Length + 1); - sb.Append(line); - sb.Append("\n"); - CurrentLineCount += 1; - CurrentCharCount += line.Length + 1; // + \n - } - CurrentSource.LineEnd = CurrentLineCount; - CurrentSource.CharEnd = CurrentCharCount; - // Console.WriteLine($"{CurrentSource.FilePath} start {CurrentSource.LineStart} end {CurrentSource.LineEnd}"); - SourceTokens.Add(CurrentSource); - sb.Append("\n"); // remember that this is here because its going to fuck up irony - end_loop:; + bool isT7 = cfg.Game == Games.T7; + cfg.ReplaceScript = cfg.ReplaceScript ?? (isT7 ? @"scripts/shared/duplicaterender_mgr.gsc" : @"scripts/zm_common/load.gsc"); + cfg.ReplaceScriptClient = cfg.ReplaceScriptClient ?? "scripts/zm_common/load.csc"; + + + // add custom symbol to control GSC/CSC script compilation + cfg.ConditionalSymbols.Add(isT7 ? "BO3" : "BO4"); + if (cfg.InjectClient) + { + cfg.ConditionalSymbols.Add("_INJECT_CLIENT"); } - replaceScript = replaceScript ?? (isT7 ? @"scripts/shared/duplicaterender_mgr.gsc" : @"scripts/zm_common/load.gsc"); - source = sb.ToString(); - var ppc = new ConditionalBlocks(); - conditionalSymbols.Add(isT7 ? "BO3" : "BO4"); - ppc.LoadConditionalTokens(conditionalSymbols); - - try + if (cfg.InjectServer) + { + cfg.ConditionalSymbols.Add("_INJECT_SERVER"); + } + if (cfg.DllBuiltins) + { + cfg.ConditionalSymbols.Add("_SUPPORTS_BUILTINS"); + } + if (cfg.DllDetours) { - source = ppc.ParseSource(source); + cfg.ConditionalSymbols.Add("_SUPPORTS_DETOURS"); } - catch(CBSyntaxException e) + if (cfg.DllLazyLink) { - int errorCharPos = e.ErrorPosition; - int numLineBreaks = 0; - foreach(var stok in SourceTokens) + cfg.ConditionalSymbols.Add("_SUPPORTS_LAZYLINK"); + } + cfg.ConditionalSymbols.Add("_SUPPORTS_GCSC"); + if (cfg.Game == Games.T8) + { + cfg.ConditionalSymbols.Add("_SUPPORTS_EVENTFUNC"); + } + + string hpath = "hashes.txt"; + StringBuilder hashes = new StringBuilder(); + List bytecode = new List(); + + if (!cfg.InjectServer && !cfg.InjectClient) + { + return Error("Inject server and inject client are both set to false"); + } + + foreach (bool client in new bool[] { true, false }) + { + if (client) + { + if (!cfg.InjectClient) + { + continue; + } + if (cfg.Game != Games.T8) + { + return Error("Can't Inject client outside of Black Ops 4"); + } + } else + { + if (!cfg.InjectServer) + { + continue; + } + } + + string source = ""; + CompiledCode code; + List SourceTokens = new List(); + StringBuilder sb = new StringBuilder(); + int CurrentLineCount = 0; + int CurrentCharCount = 0; + string[] instanceScripts = Directory.GetFiles(cfg.ScriptLocation, client ? "*.csc" : "*.gsc", SearchOption.AllDirectories); + string[] globalScripts = Directory.GetFiles(cfg.ScriptLocation, "*.gcsc", SearchOption.AllDirectories); + foreach (string f in instanceScripts.Concat(globalScripts)) { - do + var CurrentSource = new SourceTokenDef(); + CurrentSource.FilePath = f.Replace(cfg.ScriptLocation, "").Substring(1).Replace("\\", "/"); + CurrentSource.LineStart = CurrentLineCount; + CurrentSource.CharStart = CurrentCharCount; + foreach (var line in File.ReadAllLines(f)) { - if(errorCharPos < stok.CharStart || errorCharPos > stok.CharEnd) + CurrentSource.LineMappings[CurrentLineCount] = (CurrentCharCount, CurrentCharCount + line.Length + 1); + sb.Append(line); + sb.Append("\n"); + CurrentLineCount += 1; + CurrentCharCount += line.Length + 1; // + \n + } + CurrentSource.LineEnd = CurrentLineCount; + CurrentSource.CharEnd = CurrentCharCount; + // Console.WriteLine($"{CurrentSource.FilePath} start {CurrentSource.LineStart} end {CurrentSource.LineEnd}"); + SourceTokens.Add(CurrentSource); + sb.Append("\n"); // remember that this is here because its going to fuck up irony + } + + source = sb.ToString(); + var ppc = new ConditionalBlocks(); + ppc.LoadConditionalTokens(cfg.ConditionalSymbols); + if (client) + { + ppc.AddConditionalTokens("_CSC"); + } + else + { + ppc.AddConditionalTokens("_GSC"); + } + + try + { + source = ppc.ParseSource(source); + } catch (CBSyntaxException e) + { + int errorCharPos = e.ErrorPosition; + int numLineBreaks = 0; + foreach (var stok in SourceTokens) + { + do { - break; // havent reached the target index set yet + if (errorCharPos < stok.CharStart || errorCharPos > stok.CharEnd) + { + break; // havent reached the target index set yet + } + // now we have the source file we want + errorCharPos -= numLineBreaks; // adjust for inserted linebreaks between files + foreach (var line in stok.LineMappings) + { + var constraints = line.Value; + if (errorCharPos < constraints.CStart || errorCharPos > constraints.CEnd) + { + continue; // havent found the index we want yet + } + // found the target line + return Error($"{e.Message} in scripts/{stok.FilePath} at line {line.Key - stok.LineStart}, position {errorCharPos - constraints.CStart}"); + } } - // now we have the source file we want - errorCharPos -= numLineBreaks; // adjust for inserted linebreaks between files - foreach(var line in stok.LineMappings) + while (false); + numLineBreaks++; + } + return Error(e.Message); + } + + code = Compiler.Compile(cfg.Platform, cfg.Game, Modes.MP, false, source); + if (code.Error != null && code.Error.Length > 0) + { + if (code.Error.LastIndexOf("line=") < 0) + { + return Error(code.Error); + } + int iStart = code.Error.LastIndexOf("line=") + "line=".Length; + int iLength = code.Error.LastIndexOf("]") - iStart; + int line = int.Parse(code.Error.Substring(iStart, iLength)); + // Console.WriteLine(code.Error + " :: " + line); + foreach (var stok in SourceTokens) + { + do { - var constraints = line.Value; - if(errorCharPos < constraints.CStart || errorCharPos > constraints.CEnd) + if (stok.LineStart <= line && stok.LineEnd >= line) { - continue; // havent found the index we want yet + return Error($"Syntax error in scripts/{stok.FilePath} around line {line - stok.LineStart + 1}"); } - // found the target line - return Error($"{e.Message} in scripts/{stok.FilePath} at line {line.Key - stok.LineStart}, position {errorCharPos - constraints.CStart}"); } + while (false); + line--; // acccount for linebreaks appended to each file } - while (false); - numLineBreaks++; + return Error(code.Error); } - return Error(e.Message); - } - code = Compiler.Compile(platform, game, Modes.MP, false, source); - if (code.Error != null && code.Error.Length > 0) - { - if(code.Error.LastIndexOf("line=") < 0) + string cpath = $"compiled.{(client ? (code.RequiresGSI ? "csic" : "cscc") : (code.RequiresGSI ? "gsic" : "gscc"))}"; + File.WriteAllBytes(cpath, code.CompiledScript); + foreach (var kvp in code.HashMap) { - return Error(code.Error); + hashes.AppendLine($"0x{kvp.Key:X}, {kvp.Value}"); } - int iStart = code.Error.LastIndexOf("line=") + "line=".Length; - int iLength = code.Error.LastIndexOf("]") - iStart; - int line = int.Parse(code.Error.Substring(iStart, iLength)); - // Console.WriteLine(code.Error + " :: " + line); - foreach (var stok in SourceTokens) + + if (code.OpcodeEmissions != null) { - do + byte[] opsRaw = new byte[code.OpcodeEmissions.Count * 4]; + for (int i = 0; i < code.OpcodeEmissions.Count; i++) { - if(stok.LineStart <= line && stok.LineEnd >= line) - { - return Error($"Syntax error in scripts/{stok.FilePath} around line {line - stok.LineStart + 1}"); - } + BitConverter.GetBytes(code.OpcodeEmissions[i]).CopyTo(opsRaw, i * 4); } - while (false); - line--; // acccount for linebreaks appended to each file + File.WriteAllBytes(client ? "compiledclient.omap" : "compiled.omap", opsRaw); } - return Error(code.Error); - } + Success(cpath); - string cpath = $"compiled.{(code.RequiresGSI ? "gsic" : "gscc")}"; - File.WriteAllBytes(cpath, code.CompiledScript); - string hpath = "hashes.txt"; - StringBuilder hashes = new StringBuilder(); - foreach(var kvp in code.HashMap) - { - hashes.AppendLine($"0x{kvp.Key:X}, {kvp.Value}"); + bytecode.Add(new InjectionPoint { + ByteCode = code.CompiledScript, + Client = client + }); } File.WriteAllText(hpath, hashes.ToString()); - if(code.OpcodeEmissions != null) - { - byte[] opsRaw = new byte[code.OpcodeEmissions.Count * 4]; - for(int i = 0; i < code.OpcodeEmissions.Count; i++) - { - BitConverter.GetBytes(code.OpcodeEmissions[i]).CopyTo(opsRaw, i * 4); - } - File.WriteAllBytes("compiled.omap", opsRaw); - } - - Success(cpath); - if (compileOnly) + if (cfg.CompileOnly) { return Success("Script compiled."); - } - else if (buildScript) + } else if (cfg.BuildScript) { Success("Script compiled. Injecting..."); - } - else + } else { Success("Script compiled. Press I to inject or anything else to continue"); @@ -730,14 +849,22 @@ private int cmd_Compile(string[] args, string[] opts) return 0; } - byte[] data = code.CompiledScript; + bool injected = false; ; - PointerEx injresult = InjectScript(replaceScript, code.CompiledScript, game, hot, noruntime); - Console.WriteLine(); - Console.ForegroundColor = !injresult ? ConsoleColor.Green : ConsoleColor.Red; - Console.WriteLine($"\t[{replaceScript}]: {(!injresult ? "Injected" : $"Failed to Inject ({injresult:X})")}\n"); + foreach (var bc in bytecode) + { + byte[] data = bc.ByteCode; + + string rscript = bc.Client ? cfg.ReplaceScriptClient : cfg.ReplaceScript; + PointerEx injresult = InjectScript(rscript, data, cfg, bc.Client); + Console.WriteLine(); + Console.ForegroundColor = !injresult ? ConsoleColor.Green : ConsoleColor.Red; + Console.WriteLine($"\t[{rscript}]: {(!injresult ? "Injected" : $"Failed to Inject ({injresult:X})")}\n"); + + injected = injected || !injresult; + } - if (!injresult && hot == hotmode.none) + if (injected && cfg.Hot == hotmode.none) { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("Press any key to reset gsc parsetree... If in game, you are probably going to crash.\n"); @@ -782,13 +909,13 @@ private void NoExcept(Action a) private int InjectedBuffSize; private T7SPT InjectedScript; private int OriginalPID = 0; - private int InjectScript(string replacePath, byte[] buffer, Games game, hotmode hot, bool noruntime) + private int InjectScript(string replacePath, byte[] buffer, CompilerConfig cfg, bool client) { - LastGameInjected = game; - switch (game) + LastGameInjected = cfg.Game; + switch (cfg.Game) { - case Games.T7: return InjectT7(replacePath, buffer, hot, noruntime); - case Games.T8: return InjectT8(replacePath, buffer); + case Games.T7: return InjectT7(replacePath, buffer, cfg.Hot, cfg.Noruntime); + case Games.T8: return InjectT8(replacePath, buffer, cfg, client); } return Error("Invalid game provided to inject."); } @@ -1000,11 +1127,18 @@ private int InjectT7(string replacePath, byte[] buffer, hotmode hot, bool norunt return 2; } - private T8InjectCache InjectCache; + private T8InjectCache InjectCache = new T8InjectCache(); + private T8InjectCache InjectCacheClient = new T8InjectCache(); - private int InjectT8(string replacePath, byte[] buffer) + private int InjectT8(string replacePath, byte[] buffer, CompilerConfig cfg, bool client) { - NoExcept(FreeT8Script); + if (client) + { + NoExcept(FreeT8ScriptClient); + } else + { + NoExcept(FreeT8ScriptServer); + } GSICInfoT8 gsi = null; if (BitConverter.ToInt64(buffer, 0) != 0x36000A0D43534780) { @@ -1016,15 +1150,15 @@ private int InjectT8(string replacePath, byte[] buffer) using (MemoryStream ms = new MemoryStream(buffer)) using (BinaryReader reader = new BinaryReader(ms)) { - T7ScriptObject.GSIFields currentField = T7ScriptObject.GSIFields.Detours; + T89ScriptObject.GSIFields currentField = T89ScriptObject.GSIFields.Detours; reader.BaseStream.Position += 4; gsi = new GSICInfoT8(); for (int numFields = reader.ReadInt32(); numFields > 0; numFields--) { - currentField = (T7ScriptObject.GSIFields)reader.ReadInt32(); + currentField = (T89ScriptObject.GSIFields)reader.ReadInt32(); switch (currentField) { - case T7ScriptObject.GSIFields.Detours: + case T89ScriptObject.GSIFields.Detours: int numdetours = reader.ReadInt32(); for (int j = 0; j < numdetours; j++) { @@ -1056,24 +1190,43 @@ private int InjectT8(string replacePath, byte[] buffer) var SPTEntries = bo4.GetArray(sptGlob, sptCount); replacePath = replacePath.ToLower().Trim().Replace("\\", "/"); var surrogateScript = T8s64Hash(replacePath); // script we are hooking - var targetScript = 0x124CECFF7280BE52; // script we are replacing - InjectCache.hSurrogate = 0; - InjectCache.hTarget = 0; + ulong targetScript; // script we are replacing + + if (client) + { + targetScript = 0x10aeb2e4f2b455a1; + } else + { + targetScript = 0x124cecff7280be52; + } + T8InjectCache cache; + + if (client) + { + cache = InjectCacheClient; + } else + { + cache = InjectCache; + } + + + cache.hSurrogate = 0; + cache.hTarget = 0; for (int i = 0; i < SPTEntries.Length; i++) { var spt = SPTEntries[i]; if (spt.ScriptName == surrogateScript) { - InjectCache.Surrogate = spt; - InjectCache.hSurrogate = sptGlob + (ulong)(i * Marshal.SizeOf(typeof(T8SPT))); + cache.Surrogate = spt; + cache.hSurrogate = sptGlob + (ulong)(i * Marshal.SizeOf(typeof(T8SPT))); } if (spt.ScriptName == targetScript) { - InjectCache.Target = spt; - InjectCache.hTarget = sptGlob + (ulong)(i * Marshal.SizeOf(typeof(T8SPT))); + cache.Target = spt; + cache.hTarget = sptGlob + (ulong)(i * Marshal.SizeOf(typeof(T8SPT))); } - if(InjectCache.hSurrogate && InjectCache.hTarget) + if(cache.hSurrogate && cache.hTarget) { break; } @@ -1081,7 +1234,7 @@ private int InjectT8(string replacePath, byte[] buffer) try { - if (!InjectCache.hSurrogate || !InjectCache.hTarget) + if (!cache.hSurrogate || !cache.hTarget) { return Error("Unable to identify critical injection information. Double check your script path, and try restarting the game. Make sure you are injecting in the pregame lobby."); } @@ -1090,47 +1243,66 @@ private int InjectT8(string replacePath, byte[] buffer) int tableOff = 0x18; // patch include - byte includeCount = bo4.GetValue(InjectCache.Surrogate.Buffer + includeOff); - PointerEx includeTable = InjectCache.Surrogate.Buffer + bo4.GetValue(InjectCache.Surrogate.Buffer + tableOff); + byte includeCount = bo4.GetValue(cache.Surrogate.Buffer + includeOff); + PointerEx includeTable = cache.Surrogate.Buffer + bo4.GetValue(cache.Surrogate.Buffer + tableOff); for (int i = 0; i < includeCount; i++) { - if (bo4.GetValue(includeTable + (i * 8)) == targetScript) + if (bo4.GetValue(includeTable + (i * 8)) == targetScript) { goto patchBuff; } } bo4.SetValue(includeTable + (includeCount * 8), targetScript); - bo4.SetValue(InjectCache.Surrogate.Buffer + includeOff, (byte)(includeCount + 1)); + bo4.SetValue(cache.Surrogate.Buffer + includeOff, (byte)(includeCount + 1)); patchBuff: - bo4.GetBytes(InjectCache.Target.Buffer + 0x8, 8).CopyTo(buffer, 0x8); // crc32 - InjectCache.hBuffer = bo4.QuickAlloc(buffer.Length); // space - bo4.SetBytes(InjectCache.hBuffer, buffer); // write to proc - bo4.SetValue(InjectCache.hTarget + 0x10, InjectCache.hBuffer); // buffer pointer redirect - InjectCache.Pid = bo4.BaseProcess.Id; - InjectCache.BufferSize = buffer.Length; - InjectCache.IsInjected = true; + bo4.GetBytes(cache.Target.Buffer + 0x8, 8).CopyTo(buffer, 0x8); // crc32 + bo4.GetBytes(cache.Target.Buffer + 0x10, 8).CopyTo(buffer, 0x10); // ScriptName + cache.hBuffer = bo4.QuickAlloc(buffer.Length); // space + bo4.SetBytes(cache.hBuffer, buffer); // write to proc + bo4.SetValue(cache.hTarget + 0x10, cache.hBuffer); // buffer pointer redirect + cache.Pid = bo4.BaseProcess.Id; + cache.BufferSize = buffer.Length; + cache.IsInjected = true; try { - string exeFilePath = Assembly.GetExecutingAssembly().Location; - //var result = bo4.Call(bo4.GetProcAddress(@"kernel32.dll", @"LoadLibraryA"), Path.Combine(Path.GetDirectoryName(exeFilePath), "t8cinternal.dll")); - //bo4.Refresh(); - - //if (result == 0) - //{ - // return 4; - //} - - //bo4.Call(bo4.GetProcAddress(@"t8cinternal.dll", @"RemoveDetours")); - //if (gsi != null) - //{ - // // detours - // if (gsi.Detours.Count > 0) - // { - // bo4.Call(bo4.GetProcAddress(@"t8cinternal.dll", @"RegisterDetours"), gsi.PackDetours(), gsi.Detours.Count, (long)InjectCache.hBuffer); - // } - //} + if (cfg.InjectDLL) + { + string exeFilePath = Assembly.GetExecutingAssembly().Location; + var result = bo4.Call(bo4.GetProcAddress(@"kernel32.dll", @"LoadLibraryA"), Path.Combine(Path.GetDirectoryName(exeFilePath), "t8cinternal.dll")); + bo4.Refresh(); + + if (result == 0) + { + return 4; + } + + if (cfg.DllBuiltins) + { + bo4.Call(bo4.GetProcAddress(@"t8cinternal.dll", @"T8Dll_BuiltinsInit")); + } + if (cfg.DllLazyLink) + { + bo4.Call(bo4.GetProcAddress(@"t8cinternal.dll", @"T8Dll_LazyLinkInit")); + } + if (cfg.DllDetours) + { + bo4.Call(bo4.GetProcAddress(@"t8cinternal.dll", @"T8Dll_DetoursInit")); + if (gsi != null) + { + // detours + if (gsi.Detours.Count > 0) + { + bo4.Call(bo4.GetProcAddress(@"t8cinternal.dll", @"RegisterDetours"), gsi.PackDetours(), gsi.Detours.Count, (long)cache.hBuffer, client ? 1 : 0); + } + } else + { + // done inside RegisterDetours, useless with gsi + bo4.Call(bo4.GetProcAddress(@"t8cinternal.dll", @"RemoveDetours"), client ? 1 : 0); + } + } + } } catch (Exception e) { @@ -1152,40 +1324,74 @@ private int InjectT8(string replacePath, byte[] buffer) private void FreeT8Script() { - if (!InjectCache.IsInjected) + ProcessEx bo4 = "blackops4"; + if (bo4 is null) { return; } + FreeT8ScriptCache(bo4, false); + FreeT8ScriptCache(bo4, true); + } + + private void FreeT8ScriptClient() + { ProcessEx bo4 = "blackops4"; if (bo4 is null) { return; } - if (bo4.BaseProcess.Id != InjectCache.Pid) + FreeT8ScriptCache(bo4, true); + } + + private void FreeT8ScriptServer() + { + ProcessEx bo4 = "blackops4"; + if (bo4 is null) { return; } - bo4.OpenHandle(); + FreeT8ScriptCache(bo4, false); + } + + private void FreeT8ScriptCache(ProcessEx bo4, bool client) + { + T8InjectCache cache; + + if (client) + { + cache = InjectCacheClient; + } else + { + cache = InjectCache; + } + + if (!cache.IsInjected) + { + return; + } + if (bo4.BaseProcess.Id != cache.Pid) + { + return; + } + bo4.OpenHandle(); try { // free allocated space - ProcessEx.VirtualFreeEx(bo4.Handle, InjectCache.hBuffer, (uint)InjectCache.BufferSize, (int)EnvironmentEx.FreeType.Release); + ProcessEx.VirtualFreeEx(bo4.Handle, cache.hBuffer, (uint)cache.BufferSize, (int)EnvironmentEx.FreeType.Release); // Patch spt struct - bo4.SetStruct(InjectCache.hTarget, InjectCache.Target); - - // Reset hooked detours - // bo4.Call(bo4.GetProcAddress(@"t8cinternal.dll", @"RemoveDetours")); - } - finally + bo4.SetStruct(cache.hTarget, cache.Target); + bo4.Call(bo4.GetProcAddress(@"t8cinternal.dll", @"RemoveDetours"), client ? 1 : 0); + cache.IsInjected = false; + } finally { bo4.CloseHandle(); } - } +} private void FreeT7Script() { @@ -1208,7 +1414,7 @@ private void FreeT7Script() bo3.CloseHandle(); } - private struct T8InjectCache + private class T8InjectCache { public T8SPT Surrogate; public T8SPT Target; diff --git a/External/External.csproj b/External/External.csproj index 7c32828..201619a 100644 --- a/External/External.csproj +++ b/External/External.csproj @@ -102,6 +102,33 @@ prompt true + + bin\Release_Advanced\ + TRACE + true + true + AnyCPU + 7.3 + prompt + + + bin\x64\Release_Advanced\ + TRACE + true + true + x64 + 7.3 + prompt + + + bin\x86\Release_Advanced\ + TRACE + true + true + x86 + 7.3 + prompt + ..\packages\Iced.1.17.0\lib\net45\Iced.dll diff --git a/ExternalTestingUtility/t7cInstaller.csproj b/ExternalTestingUtility/t7cInstaller.csproj index 612a4d4..8231d2a 100644 --- a/ExternalTestingUtility/t7cInstaller.csproj +++ b/ExternalTestingUtility/t7cInstaller.csproj @@ -55,6 +55,24 @@ app.manifest + + bin\Release_Advanced\ + TRACE + true + AnyCPU + 7.3 + prompt + true + + + bin\x64\Release_Advanced\ + TRACE + true + x64 + 7.3 + prompt + true + diff --git a/T7CompilerLib/T7CompilerLib.csproj b/T7CompilerLib/T7CompilerLib.csproj index 3a02daa..f5ea2b8 100644 --- a/T7CompilerLib/T7CompilerLib.csproj +++ b/T7CompilerLib/T7CompilerLib.csproj @@ -58,6 +58,24 @@ prompt MinimumRecommendedRules.ruleset + + bin\Release_Advanced\ + TRACE + true + pdbonly + AnyCPU + 7.1 + prompt + + + bin\x64\Release_Advanced\ + TRACE + true + x64 + 7.1 + prompt + MinimumRecommendedRules.ruleset + diff --git a/T8CompilerLib/OpCodes/T89OP_LazyGetFunction.cs b/T8CompilerLib/OpCodes/T89OP_LazyGetFunction.cs new file mode 100644 index 0000000..43ff698 --- /dev/null +++ b/T8CompilerLib/OpCodes/T89OP_LazyGetFunction.cs @@ -0,0 +1,50 @@ +using System; + +namespace T89CompilerLib.OpCodes +{ + public class T89OP_LazyGetFunction : T89OpCode + { + private uint Namespace; + private uint Function; + private ulong Script; + + internal T89OP_LazyGetFunction(uint ns, uint func, ulong script) : base(ScriptOpCode.LazyGetFunction) + { + Script = script; + Namespace = ns; + Function = func; + } + + protected override byte[] Serialize(ushort EmissionValue) + { + byte[] data = new byte[GetSize()]; + + base.Serialize(EmissionValue).CopyTo(data, 0); + + uint WriteAddress = GetCommitDataAddress().AlignValue(0x4) - CommitAddress; + + BitConverter.GetBytes(Namespace).CopyTo(data, WriteAddress); + BitConverter.GetBytes(Function).CopyTo(data, WriteAddress + 4); + BitConverter.GetBytes(Script).CopyTo(data, WriteAddress + 8); + + return data; + } + + public override uint GetCommitDataAddress() + { + return CommitAddress + T89OP_SIZE; + } + + //OP_CODE 0x2 + //NUMPARAMS 0x1 + //padding 0x1 + //QWORD ALIGN + //Function (2*4) + //Script (8) + //0 (x4) + public override uint GetSize() + { + return (GetCommitDataAddress()).AlignValue(0x4) + (4 * 4) - CommitAddress; + } + } +} diff --git a/T8CompilerLib/OpCodes/T89OpCode.cs b/T8CompilerLib/OpCodes/T89OpCode.cs index 94d17be..70e8428 100644 --- a/T8CompilerLib/OpCodes/T89OpCode.cs +++ b/T8CompilerLib/OpCodes/T89OpCode.cs @@ -280,6 +280,7 @@ public enum ScriptOpCode : byte ClassFunctionThreadCall2, EvalLocalVariableCached2, EvalLocalVariableRefCached2, + LazyGetFunction, Invalid = 0xFF, } diff --git a/T8CompilerLib/ScriptComponents/T89ExportsSection.cs b/T8CompilerLib/ScriptComponents/T89ExportsSection.cs index c287402..0e18ca6 100644 --- a/T8CompilerLib/ScriptComponents/T89ExportsSection.cs +++ b/T8CompilerLib/ScriptComponents/T89ExportsSection.cs @@ -166,15 +166,16 @@ public static void ReadExports(ref byte[] data, uint lpExportsSection, ushort Nu /// /// Hashed function ID for export /// Hashed namespace ID for export + /// Hashed callback ID for event export /// Number of parameters for this function /// A new script export object. If the function already exists, a reference to the existing object. - public T89ScriptExport Add(uint FunctionID, uint NamespaceID, byte NumParams) + public T89ScriptExport Add(uint FunctionID, uint NamespaceID, uint CallbackEvent, byte NumParams) { if (ScriptExports.ContainsKey(FunctionID)) return ScriptExports[FunctionID]; T89ScriptExport Previous = FirstExport?.Last(); - T89ScriptExport export = T89ScriptExport.New(Previous, FunctionID, NamespaceID, NamespaceID, NumParams, Script); + T89ScriptExport export = T89ScriptExport.New(Previous, FunctionID, NamespaceID, CallbackEvent, NumParams, Script); if (FirstExport == null) FirstExport = export; @@ -248,7 +249,7 @@ public sealed class T89ScriptExport public T89ScriptObject Script { get; private set;} private T89ScriptExport(T89ScriptObject script) { Script = script; } //prevent public initializers - internal static T89ScriptExport New(T89ScriptExport Previous, uint fid, uint ns, uint ns2, byte pcount, T89ScriptObject script) + internal static T89ScriptExport New(T89ScriptExport Previous, uint fid, uint ns, uint ce, byte pcount, T89ScriptObject script) { T89ScriptExport Export = new T89ScriptExport(script); if (Previous != null) @@ -258,7 +259,7 @@ internal static T89ScriptExport New(T89ScriptExport Previous, uint fid, uint ns, } Export.FunctionID = fid; Export.Namespace = ns; - Export.Namespace2 = ns2; + Export.CallbackEvent = ce; Export.NumParams = pcount; Export.Flags = 0; Export.Locals = new T89OP_SafeCreateLocalVariables(); @@ -278,9 +279,10 @@ internal static T89ScriptExport New(T89ScriptExport Previous, uint fid, uint ns, public uint LoadedOffset { get; private set; } public uint FunctionID { get; private set; } public uint Namespace { get; private set; } - public uint Namespace2 { get; private set; } + public uint CallbackEvent { get; private set; } public byte NumParams { get; private set; } public byte Flags { get; set; } + internal uint LoadedSize; /// /// This is used when we want to perform quick removes/adds from the table @@ -403,7 +405,7 @@ public static void ReadExport(ref byte[] data, ref uint lpExportPtr, ref ushort export.FunctionID = reader.ReadUInt32(); export.Namespace = reader.ReadUInt32(); - export.Namespace2 = reader.ReadUInt32(); + export.CallbackEvent = reader.ReadUInt32(); export.NumParams = reader.ReadByte(); export.Flags = reader.ReadByte(); export.FriendlyName = "func_" + export.FunctionID.ToString("X"); @@ -470,6 +472,8 @@ public void Commit(ref byte[] data, uint NextExportPtr, T89ScriptMetadata Emissi currOp?.Commit(ref OpCodeData, ref baseaddress, EmissionTable); currOp = currOp.NextOpCode; } + LoadedOffset = (uint)ByteCodeAddress; + LoadedSize = baseaddress - (uint)ByteCodeAddress; byte[] NewBuffer = new byte[ByteCodeAddress + OpCodeData.Count]; @@ -487,7 +491,14 @@ public void Commit(ref byte[] data, uint NextExportPtr, T89ScriptMetadata Emissi writer.Write(ByteCodeAddress); writer.Write(FunctionID); writer.Write(Namespace); - writer.Write(Namespace); + if (CallbackEvent == 0) + { + writer.Write(Namespace); + } + else + { + writer.Write(CallbackEvent); + } writer.Write(NumParams); writer.Write((byte)Flags); writer.Write((ushort)0x0); @@ -635,6 +646,16 @@ public T89OpCode AddGetString(T89StringTableEntry str, bool isIstring = false) return __addop_internal(new T89OP_GetString(isIstring ? ScriptOpCode.GetIString : ScriptOpCode.GetString, str)); } + /// + /// Add a lazy get function reference + /// + /// + /// + public T89OpCode AddLazyGetFunction(ulong script, uint ns, uint func) + { + return __addop_internal(new T89OP_LazyGetFunction(ns, func, script)); + } + /// /// Try to emit a local variable. Will throw an ArgumentException if the local cant be resolved /// diff --git a/T8CompilerLib/T89CompilerLib.csproj b/T8CompilerLib/T89CompilerLib.csproj index 6e80052..0414513 100644 --- a/T8CompilerLib/T89CompilerLib.csproj +++ b/T8CompilerLib/T89CompilerLib.csproj @@ -58,6 +58,24 @@ prompt MinimumRecommendedRules.ruleset + + bin\Release_Advanced\ + TRACE + true + pdbonly + AnyCPU + 7.1 + prompt + + + bin\x64\Release_Advanced\ + TRACE + true + x64 + 7.1 + prompt + MinimumRecommendedRules.ruleset + @@ -67,6 +85,7 @@ + diff --git a/T8CompilerLib/T89ScriptObject.cs b/T8CompilerLib/T89ScriptObject.cs index fb8a31a..81aef16 100644 --- a/T8CompilerLib/T89ScriptObject.cs +++ b/T8CompilerLib/T89ScriptObject.cs @@ -29,6 +29,7 @@ public sealed class T89ScriptObject "function_", "namespace_", "var_", + "event_", "hash_", "script_" }; @@ -116,6 +117,10 @@ public byte[] Serialize() byte[] DataBuffer = new byte[0]; Header.Commit(ref DataBuffer, ref __header__); Header.CommitHeader(ref DataBuffer, 0u); + if (UsingGSI) + { + EmitGSIHeader(ref DataBuffer); + } return DataBuffer; } @@ -295,6 +300,50 @@ public void Deserialize(BinaryReader reader) reader.ReadBytes(DetourNameMaxLength + 1 - 8); } } + + public enum GSIFields + { + Detours = 0 + } + + private void EmitGSIHeader(ref byte[] data) + { + List NewHeader = new List(); + NewHeader.AddRange(new byte[] { (byte)'G', (byte)'S', (byte)'I', (byte)'C' }); + NewHeader.AddRange(BitConverter.GetBytes((int)0)); // num fields added + + int numFields = 0; + // Emit Detours + if (Detours.Count > 0) + { + numFields++; + + // Write the field type and the number of entries + NewHeader.AddRange(BitConverter.GetBytes((int)GSIFields.Detours)); + NewHeader.AddRange(BitConverter.GetBytes(Detours.Count)); + + foreach (ScriptDetour detour in Detours.Values) + { + // Apply post-serialize information + detour.FixupOffset = Exports.ScriptExports[detour.FixupName].LoadedOffset; + detour.FixupSize = Exports.ScriptExports[detour.FixupName].LoadedSize; + + // each detour should be exactly 256 bytes + NewHeader.AddRange(detour.Serialize()); + } + } + + // copy the header + byte[] finalData = new byte[data.Length + NewHeader.Count]; + NewHeader.ToArray().CopyTo(finalData, 0); + + // write number of fields + BitConverter.GetBytes(numFields).CopyTo(finalData, 0x4); + + // copy the gsc script + data.CopyTo(finalData, NewHeader.Count); + data = finalData; + } } /// @@ -376,6 +425,11 @@ public ushort this[ScriptOpCode indexer] if (ReverseOps[GetVMType()].TryGetValue(indexer, out ushort val)) return val; + if (indexer == ScriptOpCode.LazyGetFunction) + { + return 0x16; // hardcoded ig + } + Console.WriteLine($"Platform is missing opcode: {indexer.ToString()}"); return 0xFFFF; //invalid } @@ -425,6 +479,7 @@ public enum ScriptExportFlags RTLoaded = 0x1, AutoExec = 0x2, Private = 0x4, - VirtualParams = 0x20 + VirtualParams = 0x20, + Event = 0x40 } } diff --git a/TreyarchCompiler.sln b/TreyarchCompiler.sln index 0207a30..2ac5034 100644 --- a/TreyarchCompiler.sln +++ b/TreyarchCompiler.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30404.54 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.33424.131 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "T7CompilerLib", "T7CompilerLib\T7CompilerLib.csproj", "{7527FB74-E8AC-43F1-9E48-E9D4D2B5D5B3}" EndProject @@ -39,6 +39,9 @@ Global Development|Any CPU = Development|Any CPU Development|x64 = Development|x64 Development|x86 = Development|x86 + Release_Advanced|Any CPU = Release_Advanced|Any CPU + Release_Advanced|x64 = Release_Advanced|x64 + Release_Advanced|x86 = Release_Advanced|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 @@ -56,6 +59,12 @@ Global {7527FB74-E8AC-43F1-9E48-E9D4D2B5D5B3}.Development|x64.Build.0 = Debug|x64 {7527FB74-E8AC-43F1-9E48-E9D4D2B5D5B3}.Development|x86.ActiveCfg = Debug|Any CPU {7527FB74-E8AC-43F1-9E48-E9D4D2B5D5B3}.Development|x86.Build.0 = Debug|Any CPU + {7527FB74-E8AC-43F1-9E48-E9D4D2B5D5B3}.Release_Advanced|Any CPU.ActiveCfg = Release_Advanced|Any CPU + {7527FB74-E8AC-43F1-9E48-E9D4D2B5D5B3}.Release_Advanced|Any CPU.Build.0 = Release_Advanced|Any CPU + {7527FB74-E8AC-43F1-9E48-E9D4D2B5D5B3}.Release_Advanced|x64.ActiveCfg = Release_Advanced|x64 + {7527FB74-E8AC-43F1-9E48-E9D4D2B5D5B3}.Release_Advanced|x64.Build.0 = Release_Advanced|x64 + {7527FB74-E8AC-43F1-9E48-E9D4D2B5D5B3}.Release_Advanced|x86.ActiveCfg = Release_Advanced|Any CPU + {7527FB74-E8AC-43F1-9E48-E9D4D2B5D5B3}.Release_Advanced|x86.Build.0 = Release_Advanced|Any CPU {7527FB74-E8AC-43F1-9E48-E9D4D2B5D5B3}.Release|Any CPU.ActiveCfg = Release|Any CPU {7527FB74-E8AC-43F1-9E48-E9D4D2B5D5B3}.Release|Any CPU.Build.0 = Release|Any CPU {7527FB74-E8AC-43F1-9E48-E9D4D2B5D5B3}.Release|x64.ActiveCfg = Release|x64 @@ -74,6 +83,12 @@ Global {2CDEFD40-3952-44A7-B69E-711FBACAC869}.Development|x64.Build.0 = Debug|x64 {2CDEFD40-3952-44A7-B69E-711FBACAC869}.Development|x86.ActiveCfg = Debug|Any CPU {2CDEFD40-3952-44A7-B69E-711FBACAC869}.Development|x86.Build.0 = Debug|Any CPU + {2CDEFD40-3952-44A7-B69E-711FBACAC869}.Release_Advanced|Any CPU.ActiveCfg = Release_Advanced|Any CPU + {2CDEFD40-3952-44A7-B69E-711FBACAC869}.Release_Advanced|Any CPU.Build.0 = Release_Advanced|Any CPU + {2CDEFD40-3952-44A7-B69E-711FBACAC869}.Release_Advanced|x64.ActiveCfg = Release_Advanced|x64 + {2CDEFD40-3952-44A7-B69E-711FBACAC869}.Release_Advanced|x64.Build.0 = Release_Advanced|x64 + {2CDEFD40-3952-44A7-B69E-711FBACAC869}.Release_Advanced|x86.ActiveCfg = Release_Advanced|Any CPU + {2CDEFD40-3952-44A7-B69E-711FBACAC869}.Release_Advanced|x86.Build.0 = Release_Advanced|Any CPU {2CDEFD40-3952-44A7-B69E-711FBACAC869}.Release|Any CPU.ActiveCfg = Release|Any CPU {2CDEFD40-3952-44A7-B69E-711FBACAC869}.Release|Any CPU.Build.0 = Release|Any CPU {2CDEFD40-3952-44A7-B69E-711FBACAC869}.Release|x64.ActiveCfg = Release|x64 @@ -92,6 +107,12 @@ Global {A45BC8F5-CB13-4006-9EAE-A07CA7CC091C}.Development|x64.Build.0 = Debug|x64 {A45BC8F5-CB13-4006-9EAE-A07CA7CC091C}.Development|x86.ActiveCfg = Debug|Any CPU {A45BC8F5-CB13-4006-9EAE-A07CA7CC091C}.Development|x86.Build.0 = Debug|Any CPU + {A45BC8F5-CB13-4006-9EAE-A07CA7CC091C}.Release_Advanced|Any CPU.ActiveCfg = Release_Advanced|Any CPU + {A45BC8F5-CB13-4006-9EAE-A07CA7CC091C}.Release_Advanced|Any CPU.Build.0 = Release_Advanced|Any CPU + {A45BC8F5-CB13-4006-9EAE-A07CA7CC091C}.Release_Advanced|x64.ActiveCfg = Release_Advanced|x64 + {A45BC8F5-CB13-4006-9EAE-A07CA7CC091C}.Release_Advanced|x64.Build.0 = Release_Advanced|x64 + {A45BC8F5-CB13-4006-9EAE-A07CA7CC091C}.Release_Advanced|x86.ActiveCfg = Release_Advanced|Any CPU + {A45BC8F5-CB13-4006-9EAE-A07CA7CC091C}.Release_Advanced|x86.Build.0 = Release_Advanced|Any CPU {A45BC8F5-CB13-4006-9EAE-A07CA7CC091C}.Release|Any CPU.ActiveCfg = Release|Any CPU {A45BC8F5-CB13-4006-9EAE-A07CA7CC091C}.Release|Any CPU.Build.0 = Release|Any CPU {A45BC8F5-CB13-4006-9EAE-A07CA7CC091C}.Release|x64.ActiveCfg = Release|x64 @@ -110,6 +131,12 @@ Global {810BDF68-87BB-45A2-AC4F-87643045BF4C}.Development|x64.Build.0 = Development|x64 {810BDF68-87BB-45A2-AC4F-87643045BF4C}.Development|x86.ActiveCfg = Development|x86 {810BDF68-87BB-45A2-AC4F-87643045BF4C}.Development|x86.Build.0 = Development|x86 + {810BDF68-87BB-45A2-AC4F-87643045BF4C}.Release_Advanced|Any CPU.ActiveCfg = Release_Advanced|Any CPU + {810BDF68-87BB-45A2-AC4F-87643045BF4C}.Release_Advanced|Any CPU.Build.0 = Release_Advanced|Any CPU + {810BDF68-87BB-45A2-AC4F-87643045BF4C}.Release_Advanced|x64.ActiveCfg = Release_Advanced|x64 + {810BDF68-87BB-45A2-AC4F-87643045BF4C}.Release_Advanced|x64.Build.0 = Release_Advanced|x64 + {810BDF68-87BB-45A2-AC4F-87643045BF4C}.Release_Advanced|x86.ActiveCfg = Release_Advanced|x86 + {810BDF68-87BB-45A2-AC4F-87643045BF4C}.Release_Advanced|x86.Build.0 = Release_Advanced|x86 {810BDF68-87BB-45A2-AC4F-87643045BF4C}.Release|Any CPU.ActiveCfg = Release|Any CPU {810BDF68-87BB-45A2-AC4F-87643045BF4C}.Release|Any CPU.Build.0 = Release|Any CPU {810BDF68-87BB-45A2-AC4F-87643045BF4C}.Release|x64.ActiveCfg = Release|x64 @@ -128,6 +155,12 @@ Global {1DC7CE8B-F111-4E49-B915-B88E7D64358B}.Development|x64.Build.0 = Debug|x64 {1DC7CE8B-F111-4E49-B915-B88E7D64358B}.Development|x86.ActiveCfg = Debug|Any CPU {1DC7CE8B-F111-4E49-B915-B88E7D64358B}.Development|x86.Build.0 = Debug|Any CPU + {1DC7CE8B-F111-4E49-B915-B88E7D64358B}.Release_Advanced|Any CPU.ActiveCfg = Release_Advanced|Any CPU + {1DC7CE8B-F111-4E49-B915-B88E7D64358B}.Release_Advanced|Any CPU.Build.0 = Release_Advanced|Any CPU + {1DC7CE8B-F111-4E49-B915-B88E7D64358B}.Release_Advanced|x64.ActiveCfg = Release_Advanced|x64 + {1DC7CE8B-F111-4E49-B915-B88E7D64358B}.Release_Advanced|x64.Build.0 = Release_Advanced|x64 + {1DC7CE8B-F111-4E49-B915-B88E7D64358B}.Release_Advanced|x86.ActiveCfg = Release_Advanced|Any CPU + {1DC7CE8B-F111-4E49-B915-B88E7D64358B}.Release_Advanced|x86.Build.0 = Release_Advanced|Any CPU {1DC7CE8B-F111-4E49-B915-B88E7D64358B}.Release|Any CPU.ActiveCfg = Release|Any CPU {1DC7CE8B-F111-4E49-B915-B88E7D64358B}.Release|Any CPU.Build.0 = Release|Any CPU {1DC7CE8B-F111-4E49-B915-B88E7D64358B}.Release|x64.ActiveCfg = Release|x64 @@ -146,6 +179,12 @@ Global {C630E36A-4A4C-4F14-AA8B-AE80F592DAAD}.Development|x64.Build.0 = Debug|x64 {C630E36A-4A4C-4F14-AA8B-AE80F592DAAD}.Development|x86.ActiveCfg = Debug|Any CPU {C630E36A-4A4C-4F14-AA8B-AE80F592DAAD}.Development|x86.Build.0 = Debug|Any CPU + {C630E36A-4A4C-4F14-AA8B-AE80F592DAAD}.Release_Advanced|Any CPU.ActiveCfg = Release_Advanced|Any CPU + {C630E36A-4A4C-4F14-AA8B-AE80F592DAAD}.Release_Advanced|Any CPU.Build.0 = Release_Advanced|Any CPU + {C630E36A-4A4C-4F14-AA8B-AE80F592DAAD}.Release_Advanced|x64.ActiveCfg = Release_Advanced|x64 + {C630E36A-4A4C-4F14-AA8B-AE80F592DAAD}.Release_Advanced|x64.Build.0 = Release_Advanced|x64 + {C630E36A-4A4C-4F14-AA8B-AE80F592DAAD}.Release_Advanced|x86.ActiveCfg = Release_Advanced|Any CPU + {C630E36A-4A4C-4F14-AA8B-AE80F592DAAD}.Release_Advanced|x86.Build.0 = Release_Advanced|Any CPU {C630E36A-4A4C-4F14-AA8B-AE80F592DAAD}.Release|Any CPU.ActiveCfg = Release|Any CPU {C630E36A-4A4C-4F14-AA8B-AE80F592DAAD}.Release|Any CPU.Build.0 = Release|Any CPU {C630E36A-4A4C-4F14-AA8B-AE80F592DAAD}.Release|x64.ActiveCfg = Release|x64 @@ -163,6 +202,12 @@ Global {093DADA4-43D5-4327-8686-052E8966B587}.Development|x64.Build.0 = Release|x64 {093DADA4-43D5-4327-8686-052E8966B587}.Development|x86.ActiveCfg = Debug|Win32 {093DADA4-43D5-4327-8686-052E8966B587}.Development|x86.Build.0 = Debug|Win32 + {093DADA4-43D5-4327-8686-052E8966B587}.Release_Advanced|Any CPU.ActiveCfg = Release_Advanced|x64 + {093DADA4-43D5-4327-8686-052E8966B587}.Release_Advanced|Any CPU.Build.0 = Release_Advanced|x64 + {093DADA4-43D5-4327-8686-052E8966B587}.Release_Advanced|x64.ActiveCfg = Release_Advanced|x64 + {093DADA4-43D5-4327-8686-052E8966B587}.Release_Advanced|x64.Build.0 = Release_Advanced|x64 + {093DADA4-43D5-4327-8686-052E8966B587}.Release_Advanced|x86.ActiveCfg = Release_Advanced|Win32 + {093DADA4-43D5-4327-8686-052E8966B587}.Release_Advanced|x86.Build.0 = Release_Advanced|Win32 {093DADA4-43D5-4327-8686-052E8966B587}.Release|Any CPU.ActiveCfg = Release|Win32 {093DADA4-43D5-4327-8686-052E8966B587}.Release|x64.ActiveCfg = Release|x64 {093DADA4-43D5-4327-8686-052E8966B587}.Release|x64.Build.0 = Release|x64 @@ -179,6 +224,12 @@ Global {463A5FF3-DAF8-4379-90CC-6A149196D849}.Development|x64.Build.0 = Debug|x64 {463A5FF3-DAF8-4379-90CC-6A149196D849}.Development|x86.ActiveCfg = Debug|Win32 {463A5FF3-DAF8-4379-90CC-6A149196D849}.Development|x86.Build.0 = Debug|Win32 + {463A5FF3-DAF8-4379-90CC-6A149196D849}.Release_Advanced|Any CPU.ActiveCfg = Release_Advanced|x64 + {463A5FF3-DAF8-4379-90CC-6A149196D849}.Release_Advanced|Any CPU.Build.0 = Release_Advanced|x64 + {463A5FF3-DAF8-4379-90CC-6A149196D849}.Release_Advanced|x64.ActiveCfg = Release_Advanced|x64 + {463A5FF3-DAF8-4379-90CC-6A149196D849}.Release_Advanced|x64.Build.0 = Release_Advanced|x64 + {463A5FF3-DAF8-4379-90CC-6A149196D849}.Release_Advanced|x86.ActiveCfg = Release_Advanced|Win32 + {463A5FF3-DAF8-4379-90CC-6A149196D849}.Release_Advanced|x86.Build.0 = Release_Advanced|Win32 {463A5FF3-DAF8-4379-90CC-6A149196D849}.Release|Any CPU.ActiveCfg = Release|Win32 {463A5FF3-DAF8-4379-90CC-6A149196D849}.Release|x64.ActiveCfg = Release|x64 {463A5FF3-DAF8-4379-90CC-6A149196D849}.Release|x64.Build.0 = Release|x64 diff --git a/TreyarchCompiler/Games/T8Compiler.cs b/TreyarchCompiler/Games/T8Compiler.cs index 8f582a2..e43067e 100644 --- a/TreyarchCompiler/Games/T8Compiler.cs +++ b/TreyarchCompiler/Games/T8Compiler.cs @@ -52,8 +52,9 @@ public CompiledCode Compile() return data; } var assemble_ticks = DateTime.Now.Ticks; - try - { + try + { + data.RequiresGSI = Script.UsingGSI; data.CompiledScript = Script.Serialize(); data.HashMap = Script.GetHashMap(); } @@ -77,6 +78,11 @@ private byte GetAutoExecByVM() return (byte)ExportFlags.AutoExec; } + private byte GetEventByVM() + { + return (byte)ExportFlags.Event; + } + private byte GetPrivateByVM() { return (byte)ExportFlags.Private; @@ -93,6 +99,7 @@ private void CompileTree() { byte flags = GetPrivateByVM(); var FunctionFrame = directive; + string callbackName = null; switch (directive.ChildNodes[0].Term.Name.ToLower()) { case "includes": @@ -118,7 +125,12 @@ private void CompileTree() case "functionframe": FunctionFrame = directive.ChildNodes[0]; - if(FunctionFrame.ChildNodes[0].Term.Name == "autoexec") flags |= GetAutoExecByVM(); + if (FunctionFrame.ChildNodes[0].Term.Name == "autoexec") flags |= GetAutoExecByVM(); + if (FunctionFrame.ChildNodes[0].Term.Name == "event") + { + flags |= GetEventByVM(); + callbackName = FunctionFrame.ChildNodes[2].Token.ValueString.ToLower(); + } goto functionsLabel; case "functions": @@ -128,12 +140,13 @@ private void CompileTree() var Parameters = function.ChildNodes[function.ChildNodes.FindIndex(e => e.Term.Name == "parameters")].ChildNodes[0].ChildNodes; if (FunctionMetadata.ContainsKey(functionName)) throw new ArgumentException($"Function '{functionName}' has been defined more than once."); functionTree.Add(functionName, function); - FunctionMetadata[functionName] = new ScriptFunctionMetaData() - { + FunctionMetadata[functionName] = new ScriptFunctionMetaData() { FunctionHash = Script.T8Hash(functionName), NamespaceHash = ScriptNamespace, FunctionName = functionName, NamespaceName = "treyarch", + CallbackEventHash = callbackName == null ? 0 : Script.T8Hash(callbackName), + CallbackEventName = callbackName, NumParams = (byte)Parameters.Count, Flags = flags }; @@ -199,7 +212,7 @@ private void SetNamespace() private void EmitFunction(ParseTreeNode functionNode, string FunctionName) { var Parameters = functionNode.ChildNodes[functionNode.ChildNodes.FindIndex(e => e.Term.Name == "parameters")].ChildNodes[0].ChildNodes; - var CurrentFunction = Script.Exports.Add(FunctionMetadata[FunctionName].FunctionHash, FunctionMetadata[FunctionName].NamespaceHash, FunctionMetadata[FunctionName].NumParams); + var CurrentFunction = Script.Exports.Add(FunctionMetadata[FunctionName].FunctionHash, FunctionMetadata[FunctionName].NamespaceHash, FunctionMetadata[FunctionName].CallbackEventHash, FunctionMetadata[FunctionName].NumParams); CurrentFunction.Flags = FunctionMetadata[FunctionName].Flags; CurrentFunction.FriendlyName = FunctionName; foreach (var paramNode in Parameters) AddLocal(CurrentFunction, paramNode.FindTokenAndGetText()); @@ -493,6 +506,9 @@ private void IterateStack() CurrentOp.SetOperands = EmitVector(CurrentFunction, node, Context); Push(CurrentOp); break; + case "lazyFunction": + EmitLazyFunctionPtr(CurrentFunction, node); + break; case "shortHandArray": CurrentOp.SetOperands = EmitArraySH(CurrentFunction, node, Context); @@ -917,6 +933,19 @@ private IEnumerable EmitVector(T89ScriptExport CurrentFunction, ParseT CurrentFunction.AddOp(ScriptOpCode.Vector); } + private void EmitLazyFunctionPtr(dynamic CurrentFunction, ParseTreeNode node) + { + var ns = node.ChildNodes[1].Token.ValueString; + var func = node.ChildNodes[node.ChildNodes.Count - 1].Token.ValueString; + var script = node.ChildNodes[3].Token.ValueString.ToLower().Replace("\\", "/"); + if (!script.StartsWith("script_")) + { + // add the .csc/.gsc only if we aren't using the hashed value + script += node.ChildNodes[4].Token.ValueString.ToLower(); + } + CurrentFunction.AddLazyGetFunction(Script.T8s64Hash(script), Script.T8Hash(ns), Script.T8Hash(func)); + } + private void EmitFunctionPtr(T89ScriptExport CurrentFunction, ParseTreeNode node, byte Numparams) { ParseTreeNode FuncNameNode = node.ChildNodes[node.ChildNodes.Count - 1]; @@ -1023,8 +1052,10 @@ private struct ScriptFunctionMetaData { public uint FunctionHash; public uint NamespaceHash; + public uint CallbackEventHash; public string FunctionName; public string NamespaceName; + public string CallbackEventName; public byte NumParams; public byte Flags; diff --git a/TreyarchCompiler/NewSyntax.cs b/TreyarchCompiler/NewSyntax.cs index 1f83b32..d9455ef 100644 --- a/TreyarchCompiler/NewSyntax.cs +++ b/TreyarchCompiler/NewSyntax.cs @@ -91,6 +91,7 @@ public static NewSyntax ThreadSafeInstance protected NonTerminal baseCallPointer { private set; get; } protected NonTerminal gscForFunction { private set; get; } protected NonTerminal getFunction { private set; get; } + protected NonTerminal lazyFunction { private set; get; } protected NonTerminal callParameters { private set; get; } protected NonTerminal parenCallParameters { private set; get; } #endregion @@ -152,7 +153,7 @@ public static NewSyntax ThreadSafeInstance #region Virtual protected virtual IdentifierTerminal IncludeIdentifier => new IdentifierTerminal("include_identifier", @"_/\", "_"); - protected virtual NonTerminal FunctionFrame => new NonTerminal("functionFrame", ToTerm("autoexec", "autoexec") + functions | functions); + protected virtual NonTerminal FunctionFrame => new NonTerminal("functionFrame", ToTerm("autoexec", "autoexec") + functions | (ToTerm("event", "event") + "<" + Identifier + ">" + functions) | functions); protected virtual NonTerminal Overrides => new NonTerminal("overrides", Unsupported); protected virtual NonTerminal NameSpaceDirective => new NonTerminal("namespace", "#namespace" + Identifier + ";"); protected virtual NonTerminal verbatimString => new NonTerminal("Unsupported", Unsupported); @@ -261,7 +262,7 @@ protected NewSyntax() //Master Expresssion Rules expr.Rule = parenExpr | mathExpr | animRef | animTree | newArray | shortHandArray | shortHandStruct | boolNot; mathExpr.Rule = parenMathExpr | variableExpr | StringLiteral | NumberLiteral | verbatimString | size | iString | hashedString | hashedVariable | vector; - variableExpr.Rule = parenVariableExpr | directAccess | stackAccess | call | classCall | Identifier | getFunction | array; + variableExpr.Rule = parenVariableExpr | directAccess | stackAccess | call | classCall | Identifier | getFunction | lazyFunction | array; //Parenthesis parenExpr.Rule = "(" + expr + ")"; @@ -299,6 +300,8 @@ protected NewSyntax() //Script Reference Components gscForFunction.Rule = ToTerm("&") + Identifier + "::"; getFunction.Rule = ToTerm("&") + new NonTerminal("expr", Identifier) | gscForFunction + variableExpr; + lazyFunction.Rule = ToTerm("@") + Identifier + "<" + Identifier + ".gsc" + ">" + "::" + Identifier | + ToTerm("@") + Identifier + "<" + Identifier + ".csc" + ">" + "::" + Identifier; //Base Call Rules baseCall.Rule = Identifier + "::" + Identifier + parenCallParameters | Identifier + parenCallParameters; @@ -453,6 +456,7 @@ protected virtual void CreateNonTerminals() callParameters = new NonTerminal("callParameters"); baseCallPointer = new NonTerminal("baseCallPointer"); getFunction = new NonTerminal("getFunction"); + lazyFunction = new NonTerminal("lazyFunction"); array = new NonTerminal("array"); size = new NonTerminal("size"); boolNot = new NonTerminal("boolNot"); diff --git a/TreyarchCompiler/TreyarchCompiler.csproj b/TreyarchCompiler/TreyarchCompiler.csproj index e16a1dc..da2fb95 100644 --- a/TreyarchCompiler/TreyarchCompiler.csproj +++ b/TreyarchCompiler/TreyarchCompiler.csproj @@ -60,6 +60,24 @@ prompt MinimumRecommendedRules.ruleset + + bin\Release_Advanced\ + TRACE + true + pdbonly + AnyCPU + 7.1 + prompt + + + bin\x64\Release_Advanced\ + TRACE + true + x64 + 7.1 + prompt + MinimumRecommendedRules.ruleset + ..\packages\Ionic.Zip.1.9.1.8\lib\Ionic.Zip.dll diff --git a/t7cinternal/t7cinternal.vcxproj b/t7cinternal/t7cinternal.vcxproj index faa4f4c..b35e3d6 100644 --- a/t7cinternal/t7cinternal.vcxproj +++ b/t7cinternal/t7cinternal.vcxproj @@ -5,6 +5,14 @@ Debug Win32 + + Release_Advanced + Win32 + + + Release_Advanced + x64 + Release Win32 @@ -39,6 +47,13 @@ true MultiByte + + DynamicLibrary + false + v142 + true + MultiByte + DynamicLibrary false @@ -52,6 +67,13 @@ true MultiByte + + DynamicLibrary + false + v142 + true + MultiByte + @@ -63,12 +85,18 @@ + + + + + + true @@ -76,12 +104,18 @@ false + + false + true false + + false + Level3 @@ -120,6 +154,27 @@ false + + + Level3 + true + true + true + WIN32;NDEBUG;T7CINTERNAL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + None + MultiThreaded + + + Windows + true + true + true + false + + Level3 @@ -159,6 +214,28 @@ false + + + Level3 + true + true + true + NDEBUG;T7CINTERNAL_EXPORTS;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + NotUsing + pch.h + None + MultiThreaded + stdcpp17 + + + Windows + true + true + true + false + + diff --git a/t8cinternal/LazyLink.cpp b/t8cinternal/LazyLink.cpp index 37249b4..1fa150a 100644 --- a/t8cinternal/LazyLink.cpp +++ b/t8cinternal/LazyLink.cpp @@ -5,55 +5,69 @@ void LazyLink::Init() { +#ifdef DETOUR_LOGGING + GSCBuiltins::nlog("Installing Lazy link"); +#endif // Change Opcode Handler 0x16 to VM_OP_GetLazyFunction *(INT64*)(0x16 * 8 + OFF_ScrVm_Opcodes) = (INT64)VM_OP_GetLazyFunction; } void LazyLink::VM_OP_GetLazyFunction(INT32 inst, INT64* fs_0, INT64 vmc, bool* terminate) { - //INT64 base = (*fs_0 + 3) & 0xFFFFFFFFFFFFFFFCLL; - //INT32 Namespace = *(INT32*)base; - //INT32 Function = *(INT32*)(base + 4); - //char* script = (char*)(*fs_0 + (*(INT32*)(base + 8))); - //auto asset = ScriptDetours::FindScriptParsetree(script); - - //if (!asset) - //{ - // *(INT32*)(fs_0[1] + 0x18) = 0x0; // undefined - // fs_0[1] += 0x10; // change stack top - // return; - //} - - //auto buffer = *(char**)(asset + 0x10); - //auto exportsOffset = *(INT32*)(buffer + 0x20); - //auto exports = (INT64)(exportsOffset + buffer); - //auto numExports = *(INT16*)(buffer + 0x3A); - //__t7export* currentExport = (__t7export*)exports; - //bool found = false; - - //for (INT16 i = 0; i < numExports; i++, currentExport++) - //{ - // if (currentExport->funcName != Function) - // { - // continue; - // } - // if (currentExport->funcNS != Namespace) - // { - // continue; - // } - // found = true; - // break; - //} - - //if (!found) - //{ - // *(INT32*)(fs_0[1] + 0x18) = 0x0; // undefined - // fs_0[1] += 0x10; // change stack top - // return; - //} - - //*(INT32*)(fs_0[1] + 0x18) = 0xE; // assign the top variable's type - //*(INT64*)(fs_0[1] + 0x10) = (INT64)buffer + currentExport->bytecodeOffset; // assign the top variable's value - //fs_0[1] += 0x10; // change stack top - //*fs_0 = base + 0xC; // move past the data + INT64 base = (*fs_0 + 3) & 0xFFFFFFFFFFFFFFFCLL; + INT32 nsp = *(INT32*)base; + INT32 func = *(INT32*)(base + 4); + INT64 script = *(INT64*)(base + 8); + +#ifdef DETOUR_LOGGING + GSCBuiltins::nlog("Lazy loading %x::%x <%p>...", nsp, func, script); +#endif + // (field_0) move past the data + fs_0[0] = base + 0x10; + // (top) go to next field + fs_0[1] += 0x10; + + auto buffer = ScriptDetours::FindLinkedScript(script, inst); + + if (!buffer) + { +#ifdef DETOUR_LOGGING + GSCBuiltins::nlog("Failed to locate %p...", script); +#endif + *(INT32*)(fs_0[1] + 8) = 0x0; // undefined + return; + } + + auto exportsOffset = *(INT32*)(buffer + 0x30); + auto exports = (INT64)(exportsOffset + buffer); + auto numExports = *(INT16*)(buffer + 0x1E); + __t8export* currentExport = (__t8export*)exports; + + INT64 link = 0; + + for (INT16 i = 0; i < numExports; i++, currentExport++) + { + if (currentExport->funcName != func) + { + continue; + } + if (currentExport->funcNS != nsp) + { + continue; + } + link = (INT64)buffer + currentExport->bytecodeOffset; + } + +#ifdef DETOUR_LOGGING + GSCBuiltins::nlog("Output export: %p!", link); +#endif + if (!link) + { + *(INT32*)(fs_0[1] + 8) = 0x0; // undefined + return; + } + + // assign the top variable's to the ptr + *(INT64*)(fs_0[1]) = link; + *(INT32*)(fs_0[1] + 8) = 0xC; // SCRIPT_FUNCTION(0xC) } diff --git a/t8cinternal/builtins.cpp b/t8cinternal/builtins.cpp index 2918bc4..5e3ad0a 100644 --- a/t8cinternal/builtins.cpp +++ b/t8cinternal/builtins.cpp @@ -1,6 +1,8 @@ #include "builtins.h" #include "offsets.h" #include "detours.h" +#include +#include std::unordered_map GSCBuiltins::CustomFunctions; tScrVm_GetString GSCBuiltins::ScrVm_GetString; @@ -8,6 +10,7 @@ tScrVm_GetInt GSCBuiltins::ScrVm_GetInt; tScrVm_GetNumParam GSCBuiltins::ScrVm_GetNumParam; tScrVm_AddInt GSCBuiltins::ScrVm_AddInt; tScrVm_AddBool GSCBuiltins::ScrVm_AddBool; +tScrVm_AddUndefined GSCBuiltins::ScrVm_AddUndefined; // add all custom builtins here void GSCBuiltins::Generate() @@ -33,13 +36,41 @@ void GSCBuiltins::Generate() // Prints a line of text to an open, untitled notepad window. // : Text to print AddCustomFunction("nprintln", GSCBuiltins::GScr_nprintln); + + // compiler::GScr_areAdvancedFeaturesSupported()->bool + // Return if the advanced features are supported + AddCustomFunction("areadvancedfeaturessupported", GSCBuiltins::GScr_areAdvancedFeaturesSupported); + + +#ifdef T8CINTERNAL_ADVANCED + // advanced feature, using a define to avoid giving too much power to the user + + // compiler::GScr_fnprint(file, mode, message) + // Print text in a file + // : File to open + // : File open mode, w(write) or a(append) + // : Text to write + AddCustomFunction("fnprint", GSCBuiltins::GScr_fnprint); + // advanced feature, using a define to avoid giving too much power to the user + + // compiler::GScr_fnprintln(file, mode, message) + // Print a line in a file + // : File to open + // : File open mode, w(write) or a(append) + // : Text to write + AddCustomFunction("fnprintln", GSCBuiltins::GScr_fnprintln); +#endif } void GSCBuiltins::Init() { GSCBuiltins::Generate(); auto builtinFunction = (BuiltinFunctionDef*)OFF_IsProfileBuild; +#ifdef DETOUR_LOGGING + GSCBuiltins::nlog("Installing %d builtins...", CustomFunctions.size()); +#endif builtinFunction->max_args = 255; + builtinFunction->type = 0; builtinFunction->actionFunc = GSCBuiltins::Exec; ScrVm_GetString = (tScrVm_GetString)OFF_ScrVm_GetString; @@ -47,6 +78,7 @@ void GSCBuiltins::Init() ScrVm_GetNumParam = (tScrVm_GetNumParam)OFF_ScrVm_GetNumParam; ScrVm_AddInt = (tScrVm_AddInt)OFF_ScrVm_AddInt; ScrVm_AddBool = (tScrVm_AddBool)OFF_ScrVm_AddBool; + ScrVm_AddUndefined = (tScrVm_AddUndefined)OFF_ScrVm_AddUndefined; } void GSCBuiltins::AddCustomFunction(const char* name, void* funcPtr) @@ -54,25 +86,29 @@ void GSCBuiltins::AddCustomFunction(const char* name, void* funcPtr) CustomFunctions[t8hash(name)] = funcPtr; } -INT64 GSCBuiltins::Exec(int scriptInst) +void GSCBuiltins::Exec(int scriptInst) { auto numParams = ScrVm_GetNumParam(scriptInst); - nlog("called with %d parameters", numParams); // TODO if (!numParams) { - return ScrVm_AddBool(scriptInst, 0); + // default implementation + ScrVm_AddBool(scriptInst, false); + return; } INT32 func = ScrVm_GetInt(scriptInst, 0); +#ifdef DETOUR_LOGGING + nlog("called with %d parameters with %p", numParams, func); // TODO +#endif if (CustomFunctions.find(func) == CustomFunctions.end()) { // unknown builtin nlog("unknown builtin %p", func); - return ScrVm_AddBool(scriptInst, 0); + ScrVm_AddBool(scriptInst, false); + return; } - reinterpret_cast(CustomFunctions[func])(scriptInst); - return ScrVm_AddBool(scriptInst, 0); + reinterpret_cast(CustomFunctions[func])(scriptInst); } // START OF BUILTIN DEFINITIONS @@ -85,55 +121,145 @@ void GSCBuiltins::GScr_nprintln(int scriptInst) { // note: we use 1 as our param index because custom builtin params start at 1. The first param (0) is always the name of the function called. // we also use %s to prevent a string format vulnerability! - nlog("%s", ScrVm_GetString(0, 1)); + nlog("%s", ScrVm_GetString(scriptInst, 1)); + ScrVm_AddUndefined(scriptInst); } void GSCBuiltins::GScr_detour(int scriptInst) { - if (scriptInst) + ScriptDetours::DetoursEnabled[scriptInst] = true; + ScrVm_AddUndefined(scriptInst); +} + +void GSCBuiltins::GScr_relinkDetours(int scriptInst) +{ + ScriptDetours::LinkDetours(scriptInst); + ScrVm_AddUndefined(scriptInst); +} + +void GSCBuiltins::GScr_livesplit(int scriptInst) +{ + HANDLE livesplit = CreateFile("\\\\.\\pipe\\LiveSplit", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (!livesplit) { return; } - ScriptDetours::DetoursEnabled = true; + + const char* message = ScrVm_GetString(scriptInst, 1); + WriteFile(livesplit, message, strlen(message), nullptr, NULL); + CloseHandle(livesplit); + ScrVm_AddUndefined(scriptInst); } -void GSCBuiltins::GScr_relinkDetours(int scriptInst) +void GSCBuiltins::GScr_fnprintln(int scriptInst) { - if (scriptInst) + auto numParams = ScrVm_GetNumParam(scriptInst); + if (numParams < 4) { + ScrVm_AddUndefined(scriptInst); + nlog("bad writefile call with %d param", numParams); + return; + } + const char* file = ScrVm_GetString(scriptInst, 1); + const char* mode = ScrVm_GetString(scriptInst, 2); + const char* message = ScrVm_GetString(scriptInst, 3); + + + std::ios::openmode m; + if (!_strcmpi("w", mode)) + { + m = std::ios::out; + } + else if (!_strcmpi("a", mode)) + { + m = std::ios::app; + } + else { + ScrVm_AddUndefined(scriptInst); return; } - ScriptDetours::LinkDetours(); + + std::ofstream output{ file, std::ios::app }; + + if (output) + { + output << message << "\n"; + output.close(); + } + else + { + nlog("Error while opening %s with mode %s", file, mode); + } + + ScrVm_AddUndefined(scriptInst); } -void GSCBuiltins::GScr_livesplit(int scriptInst) +void GSCBuiltins::GScr_fnprint(int scriptInst) { - if (scriptInst) + auto numParams = ScrVm_GetNumParam(scriptInst); + if (numParams < 4) { + ScrVm_AddUndefined(scriptInst); + nlog("bad writefile call with %d param", numParams); return; } + const char* file = ScrVm_GetString(scriptInst, 1); + const char* mode = ScrVm_GetString(scriptInst, 2); + const char* message = ScrVm_GetString(scriptInst, 3); - HANDLE livesplit = CreateFile("\\\\.\\pipe\\LiveSplit", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - if (!livesplit) + + std::ios::openmode m; + if (!_strcmpi("w", mode)) { + m = std::ios::out; + } + else if (!_strcmpi("a", mode)) + { + m = std::ios::app; + } + else { + ScrVm_AddUndefined(scriptInst); return; } - const char* message = ScrVm_GetString(0, 1); - WriteFile(livesplit, message, strlen(message), nullptr, NULL); - CloseHandle(livesplit); + std::ofstream output{ file, std::ios::app }; + + if (output) + { + output << message; + output.close(); + } + else + { + nlog("Error while opening %s with mode %s", file, mode); + } + + ScrVm_AddUndefined(scriptInst); } +void GSCBuiltins::GScr_areAdvancedFeaturesSupported(int inst) +{ +#ifdef T8CINTERNAL_ADVANCED + ScrVm_AddBool(inst, true); +#else + ScrVm_AddBool(inst, false); +#endif +} void GSCBuiltins::nlog(const char* str, ...) { - va_list ap; - HWND notepad, edit; char buf[256]; + va_list ap; va_start(ap, str); vsprintf(buf, str, ap); va_end(ap); + std::ofstream output{ "t8compiler.log", std::ios::app }; + + output << buf << "\n"; + output.close(); + /* + HWND notepad, edit; strcat_s(buf, 256, "\r\n"); notepad = FindWindow(NULL, "Untitled - Notepad"); if (!notepad) @@ -142,4 +268,5 @@ void GSCBuiltins::nlog(const char* str, ...) } edit = FindWindowEx(notepad, NULL, "EDIT", NULL); SendMessage(edit, EM_REPLACESEL, TRUE, (LPARAM)buf); + //*/ } \ No newline at end of file diff --git a/t8cinternal/builtins.h b/t8cinternal/builtins.h index facf384..1b0df31 100644 --- a/t8cinternal/builtins.h +++ b/t8cinternal/builtins.h @@ -29,8 +29,9 @@ inline uint32_t t8hash(const char* key) { typedef INT64(__fastcall* tScrVm_GetInt)(unsigned int inst, unsigned int index); typedef char*(__fastcall* tScrVm_GetString)(unsigned int inst, unsigned int index); typedef INT64(__fastcall* tScrVm_GetNumParam)(unsigned int inst); -typedef INT64(__fastcall* tScrVm_AddInt)(unsigned int inst, int64_t value); -typedef INT64(__fastcall* tScrVm_AddBool)(unsigned int inst, bool value); +typedef void(__fastcall* tScrVm_AddInt)(unsigned int inst, int64_t value); +typedef void(__fastcall* tScrVm_AddBool)(unsigned int inst, bool value); +typedef void(__fastcall* tScrVm_AddUndefined)(unsigned int inst); class GSCBuiltins { @@ -42,9 +43,10 @@ class GSCBuiltins static tScrVm_GetNumParam ScrVm_GetNumParam; static tScrVm_AddInt ScrVm_AddInt; static tScrVm_AddBool ScrVm_AddBool; + static tScrVm_AddUndefined ScrVm_AddUndefined; private: - static INT64 Exec(int scriptInst); + static void Exec(int scriptInst); static void Generate(); static std::unordered_map CustomFunctions; @@ -53,6 +55,9 @@ class GSCBuiltins static void GScr_detour(int scriptInst); static void GScr_relinkDetours(int scriptInst); static void GScr_livesplit(int scriptInst); + static void GScr_fnprint(int scriptInst); + static void GScr_fnprintln(int scriptInst); + static void GScr_areAdvancedFeaturesSupported(int scriptInst); public: static void nlog(const char* str, ...); diff --git a/t8cinternal/detours.cpp b/t8cinternal/detours.cpp index a10b847..4a55e6c 100644 --- a/t8cinternal/detours.cpp +++ b/t8cinternal/detours.cpp @@ -16,13 +16,15 @@ struct ReadScriptDetour tScr_GetFunction ScriptDetours::Scr_GetFunction = NULL; tScr_GetMethod ScriptDetours::Scr_GetMethod = NULL; +tScr_GetFunction ScriptDetours::CScr_GetFunction = NULL; +tScr_GetMethod ScriptDetours::CScr_GetMethod = NULL; tDB_FindXAssetHeader ScriptDetours::DB_FindXAssetHeader = NULL; tScr_GscObjLink ScriptDetours::Scr_GscObjLink = NULL; -char* ScriptDetours::GSC_OBJ = NULL; +char* ScriptDetours::GSC_OBJ[2] = { NULL, NULL }; -std::vector ScriptDetours::RegisteredDetours; -std::unordered_map ScriptDetours::LinkedDetours; -std::unordered_map ScriptDetours::AppliedFixups; +std::vector ScriptDetours::RegisteredDetours[2]; +std::unordered_map ScriptDetours::LinkedDetours[2]; +std::unordered_map ScriptDetours::AppliedFixups[2]; tVM_Opcode ScriptDetours::VM_OP_GetFunction_Old = NULL; tVM_Opcode ScriptDetours::VM_OP_GetAPIFunction_Old = NULL; tVM_Opcode ScriptDetours::VM_OP_ScriptFunctionCall_Old = NULL; @@ -31,9 +33,9 @@ tVM_Opcode ScriptDetours::VM_OP_ScriptThreadCall_Old = NULL; tVM_Opcode ScriptDetours::VM_OP_ScriptMethodThreadCall_Old = NULL; tVM_Opcode ScriptDetours::VM_OP_CallBuiltin_Old = NULL; tVM_Opcode ScriptDetours::VM_OP_CallBuiltinMethod_Old = NULL; -bool ScriptDetours::DetoursLinked = false; -bool ScriptDetours::DetoursReset = true; -bool ScriptDetours::DetoursEnabled = false; +bool ScriptDetours::DetoursLinked[2] = { false, false }; +bool ScriptDetours::DetoursReset[2] = { true, true }; +bool ScriptDetours::DetoursEnabled[2] = { false, false }; bool ScriptDetours::DetoursInitialized = false; EXPORT void ResetDetours() @@ -45,45 +47,75 @@ EXPORT void ResetDetours() #ifdef DETOUR_LOGGING GSCBuiltins::nlog("Resetting detours..."); #endif - for (auto it = ScriptDetours::AppliedFixups.begin(); it != ScriptDetours::AppliedFixups.end(); it++) + for (int inst = 0; inst < 2; inst++) + { + ScriptDetours::ResetDetoursByInst(inst); + } +} + +void ScriptDetours::ResetDetoursByInst(int inst) +{ + if (!ScriptDetours::DetoursInitialized) + { + return; + } +#ifdef DETOUR_LOGGING + GSCBuiltins::nlog("Resetting detours (%s)...", inst ? "CLIENT" : "SERVER"); +#endif + for (auto it = AppliedFixups[inst].begin(); it != AppliedFixups[inst].end(); it++) { *it->first = it->second; } - ScriptDetours::AppliedFixups.clear(); - ScriptDetours::DetoursReset = true; - ScriptDetours::DetoursLinked = false; - ScriptDetours::DetoursEnabled = false; + AppliedFixups[inst].clear(); + DetoursReset[inst] = true; + DetoursLinked[inst] = false; + DetoursEnabled[inst] = false; } -EXPORT void RemoveDetours() +EXPORT void RemoveDetours(INT32 inst) { if (!ScriptDetours::DetoursInitialized) { return; } #ifdef DETOUR_LOGGING - GSCBuiltins::nlog("Removing detours..."); + GSCBuiltins::nlog("Removing detours (%s)...", inst ? "CLIENT" : "SERVER"); #endif - for (auto it = ScriptDetours::RegisteredDetours.begin(); it != ScriptDetours::RegisteredDetours.end(); it++) + for (auto it = ScriptDetours::RegisteredDetours[inst].begin(); it != ScriptDetours::RegisteredDetours[inst].end(); it++) { free(*it); } - ResetDetours(); - ScriptDetours::RegisteredDetours.clear(); - ScriptDetours::DetoursLinked = false; + ScriptDetours::RegisteredDetours[inst].clear(); + ScriptDetours::DetoursLinked[inst] = false; + ScriptDetours::ResetDetoursByInst(inst); +} + +void ScriptDetours::RemoveAllDetours() +{ + if (!ScriptDetours::DetoursInitialized) + { + return; + } +#ifdef DETOUR_LOGGING + GSCBuiltins::nlog("Removing detours..."); +#endif + for (int inst = 0; inst < 2; inst++) + { + RemoveDetours(inst); + } } -EXPORT bool RegisterDetours(void* DetourData, int NumDetours, INT64 scriptOffset) +EXPORT bool RegisterDetours(void* DetourData, int NumDetours, INT64 scriptOffset, INT32 inst) { if (!ScriptDetours::DetoursInitialized) { return true; } - RemoveDetours(); - ScriptDetours::GSC_OBJ = (char*)scriptOffset; + RemoveDetours(inst); + ScriptDetours::GSC_OBJ[inst] = (char*)scriptOffset; #ifdef DETOUR_LOGGING - GSCBuiltins::nlog("Registering %d detours in script %p...", NumDetours, scriptOffset); + GSCBuiltins::nlog("Registering %d detours in script %p (%s)...", NumDetours, scriptOffset, inst ? "CLIENT" : "SERVER"); #endif INT64 base = (INT64)DetourData; @@ -99,10 +131,10 @@ EXPORT bool RegisterDetours(void* DetourData, int NumDetours, INT64 scriptOffset GSCBuiltins::nlog("Detour Parsed: {FixupName:%x, ReplaceNamespace:%x, ReplaceFunction:%x, FixupOffset:%x, FixupSize:%x} {FixupMin:%p, FixupMax:%p}", read_detour->FixupName, read_detour->ReplaceNamespace, read_detour->ReplaceFunction, read_detour->FixupOffset, read_detour->FixupSize, detour->hFixup, detour->hFixup + detour->FixupSize); #endif detour->ReplaceScriptName = *(INT64*)((INT64)read_detour + sizeof(ReadScriptDetour)); - ScriptDetours::RegisteredDetours.push_back(detour); + ScriptDetours::RegisteredDetours[inst].push_back(detour); } - ScriptDetours::DetoursLinked = false; + ScriptDetours::DetoursLinked[inst] = false; return true; } @@ -115,6 +147,8 @@ void ScriptDetours::InstallHooks() // initialize methods Scr_GetFunction = (tScr_GetFunction)OFF_Scr_GetFunction; Scr_GetMethod = (tScr_GetMethod)OFF_Scr_GetMethod; + CScr_GetFunction = (tScr_GetFunction)OFF_CScr_GetFunction; + CScr_GetMethod = (tScr_GetMethod)OFF_CScr_GetMethod; DB_FindXAssetHeader = (tDB_FindXAssetHeader)OFF_DB_FindXAssetHeader; Scr_GscObjLink = (tScr_GscObjLink)OFF_Scr_GscObjLink; @@ -132,9 +166,17 @@ void ScriptDetours::InstallHooks() DetoursInitialized = true; } +INT64 ScriptDetours::GetFunction(INT32 inst, INT32 canonID, INT32* type, INT32* min_args, INT32* max_args) { + return (inst ? CScr_GetFunction : Scr_GetFunction)(canonID, type, min_args, max_args); +} + +INT64 ScriptDetours::GetMethod(INT32 inst, INT32 canonID, INT32* type, INT32* min_args, INT32* max_args) { + return (inst ? CScr_GetMethod : Scr_GetMethod)(canonID, type, min_args, max_args); +} + INT64 ScriptDetours::FindScriptParsetree(INT64 name) { - SPTEntry* currentSpt = (SPTEntry*)*(INT64*)OFFSET(0x912BBB0); + SPTEntry* currentSpt = (SPTEntry*)*(INT64*)(OFF_xAssetScriptParseTree); INT32 sptCount = *(INT32*)OFFSET(0x912BBB0 + 0x14); for (int i = 0; i < sptCount; i++, currentSpt++) { @@ -147,10 +189,25 @@ INT64 ScriptDetours::FindScriptParsetree(INT64 name) return 0; } -void ScriptDetours::LinkDetours() +INT64 ScriptDetours::FindLinkedScript(INT64 name, INT32 inst) { - LinkedDetours.clear(); - for (auto it = RegisteredDetours.begin(); it != RegisteredDetours.end(); it++) + INT32 linkedCount = ((INT32*)(OFF_gObjFileInfoCount))[inst]; + LinkedObjFileInfo* linkedObject = &(*(tobjFileInfo*)(OFF_gObjFileInfo))[inst][0]; + + for (int i = 0; i < linkedCount; i++, linkedObject++) + { + if (!linkedObject->object) continue; + INT64 objName = *(INT64*)(linkedObject->object + 0x10); + if (objName != name) continue; + return (INT64)linkedObject->object; + } + return 0; +} + +void ScriptDetours::LinkDetours(INT32 inst) +{ + LinkedDetours[inst].clear(); + for (auto it = RegisteredDetours[inst].begin(); it != RegisteredDetours[inst].end(); it++) { auto detour = *it; if (detour->ReplaceScriptName) // not a builtin @@ -158,9 +215,9 @@ void ScriptDetours::LinkDetours() #ifdef DETOUR_LOGGING GSCBuiltins::nlog("Linking replacement %x<%p>::%x...", detour->ReplaceNamespace, detour->ReplaceScriptName, detour->ReplaceFunction); #endif - // locate the script to replace - auto asset = FindScriptParsetree(detour->ReplaceScriptName); - if (!asset) + // locate the script to replace (using the linked scripts instead of the xassets to avoid issues with unlinked scripts) + auto buffer = FindLinkedScript(detour->ReplaceScriptName, inst); + if (!buffer) { #ifdef DETOUR_LOGGING GSCBuiltins::nlog("Failed to locate %p...", detour->ReplaceScriptName); @@ -172,7 +229,6 @@ void ScriptDetours::LinkDetours() GSCBuiltins::nlog("Located xAssetHeader..."); #endif // locate the target export to link - auto buffer = *(char**)(asset + 0x10); auto exportsOffset = *(INT32*)(buffer + 0x30); auto exports = (INT64)(exportsOffset + buffer); auto numExports = *(INT16*)(buffer + 0x1E); @@ -190,7 +246,7 @@ void ScriptDetours::LinkDetours() #ifdef DETOUR_LOGGING GSCBuiltins::nlog("Found export at %p!", (INT64)buffer + currentExport->bytecodeOffset); #endif - LinkedDetours[(INT64)buffer + currentExport->bytecodeOffset] = detour; + LinkedDetours[inst][(INT64)buffer + currentExport->bytecodeOffset] = detour; break; } } @@ -202,21 +258,21 @@ void ScriptDetours::LinkDetours() INT32 discardType; INT32 discardMinParams; INT32 discardMaxParams; - auto hReplace = Scr_GetFunction(detour->ReplaceFunction, &discardType, &discardMinParams, &discardMaxParams); + auto hReplace = GetFunction(inst, detour->ReplaceFunction, &discardType, &discardMinParams, &discardMaxParams); if (!hReplace) { - hReplace = Scr_GetMethod(detour->ReplaceFunction, &discardType, &discardMinParams, &discardMaxParams); + hReplace = GetMethod(inst, detour->ReplaceFunction, &discardType, &discardMinParams, &discardMaxParams); } if (hReplace) { #ifdef DETOUR_LOGGING GSCBuiltins::nlog("Found function definition at %p!", hReplace); #endif - LinkedDetours[hReplace] = detour; + LinkedDetours[inst][hReplace] = detour; } } } - DetoursLinked = true; + DetoursLinked[inst] = true; } void ScriptDetours::VTableReplace(INT32 original_code, tVM_Opcode ReplaceFunc, tVM_Opcode* OutOld) @@ -295,42 +351,37 @@ void ScriptDetours::VM_OP_CallBuiltinMethod(INT32 inst, INT64* fs_0, INT64 vmc, bool ScriptDetours::CheckDetour(INT32 inst, INT64* fs_0, INT32 offset) { - if (!DetoursEnabled) + if (!DetoursEnabled[inst]) { return false; } // detours are not supported in UI level if (*(BYTE*)(OFF_s_runningUILevel)) { - if (!ScriptDetours::DetoursReset) + if (!ScriptDetours::DetoursReset[inst]) { ResetDetours(); } return false; } - if (inst) - { - // csc is not supported at this time - return false; - } bool fixupApplied = false; - if (!DetoursLinked) + if (!DetoursLinked[inst]) { - LinkDetours(); + LinkDetours(inst); } INT64 ptrval = *(INT64*)((*fs_0 + 7 + offset) & 0xFFFFFFFFFFFFFFF8); - if (LinkedDetours.find(ptrval) != LinkedDetours.end() && LinkedDetours[ptrval]->hFixup) + if (LinkedDetours[inst].find(ptrval) != LinkedDetours[inst].end() && LinkedDetours[inst][ptrval]->hFixup) { INT64 fs_pos = *fs_0; // if pointer is below fixup or above it, the pointer is not within the detour and thus can be fixed up - if (LinkedDetours[ptrval]->hFixup > fs_pos || ((LinkedDetours[ptrval]->hFixup + LinkedDetours[ptrval]->FixupSize) <= fs_pos)) + if (LinkedDetours[inst][ptrval]->hFixup > fs_pos || ((LinkedDetours[inst][ptrval]->hFixup + LinkedDetours[inst][ptrval]->FixupSize) <= fs_pos)) { #ifdef DETOUR_LOGGING - GSCBuiltins::nlog("Replaced call at %p to fixup %p! Opcode: %x", (INT64)((*fs_0 + 7 + offset) & 0xFFFFFFFFFFFFFFF8), LinkedDetours[ptrval]->hFixup, *(INT16*)(*fs_0 - 2)); + GSCBuiltins::nlog("Replaced call at %p to fixup %p! Opcode: %x", (INT64)((*fs_0 + 7 + offset) & 0xFFFFFFFFFFFFFFF8), LinkedDetours[inst][ptrval]->hFixup, *(INT16*)(*fs_0 - 2)); #endif - AppliedFixups[(INT64*)((*fs_0 + 7 + offset) & 0xFFFFFFFFFFFFFFF8)] = ptrval; - *(INT64*)((*fs_0 + 7 + offset) & 0xFFFFFFFFFFFFFFF8) = LinkedDetours[ptrval]->hFixup; - DetoursReset = false; + AppliedFixups[inst][(INT64*)((*fs_0 + 7 + offset) & 0xFFFFFFFFFFFFFFF8)] = ptrval; + *(INT64*)((*fs_0 + 7 + offset) & 0xFFFFFFFFFFFFFFF8) = LinkedDetours[inst][ptrval]->hFixup; + DetoursReset[inst] = false; fixupApplied = true; } } diff --git a/t8cinternal/detours.h b/t8cinternal/detours.h index 90b0e83..fac4916 100644 --- a/t8cinternal/detours.h +++ b/t8cinternal/detours.h @@ -31,28 +31,43 @@ struct SPTEntry INT32 Unk0; }; +struct LinkedObjFileInfo +{ + INT64 object; + int slot; + int refCount; + INT32 groupId; + INT32 Pad0; +}; + +typedef LinkedObjFileInfo tobjFileInfo[2][650]; + + typedef void(__fastcall* tVM_Opcode)(INT32 inst, INT64* fs_0, INT64 vmc, bool* terminate); typedef INT64(__fastcall* tScr_GetFunction)(INT32 canonID, INT32* type, INT32* min_args, INT32* max_args); typedef INT64(__fastcall* tScr_GetMethod)(INT32 canonID, INT32* type, INT32* min_args, INT32* max_args); typedef INT64(__fastcall* tDB_FindXAssetHeader)(int type, char* name, bool errorIfMissing, int waitTime); -typedef INT64(__fastcall* tScr_GscObjLink)(int inst, char* gsc_obj); +typedef INT64(__fastcall* tScr_GscObjLink)(INT32 inst, char* gsc_obj); #define DETOUR_LOGGING class ScriptDetours { public: - static std::vector RegisteredDetours; - static std::unordered_map LinkedDetours; - static std::unordered_map AppliedFixups; + static std::vector RegisteredDetours[2]; + static std::unordered_map LinkedDetours[2]; + static std::unordered_map AppliedFixups[2]; static INT64 FindScriptParsetree(INT64 name); - static bool DetoursLinked; - static bool DetoursReset; - static bool DetoursEnabled; + static INT64 FindLinkedScript(INT64 name, INT32 inst); + static bool DetoursLinked[2]; + static bool DetoursReset[2]; + static bool DetoursEnabled[2]; static bool DetoursInitialized; - static char* GSC_OBJ; + static char* GSC_OBJ[2]; static void InstallHooks(); - static void LinkDetours(); + static void LinkDetours(INT32 inst); + static void RemoveAllDetours(); + static void ResetDetoursByInst(int inst); private: static void VTableReplace(INT32 sub_offset, tVM_Opcode ReplaceFunc, tVM_Opcode* OutOld); @@ -65,8 +80,12 @@ class ScriptDetours static void VM_OP_CallBuiltin(INT32 inst, INT64* fs_0, INT64 vmc, bool* terminate); static void VM_OP_CallBuiltinMethod(INT32 inst, INT64* fs_0, INT64 vmc, bool* terminate); static bool CheckDetour(INT32 inst, INT64* fs_0, INT32 offset = 0); + static INT64 GetFunction(INT32 inst, INT32 canonID, INT32* type, INT32* min_args, INT32* max_args); + static INT64 GetMethod(INT32 inst, INT32 canonID, INT32* type, INT32* min_args, INT32* max_args); static tScr_GetFunction Scr_GetFunction; + static tScr_GetFunction CScr_GetFunction; static tScr_GetMethod Scr_GetMethod; + static tScr_GetMethod CScr_GetMethod; static tScr_GscObjLink Scr_GscObjLink; static tDB_FindXAssetHeader DB_FindXAssetHeader; static tVM_Opcode VM_OP_GetFunction_Old; diff --git a/t8cinternal/dllmain.cpp b/t8cinternal/dllmain.cpp index f67add0..b099a41 100644 --- a/t8cinternal/dllmain.cpp +++ b/t8cinternal/dllmain.cpp @@ -3,6 +3,7 @@ #include "builtins.h" #include "detours.h" #include "LazyLink.h" +#include BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, @@ -12,10 +13,6 @@ BOOL APIENTRY DllMain( HMODULE hModule, switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: - // GSCBuiltins::Init(); - // ScriptDetours::InstallHooks(); - // LazyLink::Init(); - break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: @@ -24,3 +21,21 @@ BOOL APIENTRY DllMain( HMODULE hModule, return TRUE; } + +EXPORT void T8Dll_BuiltinsInit() +{ + static std::once_flag flag; + std::call_once(flag, GSCBuiltins::Init); +} + +EXPORT void T8Dll_DetoursInit() +{ + static std::once_flag flag; + std::call_once(flag, ScriptDetours::InstallHooks); +} + +EXPORT void T8Dll_LazyLinkInit() +{ + static std::once_flag flag; + std::call_once(flag, LazyLink::Init); +} diff --git a/t8cinternal/offsets.h b/t8cinternal/offsets.h index 013e36c..d912eb2 100644 --- a/t8cinternal/offsets.h +++ b/t8cinternal/offsets.h @@ -5,16 +5,22 @@ #define OFFSET(x) ((INT64)GetModuleHandle(NULL) + (INT64)x) -#define OFF_IsProfileBuild OFFSET(0x49621A0) -#define OFF_ScrVm_GetInt OFFSET(0x2773C50) -#define OFF_ScrVm_GetString OFFSET(0x2774940) -#define OFF_ScrVm_GetNumParam OFFSET(0x2774540) -#define OFF_ScrVm_AddInt OFFSET(0x276EC80) -#define OFF_ScrVm_AddBool OFFSET(0x276E860) -#define OFF_ScrVm_Opcodes OFFSET(0x4EEE340) -#define OFF_Scr_GetFunction OFFSET(0x33AF940) -#define OFF_Scr_GetMethod OFFSET(0x33AFD20) -#define OFF_DB_FindXAssetHeader OFFSET(0x2EB76B0) +#define OFF_IsProfileBuild OFFSET(0x49611A0) +#define OFF_ScrVm_GetInt OFFSET(0x2773B50) +#define OFF_ScrVm_GetString OFFSET(0x2774840) +#define OFF_ScrVm_GetNumParam OFFSET(0x2774440) +#define OFF_ScrVm_AddInt OFFSET(0x276EB80) +#define OFF_ScrVm_AddBool OFFSET(0x276E760) +#define OFF_ScrVm_AddUndefined OFFSET(0x4072AE0) +#define OFF_ScrVm_Opcodes OFFSET(0x4EED340) +#define OFF_Scr_GetFunction OFFSET(0x33AF840) +#define OFF_Scr_GetMethod OFFSET(0x33AFC20) +#define OFF_CScr_GetFunction OFFSET(0x1F13140) +#define OFF_CScr_GetMethod OFFSET(0x1F13650) +#define OFF_DB_FindXAssetHeader OFFSET(0x2EB75B0) +#define OFF_xAssetScriptParseTree OFFSET(0x912BBB0) #define XASSETTYPE_SCRIPTPARSETREE 0x30 -#define OFF_s_runningUILevel OFFSET(0x8B51819) -#define OFF_Scr_GscObjLink OFFSET(0x2748F70) \ No newline at end of file +#define OFF_gObjFileInfo OFFSET(0x82efcd0) +#define OFF_gObjFileInfoCount OFFSET(0x82f76b0) +#define OFF_s_runningUILevel OFFSET(0x0000000008B50819) +#define OFF_Scr_GscObjLink OFFSET(0x2748E70) \ No newline at end of file diff --git a/t8cinternal/t8cinternal.vcxproj b/t8cinternal/t8cinternal.vcxproj index cdc775e..6b8852e 100644 --- a/t8cinternal/t8cinternal.vcxproj +++ b/t8cinternal/t8cinternal.vcxproj @@ -5,6 +5,14 @@ Debug Win32 + + Release_Advanced + Win32 + + + Release_Advanced + x64 + Release Win32 @@ -39,6 +47,13 @@ true MultiByte + + DynamicLibrary + false + v142 + true + MultiByte + DynamicLibrary false @@ -52,6 +67,13 @@ true MultiByte + + DynamicLibrary + false + v142 + true + MultiByte + @@ -63,12 +85,18 @@ + + + + + + true @@ -76,12 +104,18 @@ false + + false + true false + + false + Level3 @@ -120,6 +154,27 @@ false + + + Level3 + true + true + true + WIN32;NDEBUG;T7CINTERNAL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + None + MultiThreaded + + + Windows + true + true + true + false + + Level3 @@ -158,6 +213,27 @@ false + + + Level3 + true + true + true + NDEBUG;T7CINTERNAL_EXPORTS;T8CINTERNAL_ADVANCED;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + NotUsing + pch.h + None + MultiThreaded + + + Windows + true + true + true + false + +