static void AddAsyncEvent(string path) { string eventSrc = File.ReadAllText(path); string name = "gml_Object_objControlerN_Other_75"; // Make a code entry. UndertaleCode codeEntry = new UndertaleCode { Name = Data.Strings.MakeString(name) }; Data.Code.Add(codeEntry); // Make a code locals entry. UndertaleCodeLocals locals = new UndertaleCodeLocals { Name = codeEntry.Name }; // Make a `var arguments;` entry. UndertaleCodeLocals.LocalVar argsLocal = new UndertaleCodeLocals.LocalVar { Name = Data.Strings.MakeString("arguments"), Index = 0 }; // Glue everything together. locals.Locals.Add(argsLocal); Data.CodeLocals.Add(locals); // Set code locals entry for the code entry. codeEntry.LocalsCount = 1; //codeEntry.GenerateLocalVarDefinitions(codeEntry.FindReferencedLocalVars(), locals); // fails here. // FINALLY compile our script. Data.Code.ByName(name).ReplaceGML(eventSrc, Data); // Add this code entry to Async System event in objControllerN... var obj = Data.GameObjects.ByName("objControlerN"); int OtherEventInd = 7; uint MethodNumber = 75; UndertaleGameObject.EventAction action = new UndertaleGameObject.EventAction { ActionName = codeEntry.Name, CodeId = codeEntry }; UndertaleGameObject.Event evnt = new UndertaleGameObject.Event { EventSubtype = MethodNumber }; evnt.Actions.Add(action); var eventList = obj.Events[OtherEventInd]; eventList.Add(evnt); }
static void AddScriptFromFile(string path) { string scriptSource = File.ReadAllText(path); string gmlWeirdName = "gml_Script_" + Path.GetFileNameWithoutExtension(path); // Make a code entry. UndertaleCode codeEntry = new UndertaleCode { Name = Data.Strings.MakeString(gmlWeirdName) }; Data.Code.Add(codeEntry); // Make a code locals entry. UndertaleCodeLocals locals = new UndertaleCodeLocals { Name = codeEntry.Name }; // Make a `var arguments;` entry. UndertaleCodeLocals.LocalVar argsLocal = new UndertaleCodeLocals.LocalVar { Name = Data.Strings.MakeString("arguments"), Index = 0 }; // Glue everything together. locals.Locals.Add(argsLocal); Data.CodeLocals.Add(locals); // Set code locals entry for the code entry. codeEntry.LocalsCount = 1; //codeEntry.GenerateLocalVarDefinitions(codeEntry.FindReferencedLocalVars(), locals); // fails here. // FINALLY compile our script. Data.Code.ByName(gmlWeirdName).ReplaceGML(scriptSource, Data); // ... and actually add it like a script... var scr = new UndertaleScript { Code = Data.Code.ByName(gmlWeirdName), Name = Data.Strings.MakeString(Path.GetFileNameWithoutExtension(path)) }; Data.Scripts.Add(scr); // ... oh, and don't forget to add a *function* reference. var funcentry = new UndertaleFunction { Name = Data.Strings.MakeString(Path.GetFileNameWithoutExtension(path)) }; Data.Functions.Add(funcentry); }
public static string Disassemble(this UndertaleCode code, IList <UndertaleVariable> vars, UndertaleCodeLocals locals) { StringBuilder sb = new StringBuilder(); if (locals == null && !code.WeirdLocalFlag) { sb.Append("; WARNING: Missing code locals, possibly due to unsupported bytecode version or a brand new code entry.\n"); } else { sb.Append(code.GenerateLocalVarDefinitions(vars, locals)); } Dictionary <uint, string> fragments = new Dictionary <uint, string>(); foreach (var dup in code.Duplicates) { fragments.Add(dup.Offset / 4, (dup.Name?.Content ?? "<null>") + $" (locals={dup.LocalsCount}, argc={dup.ArgumentsCount})"); } List <uint> blocks = FindBlockAddresses(code); foreach (var inst in code.Instructions) { bool doNewline = true; if (fragments.TryGetValue(inst.Address, out string entry)) { sb.AppendLine(); sb.AppendLine($"> {entry}"); doNewline = false; } int ind = blocks.IndexOf(inst.Address); if (ind != -1) { if (doNewline) { sb.AppendLine(); } sb.AppendLine($":[{ind}]"); } sb.AppendLine(inst.ToString(code, blocks)); } sb.AppendLine(); sb.Append(":[end]"); return(sb.ToString()); }
public static string GenerateLocalVarDefinitions(this UndertaleCode code, IList <UndertaleVariable> vars, UndertaleCodeLocals locals) { if (code.WeirdLocalFlag) { return(""); } if (locals == null) { return("; Missing code locals- possibly due to unsupported bytecode version or brand new code entry.\n"); } StringBuilder sb = new StringBuilder(); var referenced = code.FindReferencedLocalVars(); if (locals.Name != code.Name) { throw new Exception("Name of the locals block does not match name of the code block"); } foreach (var arg in locals.Locals) { sb.Append(".localvar " + arg.Index + " " + arg.Name.Content); var refvar = referenced.Where((x) => x.Name == arg.Name && x.VarID == arg.Index).FirstOrDefault(); if (refvar != null) { sb.Append(" " + vars.IndexOf(refvar)); } sb.Append('\n'); } return(sb.ToString()); }
void ImportCode(string codeName, string gmlCode, bool IsGML = true, bool doParse = true, bool destroyASM = true, bool CheckDecompiler = false) { bool SkipPortions = false; UndertaleCode code = Data.Code.ByName(codeName); if (Data.Code.ByName(codeName) == null) { code = new UndertaleCode(); code.Name = Data.Strings.MakeString(codeName); Data.Code.Add(code); } if (Data?.GeneralInfo.BytecodeVersion > 14 && Data.CodeLocals.ByName(codeName) == null) { UndertaleCodeLocals locals = new UndertaleCodeLocals(); locals.Name = code.Name; UndertaleCodeLocals.LocalVar argsLocal = new UndertaleCodeLocals.LocalVar(); argsLocal.Name = Data.Strings.MakeString("arguments"); argsLocal.Index = 0; locals.Locals.Add(argsLocal); code.LocalsCount = 1; code.GenerateLocalVarDefinitions(code.FindReferencedLocalVars(), locals); // Dunno if we actually need this line, but it seems to work? Data.CodeLocals.Add(locals); } if (doParse) { // This portion links code. if (codeName.Substring(0, 10).Equals("gml_Script")) { // Add code to scripts section. if (Data.Scripts.ByName(codeName.Substring(11)) == null) { UndertaleScript scr = new UndertaleScript(); scr.Name = Data.Strings.MakeString(codeName.Substring(11)); scr.Code = code; Data.Scripts.Add(scr); } else { UndertaleScript scr = Data.Scripts.ByName(codeName.Substring(11)); scr.Code = code; } } else if (codeName.Substring(0, 16).Equals("gml_GlobalScript")) { // Add code to global init section. UndertaleGlobalInit init_entry = null; // This doesn't work, have to do it the hard way: UndertaleGlobalInit init_entry = Data.GlobalInitScripts.ByName(scr_dup_code_name_con); foreach (UndertaleGlobalInit globalInit in Data.GlobalInitScripts) { if (globalInit.Code.Name.Content == codeName) { init_entry = globalInit; break; } } if (init_entry == null) { UndertaleGlobalInit NewInit = new UndertaleGlobalInit(); NewInit.Code = code; Data.GlobalInitScripts.Add(NewInit); } else { UndertaleGlobalInit NewInit = init_entry; NewInit.Code = code; } } else if (codeName.Substring(0, 10).Equals("gml_Object")) { string afterPrefix = codeName.Substring(11); int underCount = 0; string methodNumberStr = "", methodName = "", objName = ""; for (int i = afterPrefix.Length - 1; i >= 0; i--) { if (afterPrefix[i] == '_') { underCount++; if (underCount == 1) { methodNumberStr = afterPrefix.Substring(i + 1); } else if (underCount == 2) { objName = afterPrefix.Substring(0, i); methodName = afterPrefix.Substring(i + 1, afterPrefix.Length - objName.Length - methodNumberStr.Length - 2); break; } } } int methodNumber = 0; try { methodNumber = int.Parse(methodNumberStr); if (methodName == "Collision" && (methodNumber >= Data.GameObjects.Count || methodNumber < 0)) { bool doNewObj = ScriptQuestion("Object of ID " + methodNumber.ToString() + " was not found.\nAdd new object?"); if (doNewObj) { UndertaleGameObject gameObj = new UndertaleGameObject(); gameObj.Name = Data.Strings.MakeString(SimpleTextInput("Enter object name", "Enter object name", "This is a single text line input box test.", false)); Data.GameObjects.Add(gameObj); } else { // It *needs* to have a valid value, make the user specify one. List <uint> possible_values = new List <uint>(); possible_values.Add(uint.MaxValue); methodNumber = (int)ReduceCollisionValue(possible_values); } } } catch { if (afterPrefix.LastIndexOf("_Collision_") != -1) { string s2 = "_Collision_"; objName = afterPrefix.Substring(0, (afterPrefix.LastIndexOf("_Collision_"))); methodNumberStr = afterPrefix.Substring(afterPrefix.LastIndexOf("_Collision_") + s2.Length, afterPrefix.Length - (afterPrefix.LastIndexOf("_Collision_") + s2.Length)); methodName = "Collision"; // GMS 2.3+ use the object name for the one colliding, which is rather useful. if (Data.GMS2_3) { if (Data.GameObjects.ByName(methodNumberStr) != null) { for (var i = 0; i < Data.GameObjects.Count; i++) { if (Data.GameObjects[i].Name.Content == methodNumberStr) { methodNumber = i; break; } } } else { bool doNewObj = ScriptQuestion("Object " + objName + " was not found.\nAdd new object called " + objName + "?"); if (doNewObj) { UndertaleGameObject gameObj = new UndertaleGameObject(); gameObj.Name = Data.Strings.MakeString(objName); Data.GameObjects.Add(gameObj); } } if (Data.GameObjects.ByName(methodNumberStr) != null) { // It *needs* to have a valid value, make the user specify one, silly. List <uint> possible_values = new List <uint>(); possible_values.Add(uint.MaxValue); ReassignGUIDs(methodNumberStr, ReduceCollisionValue(possible_values)); } } else { // Let's try to get this going methodNumber = (int)ReduceCollisionValue(GetCollisionValueFromCodeNameGUID(codeName)); ReassignGUIDs(methodNumberStr, ReduceCollisionValue(GetCollisionValueFromCodeNameGUID(codeName))); } } } UndertaleGameObject obj = Data.GameObjects.ByName(objName); if (obj == null) { bool doNewObj = ScriptQuestion("Object " + objName + " was not found.\nAdd new object called " + objName + "?"); if (doNewObj) { UndertaleGameObject gameObj = new UndertaleGameObject(); gameObj.Name = Data.Strings.MakeString(objName); Data.GameObjects.Add(gameObj); } else { SkipPortions = true; } } if (!(SkipPortions)) { obj = Data.GameObjects.ByName(objName); int eventIdx = (int)Enum.Parse(typeof(EventTypes), methodName); bool duplicate = false; try { foreach (UndertaleGameObject.Event evnt in obj.Events[eventIdx]) { foreach (UndertaleGameObject.EventAction action in evnt.Actions) { if (action.CodeId?.Name?.Content == codeName) { duplicate = true; } } } } catch { // Something went wrong, but probably because it's trying to check something non-existent // Just keep going } if (duplicate == false) { UndertalePointerList <UndertaleGameObject.Event> eventList = obj.Events[eventIdx]; UndertaleGameObject.EventAction action = new UndertaleGameObject.EventAction(); UndertaleGameObject.Event evnt = new UndertaleGameObject.Event(); action.ActionName = code.Name; action.CodeId = code; evnt.EventSubtype = (uint)methodNumber; evnt.Actions.Add(action); eventList.Add(evnt); } } } } SafeImport(codeName, gmlCode, IsGML, destroyASM, CheckDecompiler); }