예제 #1
0
        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);
        }
예제 #2
0
        static void Main(string[] args)
        {
            bool israel = false;

            if (args.Length < 1 || args.Contains("help"))
            {
                Console.WriteLine(@"Sanae's Disappearing Rabbit Patcher v1.1
A game patcher for Oh Jeez, Oh No, My Rabbits Are Gone!
Command Line Usage: RMP.exe <path to data.win> [patches]
The path to the data.win file should be where you downloaded your game.
If you downloaded it on Steam, look up how to get to a game's local files.
Patches available are:
- all - default option, applies all patches, all must be the first patch in the command
  all other supplied patches will be ignored
  if you want to specific patches, please type them instead of using all
- speedrun - the main patch for speedrunning, some patches have a dependency on this one
- intro - skips the intro in speedrunning mode (depends on speedrun)
- clock - adds a clock to the top left of the window (depends on speedrun)
- frame - press P to change maximum frame speed between 1000 and 60
- debug - adds several debug options to the game (disabled by default) (see github readme for more info)
- color - a cute little bunny color changer, randomized on room (re)entry
- nosave - prevents savepoints from being created (disabled by default)
- decomp - dumps all game code to a folder inside current directory (disabled by default)
- everything - runs *every* patch/action, unlike all, which just runs all non-severe actions/patches
- every - everything except for the decomp action
- ever - every but no decomp and no debug
");
                if (args.Length == 1 && args[0] != "help")
                {
                    Console.WriteLine("Press any key to exit");
                    Console.ReadKey();
                    return;
                }
                var ofd = new OpenFileDialog();
                ofd.Title  = "Locate your data.win file";
                ofd.Filter = "data.win|data.win";
                DialogResult dr = ofd.ShowDialog();
                if (dr == DialogResult.Cancel)
                {
                    return;
                }
                baseloc = ofd.FileName;
                israel  = true;
            }
            else
            {
                baseloc = args[0];
            }
            List <string> actions;

            {
                if (israel || args.Length == 1 || (args[1] == "all"))
                {
                    actions = new List <string>()
                    {
                        "speedrun",
                        "frame",
                        "color",
                        "clock",
                        "intro"
                    }
                }
                ;
                else if (args[1] == "everything")
                {
                    actions = new List <string>()
                    {
                        "speedrun",
                        "frame",
                        "color",
                        "clock",
                        "intro",
                        "debug",
                        "nosave",
                        "decomp"
                    };
                }
                else if (args[1] == "every")
                {
                    actions = new List <string>()
                    {
                        "speedrun",
                        "frame",
                        "color",
                        "clock",
                        "intro",
                        "debug",
                        "nosave"
                    };
                }
                else
                {
                    actions = new List <string>()
                    {
                        ""
                    };
                    actions.AddRange(args.Skip(1));
                }
            }
            if (!Path.HasExtension(baseloc) || !Directory.Exists(Path.GetDirectoryName(baseloc)))
            {
                Console.Error.WriteLine("Invalid data.win path!\n" + baseloc);
                return;
            }
            baseloc = Path.GetDirectoryName(baseloc) + "\\";
            if (!File.Exists(baseloc + "data.win.orig"))
            {
                if (File.Exists(baseloc + "data.win"))
                {
                    File.Move(baseloc + "data.win", baseloc + "data.win.orig");
                }
                else
                {
                    Console.Error.WriteLine("There's no data.win in this folder, and there's no data.win.orig!");
                    return;
                }
            }
            File.Delete(baseloc + "data.win");
            File.Copy(baseloc + "data.win.orig", baseloc + "data.win");
            FileStream    fileStream = File.Open(baseloc + "data.win", FileMode.Open);
            UndertaleData data       = UndertaleIO.Read(fileStream);

            fileStream.Close();
            File.Delete(baseloc + "data.win");
            if (data.GeneralInfo.DisplayName.Content != "My Rabbits Are Gone")
            {
                Console.Error.WriteLine("This data.win is not from Oh Jeez, Oh No, My Rabbits Are Gone\nPlease provide that one instead.");
                File.Copy(baseloc + "data.win.orig", baseloc + "data.win");
                return;
            }

            List <string> usedActions = new List <string>();
            var           gi          = new UndertaleGlobalInit();
            var           code        = new UndertaleCode();

            code.ReplaceGML("global.patched = true", data);
            code.Name = data.Strings.MakeString("gml_global_set_patched");
            gi.Code   = code;
            bool speedran = false;

            for (int i = 0; i < actions.Count; i++)
            {
                if (actions[i] != "")
                {
                    if (usedActions.Contains(actions[i]))
                    {
                        continue;
                    }
                    usedActions.Add(actions[i]);//prevent repatching actions that are already patched
                    Console.WriteLine(actions[i]);
                }
                switch (actions[i])
                {
                case "speedrun":
                    speedran = true;
                    SpeedrunPatches(data);
                    break;

                case "debug":
                    DebugPatches(data);
                    break;

                case "clock":
                    if (!speedran)
                    {
                        YUNORAN();
                    }
                    ClockPatches(data);
                    break;

                case "intro":
                    if (!speedran)
                    {
                        YUNORAN();
                    }
                    IntroPatch(data);
                    break;

                case "nosave":
                    if (!speedran)
                    {
                        YUNORAN();
                    }
                    SavePointPatch(data);
                    break;

                case "decomp":
                    Directory.CreateDirectory("./decomp/");
                    for (int ia = 0; ia < data.Code.Count; ia++)
                    {
                        UndertaleCode uc = data.Code[ia];
                        Console.WriteLine("Writing " + uc.Name.Content);
                        try
                        {
                            File.WriteAllText(baseloc + "/decomp/" + uc.Name.Content, Decompiler.Decompile(uc, new DecompileContext(data, false)));
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine("failed! " + e.Message);
                        }
                        Console.WriteLine("Wrote " + uc.Name.Content);
                    }
                    break;

                case "frame":
                    FramecapRemover(data);
                    break;

                case "color":
                    ColorRandomizer(data);
                    break;

                case "multiplayer":
                    Console.WriteLine("maybe one day, not today 😭");
                    break;

                case "":
                    break;

                default:
                    Console.Error.WriteLine($"Invalid action {actions[i]}\n Run RMP.exe with no arguments to see proper usage of the program.");
                    File.Copy(baseloc + "data.win.orig", baseloc + "data.win");
                    return;
                }
            }

            UndertaleIO.Write(File.Open(baseloc + "data.win", FileMode.OpenOrCreate), data);
            Console.WriteLine("Wrote data.win! A new option has been added to the main menu.");
        }