private static void DumpConsoleCallableFunctions(CacheFile cache) { var gameInstanceDef = cache.GetNative("GameInstance"); var stringType = cache.GetNative("String"); var candidates = cache.Definitions .OfType <FunctionDefinition>() .Where(f => f.Parameters.Count >= 1 && f.Parameters[0].Type == gameInstanceDef && f.Parameters.Skip(1).All(p => p.Type == stringType)) .ToArray(); var regex = new Regex(@"^[a-zA-Z0-9_]+$"); var sb = new StringBuilder(); sb.AppendLine("Script functions that are callable by yamashi's CyberEngineTweaks console."); sb.AppendLine("https://github.com/yamashi/CyberEngineTweaks"); sb.AppendLine(); foreach (var tuple in candidates .Select(c => (function: c, path: c.ToPath())) .Where(t => t.function.Parent == null) .OrderBy(t => t.path)) { var(function, path) = tuple; if (regex.IsMatch(function.Name) == true) { sb.Append($"Game.{function.Name}"); } else { var name = LuaHelpers.Escape(function.Name); sb.Append($"Game['{name}']"); } sb.Append('('); for (int i = 1; i < function.Parameters.Count; i++) { var parameter = function.Parameters[i]; if (i > 1) { sb.Append(", "); } sb.Append($"{parameter.Name}"); } sb.AppendLine(")"); } File.WriteAllText("console_callable_script_functions.txt", sb.ToString()); }
private static void AddExecCommandSTS(CacheFile cache, SourceFileDefinition mySourceFile) { var gameInstanceNative = cache.GetNative("GameInstance"); var stringNative = cache.GetNative("String"); var inkMenuInstanceSwitchToScenarioClass = cache.GetClass("inkMenuInstance_SwitchToScenario"); /* Add a new definition for a native inkMenuInstance_SwitchToScenario since it doesn't exist * in the cache by default. It is actually defined by the game but none of the existing game * code uses it, so it's missing. */ var inkMenuInstanceSwitchToScenario = new NativeDefinition() { Name = "inkMenuInstance_SwitchToScenario", NativeType = NativeType.Complex, }; cache.Definitions.Add(inkMenuInstanceSwitchToScenario); // Add a new ref native for the previous native. var inkMenuInstanceSwitchToScenarioRef = new NativeDefinition() { Name = "ref:inkMenuInstance_SwitchToScenario", NativeType = NativeType.Handle, BaseType = inkMenuInstanceSwitchToScenario, }; cache.Definitions.Add(inkMenuInstanceSwitchToScenarioRef); // stuff we use in our custom function var stringToNameFunction = cache.GetFunction("StringToName"); var getUISystemFunction = cache.GetFunction("GameInstance", "GetUISystem"); var uiSystemClass = cache.GetClass("UISystem"); var queueEventFunction = uiSystemClass.GetFunction("QueueEvent"); var myFunction = new FunctionDefinition() { Name = "STS", Flags = FunctionFlags.IsStatic | FunctionFlags.IsExec | FunctionFlags.HasParameters | FunctionFlags.HasLocals | FunctionFlags.HasCode, SourceFile = mySourceFile, }; cache.Definitions.Add(myFunction); var myParameter0 = new ParameterDefinition() { Name = "gameInstance", Parent = myFunction, Type = gameInstanceNative, Flags = ParameterFlags.None, }; cache.Definitions.Add(myParameter0); myFunction.Parameters.Add(myParameter0); var myParameter1 = new ParameterDefinition() { Name = "name", Parent = myFunction, Type = stringNative, Flags = ParameterFlags.None, }; cache.Definitions.Add(myParameter1); myFunction.Parameters.Add(myParameter1); var myLocal0 = new LocalDefinition() { Name = "event", Parent = myFunction, Type = inkMenuInstanceSwitchToScenarioRef, Unknown28 = 0, }; cache.Definitions.Add(myLocal0); myFunction.Locals.Add(myLocal0); var cg = new CodeGenerator(); // event = new inkMenuInstance_SwitchToScenario cg.Emit(Opcode.Assign); { cg.Emit(Opcode.LocalVar, myLocal0); cg.Emit(Opcode.New, inkMenuInstanceSwitchToScenarioClass); } // event.Init(StringToName(name), 0) var afterEventContextLabel = cg.DefineLabel(); cg.Emit(Opcode.Context, afterEventContextLabel); { cg.Emit(Opcode.LocalVar, myLocal0); cg.Emit(Opcode.FinalFunc, afterEventContextLabel, 0, inkMenuInstanceSwitchToScenarioClass.Functions[0]); { // StringToName(name) var afterStringToNameLabel = cg.DefineLabel(); cg.Emit(Opcode.FinalFunc, afterStringToNameLabel, 0, stringToNameFunction); { cg.Emit(Opcode.ParamVar, myParameter1); cg.Emit(Opcode.ParamEnd); } cg.MarkLabel(afterStringToNameLabel); } cg.Emit(Opcode.Nop); cg.Emit(Opcode.ParamEnd); } cg.MarkLabel(afterEventContextLabel); // getUISystem(gameInstance).QueueEvent(event) var afterUISystemContextLabel = cg.DefineLabel(); cg.Emit(Opcode.Context, afterUISystemContextLabel); { var afterGetUISystemLabel = cg.DefineLabel(); cg.Emit(Opcode.FinalFunc, afterGetUISystemLabel, 0, getUISystemFunction); { cg.Emit(Opcode.ParamVar, myParameter0); cg.Emit(Opcode.ParamEnd); } cg.MarkLabel(afterGetUISystemLabel); cg.Emit(Opcode.FinalFunc, afterUISystemContextLabel, 0, queueEventFunction); { cg.Emit(Opcode.LocalVar, myLocal0); cg.Emit(Opcode.ParamEnd); } } cg.MarkLabel(afterUISystemContextLabel); cg.Emit(Opcode.Nop); myFunction.Code.AddRange(cg.GetCode()); }