private static void ProcessUMFMod(string modName)
        {
            Logger.Log("Processing UMF mod '" + modName + "'");
            Type[]            gadgetTypes = GadgetCoreAPI.GetUMFAPI().GetModAssembly(modName).GetExportedTypes().Where(x => x.IsSubclassOf(typeof(Gadget)) && x.GetCustomAttributes(typeof(GadgetAttribute), true).FirstOrDefault() != null).ToArray();
            List <GadgetInfo> gadgets     = new List <GadgetInfo>();

            for (int i = 0; i < gadgetTypes.Length; i++)
            {
                Type            type      = gadgetTypes[i];
                GadgetAttribute attribute = (GadgetAttribute)type.GetCustomAttributes(typeof(GadgetAttribute), true).FirstOrDefault();
                if (gadgets.Any(x => x.Attribute.Name == attribute.Name))
                {
                    throw new InvalidOperationException("It is illegal for a mod to contain multiple Gadgets with the same name: " + attribute.Name);
                }
                int[] targetVersionNums = attribute.TargetGCVersion.Split('.').Select(x => int.Parse(x)).ToArray();
                if (targetVersionNums.Length != 4)
                {
                    Array.Resize(ref targetVersionNums, 4);
                }
                if ((attribute.GadgetCoreVersionSpecificity == VersionSpecificity.MAJOR && GadgetCoreAPI.currentVersionNums[0] == targetVersionNums[0] && (GadgetCoreAPI.currentVersionNums[1] > targetVersionNums[1] || (GadgetCoreAPI.currentVersionNums[1] == targetVersionNums[1] && (GadgetCoreAPI.currentVersionNums[2] > targetVersionNums[2] || (GadgetCoreAPI.currentVersionNums[2] == targetVersionNums[2] && GadgetCoreAPI.currentVersionNums[3] >= targetVersionNums[3]))))) ||
                    (attribute.GadgetCoreVersionSpecificity == VersionSpecificity.MINOR && GadgetCoreAPI.currentVersionNums[0] == targetVersionNums[0] && GadgetCoreAPI.currentVersionNums[1] == targetVersionNums[1] && (GadgetCoreAPI.currentVersionNums[2] > targetVersionNums[2] || (GadgetCoreAPI.currentVersionNums[2] == targetVersionNums[2] && GadgetCoreAPI.currentVersionNums[3] >= targetVersionNums[3]))) ||
                    (attribute.GadgetCoreVersionSpecificity == VersionSpecificity.NONBREAKING && GadgetCoreAPI.currentVersionNums[0] == targetVersionNums[0] && GadgetCoreAPI.currentVersionNums[1] == targetVersionNums[1] && GadgetCoreAPI.currentVersionNums[2] == targetVersionNums[2] && GadgetCoreAPI.currentVersionNums[3] >= targetVersionNums[3]) ||
                    (attribute.GadgetCoreVersionSpecificity == VersionSpecificity.BUGFIX && GadgetCoreAPI.currentVersionNums[0] == targetVersionNums[0] && GadgetCoreAPI.currentVersionNums[1] == targetVersionNums[1] && GadgetCoreAPI.currentVersionNums[2] == targetVersionNums[2] && GadgetCoreAPI.currentVersionNums[3] == targetVersionNums[3]))
                {
                    Gadget gadget = null;
                    try
                    {
                        gadget = Activator.CreateInstance(type) as Gadget;
                        if (gadget != null)
                        {
                            Logger.Log("Found Gadget to load: " + attribute.Name + ", in UMF mod: {" + modName + "}");
                            GadgetInfo info = new GadgetInfo(gadget, attribute, modName);
                            gadget.Logger = new GadgetLogger(modName, attribute.Name);
                            gadget.Config = new GadgetConfig(Path.Combine(GadgetPaths.ConfigsPath, modName + ".ini"), attribute.Name);
                            Gadgets.RegisterGadget(info);
                            gadgets.Add(info);
                            if (!BatchLoading)
                            {
                                QueuedGadgets.Add(info);
                                EnableQueuedGadgets();
                            }
                        }
                    }
                    catch (Exception) { }
                    finally
                    {
                        if (gadget == null)
                        {
                            Logger.LogWarning("Found Gadget that could not be constructed: " + attribute.Name + ", in UMF mod: {" + modName + "}");
                        }
                    }
                }
                else
                {
                    int rD = (int)attribute.GadgetCoreVersionSpecificity;
                    Logger.LogWarning("Found Gadget with an incompatible version: " + attribute.Name + ", in UMF mod: {" + modName + "}. Requires version: " + new string(attribute.TargetGCVersion.TakeWhile(x => (x == '.' ? --rD : rD) > 0).ToArray()));
                }
            }
        }
Exemple #2
0
        public HttpResponseMessage Delete(GadgetInfo gadget)
        {
            try
            {
                _repository.RemoveGadgetLink(gadget);

                return(Request.CreateResponse(HttpStatusCode.OK));
            }
            catch (Exception ex)
            {
                return(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex));
            }
        }
        internal static void EnableQueuedGadgets()
        {
            List <GadgetInfo> preFilteredQueuedGadgets = QueuedGadgets;

            QueuedGadgets = new List <GadgetInfo>();
            foreach (GadgetInfo gadget in preFilteredQueuedGadgets)
            {
                bool valid = true;
                foreach (string dependency in gadget.Attribute.Dependencies)
                {
                    string[]   splitDependency  = dependency.Split(' ');
                    GadgetInfo dependencyGadget = Gadgets.GetGadgetInfo(splitDependency[0]);
                    valid = dependencyGadget != null;
                    if (valid && splitDependency.Length == 2)
                    {
                        string versionString     = splitDependency[1].Replace("v", "");
                        int[]  targetVersionNums = versionString.Split('.').Select(x => int.Parse(x)).ToArray();
                        if (targetVersionNums.Length > 4)
                        {
                            continue;
                        }
                        int[] actualVersionNums = dependencyGadget.Mod.Version.ToString().Split('.').Select(x => int.Parse(x)).ToArray();
                        VersionSpecificity versionSpecificity = (VersionSpecificity)targetVersionNums.Length;
                        if (targetVersionNums.Length != 4)
                        {
                            Array.Resize(ref targetVersionNums, 4);
                        }
                        if (actualVersionNums.Length != 4)
                        {
                            Array.Resize(ref actualVersionNums, 4);
                        }
                        valid = (versionSpecificity == VersionSpecificity.MAJOR && actualVersionNums[0] == targetVersionNums[0] && (actualVersionNums[1] > targetVersionNums[1] || (actualVersionNums[1] == targetVersionNums[1] && (actualVersionNums[2] > targetVersionNums[2] || (actualVersionNums[2] == targetVersionNums[2] && actualVersionNums[3] >= targetVersionNums[3]))))) ||
                                (versionSpecificity == VersionSpecificity.MINOR && actualVersionNums[0] == targetVersionNums[0] && actualVersionNums[1] == targetVersionNums[1] && (actualVersionNums[2] > targetVersionNums[2] || (actualVersionNums[2] == targetVersionNums[2] && actualVersionNums[3] >= targetVersionNums[3]))) ||
                                (versionSpecificity == VersionSpecificity.NONBREAKING && actualVersionNums[0] == targetVersionNums[0] && actualVersionNums[1] == targetVersionNums[1] && actualVersionNums[2] == targetVersionNums[2] && actualVersionNums[3] >= targetVersionNums[3]) ||
                                (versionSpecificity == VersionSpecificity.BUGFIX && actualVersionNums[0] == targetVersionNums[0] && actualVersionNums[1] == targetVersionNums[1] && actualVersionNums[2] == targetVersionNums[2] && actualVersionNums[3] == targetVersionNums[3]);
                    }
                }
                if (valid)
                {
                    QueuedGadgets.Add(gadget);
                }
                else
                {
                    gadget.Gadget.Enabled = false;
                    GadgetCoreConfig.enabledGadgets[gadget.Attribute.Name] = false;
                    Gadgets.UnregisterGadget(gadget);
                    Logger.LogWarning("Aborted loading Gadget " + gadget.Attribute.Name + " due to missing dependencies.");
                }
            }
            Logger.Log("Loading Gadget configs...");
            foreach (GadgetInfo gadget in QueuedGadgets.ToList())
            {
                Logger.Log("Loading Config for Gadget '" + gadget.Attribute.Name + "'");
                try
                {
                    gadget.Gadget.LoadConfig();
                }
                catch (Exception e)
                {
                    gadget.Gadget.Enabled = false;
                    GadgetCoreConfig.enabledGadgets[gadget.Attribute.Name] = false;
                    Gadgets.UnregisterGadget(gadget);
                    QueuedGadgets.Remove(gadget);
                    Logger.LogError("Exception Loading Config For Gadget '" + gadget.Attribute.Name + "':" + Environment.NewLine + e.ToString());
                }
            }
            Logger.Log("Done loading Gadget configs.");
            Logger.Log("Preparing Gadgets for patching...");
            foreach (GadgetInfo gadget in QueuedGadgets.ToList())
            {
                Logger.Log("PrePatching Gadget '" + gadget.Attribute.Name + "'");
                try
                {
                    gadget.Gadget.HarmonyInstance = new Harmony(gadget.Mod.Name + "." + gadget.Attribute.Name + ".gadget");
                    gadget.Gadget.PrePatch();
                }
                catch (Exception e)
                {
                    gadget.Gadget.Enabled = false;
                    GadgetCoreConfig.enabledGadgets[gadget.Attribute.Name] = false;
                    Gadgets.UnregisterGadget(gadget);
                    QueuedGadgets.Remove(gadget);
                    Logger.LogError("Exception PrePatching Gadget '" + gadget.Attribute.Name + "':" + Environment.NewLine + e.ToString());
                }
            }
            Logger.Log("Done preparing Gadgets for patching.");
            Logger.Log("Patching Gadgets...");
            foreach (GadgetInfo gadget in QueuedGadgets.ToList())
            {
                int patches = 0;
                try
                {
                    gadget.Mod.Assembly.GetExportedTypes().Do(delegate(Type type)
                    {
                        HarmonyGadgetAttribute attribute = type.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(HarmonyGadgetAttribute)) as HarmonyGadgetAttribute;
                        if (attribute?.Gadget == gadget.Attribute.Name && attribute.RequiredGadgets.All(x => Gadgets.GetGadget(x) != null))
                        {
                            try
                            {
                                gadget.Gadget.HarmonyInstance.CreateClassProcessor(type).Patch();
                                patches++;
                            }
                            catch (Exception e)
                            {
                                Logger.LogError("Exception running patch '" + type.Name + "' for Gadget '" + gadget.Attribute.Name + "': " + Environment.NewLine + e.ToString());
                            }
                        }
                    });
                    Logger.Log("Performed " + patches + " patches for " + gadget.Attribute.Name);
                }
                catch (Exception e)
                {
                    gadget.Gadget.Enabled = false;
                    GadgetCoreConfig.enabledGadgets[gadget.Attribute.Name] = false;
                    Gadgets.UnregisterGadget(gadget);
                    QueuedGadgets.Remove(gadget);
                    Logger.LogError("Exception patching Gadget '" + gadget.Attribute.Name + "':" + Environment.NewLine + e.ToString());
                }
            }
            Logger.Log("Done patching Gadgets.");
            Logger.Log("Creating registries...");
            foreach (GadgetInfo gadget in QueuedGadgets.ToList())
            {
                if (gadget.Gadget.Enabled)
                {
                    foreach (Registry registry in gadget.Gadget.CreateRegistries())
                    {
                        GameRegistry.RegisterRegistry(registry);
                    }
                }
            }
            Logger.Log("Done creating registries.");
            Logger.Log("Initializing Gadgets...");
            foreach (GadgetInfo gadget in QueuedGadgets.ToList())
            {
                Logger.Log("Initializing Gadget '" + gadget.Attribute.Name + "'");
                try
                {
                    Registry.modRegistering = gadget.Gadget.ModID;
                    gadget.Gadget.Initialize();
                }
                catch (Exception e)
                {
                    gadget.Gadget.Enabled = false;
                    GadgetCoreConfig.enabledGadgets[gadget.Attribute.Name] = false;
                    Gadgets.UnregisterGadget(gadget);
                    QueuedGadgets.Remove(gadget);
                    Logger.LogError("Exception Initializing Gadget '" + gadget.Attribute.Name + "':" + Environment.NewLine + e.ToString());
                }
                finally
                {
                    Registry.modRegistering = -1;
                }
            }
            Logger.Log("Done initializing Gadgets.");
            QueuedGadgets.Clear();
        }
 private static void LoadGadgetMod(GadgetMod mod)
 {
     Logger.Log("Loading mod '" + mod.Name + "'");
     if (mod.ModDependencies.Any(x => GadgetMods.GetModByName(x) == null))
     {
         m_IncompatibleMods.Add(mod);
         Logger.LogWarning("Aborted loading mod '" + mod.Name + "' because of the following missing dependencies: " + mod.ModDependencies.Where(x => GadgetMods.GetModByName(x) == null).Concat());
         return;
     }
     Type[] gadgetTypes = mod.Assembly.GetExportedTypes().Where(x => x.IsSubclassOf(typeof(Gadget)) && x.GetCustomAttributes(typeof(GadgetAttribute), true).FirstOrDefault() != null).ToArray();
     if (gadgetTypes.Length == 0)
     {
         m_EmptyMods.Add(mod);
         Logger.LogWarning("Aborted loading mod '" + mod.Name + "' because it does not contain any Gadgets");
     }
     mod.m_LoadedGadgets   = new List <GadgetInfo>();
     mod.LoadedGadgets     = new ReadOnlyCollection <GadgetInfo>(mod.m_LoadedGadgets);
     mod.m_UnloadedGadgets = new List <GadgetInfo>();
     mod.UnloadedGadgets   = new ReadOnlyCollection <GadgetInfo>(mod.m_UnloadedGadgets);
     for (int i = 0; i < gadgetTypes.Length; i++)
     {
         Type            type      = gadgetTypes[i];
         GadgetAttribute attribute = (GadgetAttribute)type.GetCustomAttributes(typeof(GadgetAttribute), true).FirstOrDefault();
         if (mod.m_LoadedGadgets.Any(x => x.Attribute.Name == attribute.Name))
         {
             throw new InvalidOperationException("It is illegal for a mod to contain multiple Gadgets with the same name: " + attribute.Name);
         }
         int[] targetVersionNums = attribute.TargetGCVersion.Split('.').Select(x => int.Parse(x)).ToArray();
         if (targetVersionNums.Length != 4)
         {
             Array.Resize(ref targetVersionNums, 4);
         }
         if ((attribute.GadgetCoreVersionSpecificity == VersionSpecificity.MAJOR && GadgetCoreAPI.currentVersionNums[0] == targetVersionNums[0] && (GadgetCoreAPI.currentVersionNums[1] > targetVersionNums[1] || (GadgetCoreAPI.currentVersionNums[1] == targetVersionNums[1] && (GadgetCoreAPI.currentVersionNums[2] > targetVersionNums[2] || (GadgetCoreAPI.currentVersionNums[2] == targetVersionNums[2] && GadgetCoreAPI.currentVersionNums[3] >= targetVersionNums[3]))))) ||
             (attribute.GadgetCoreVersionSpecificity == VersionSpecificity.MINOR && GadgetCoreAPI.currentVersionNums[0] == targetVersionNums[0] && GadgetCoreAPI.currentVersionNums[1] == targetVersionNums[1] && (GadgetCoreAPI.currentVersionNums[2] > targetVersionNums[2] || (GadgetCoreAPI.currentVersionNums[2] == targetVersionNums[2] && GadgetCoreAPI.currentVersionNums[3] >= targetVersionNums[3]))) ||
             (attribute.GadgetCoreVersionSpecificity == VersionSpecificity.NONBREAKING && GadgetCoreAPI.currentVersionNums[0] == targetVersionNums[0] && GadgetCoreAPI.currentVersionNums[1] == targetVersionNums[1] && GadgetCoreAPI.currentVersionNums[2] == targetVersionNums[2] && GadgetCoreAPI.currentVersionNums[3] >= targetVersionNums[3]) ||
             (attribute.GadgetCoreVersionSpecificity == VersionSpecificity.BUGFIX && GadgetCoreAPI.currentVersionNums[0] == targetVersionNums[0] && GadgetCoreAPI.currentVersionNums[1] == targetVersionNums[1] && GadgetCoreAPI.currentVersionNums[2] == targetVersionNums[2] && GadgetCoreAPI.currentVersionNums[3] == targetVersionNums[3]))
         {
             Gadget gadget = null;
             try
             {
                 gadget = Activator.CreateInstance(type) as Gadget;
             }
             catch (Exception e)
             {
                 Logger.LogWarning("Found Gadget that could not be constructed: " + attribute.Name + ", in mod: {" + mod.Name + "}: Error: " + e);
             }
             if (gadget != null)
             {
                 try
                 {
                     Logger.Log("Found Gadget to load: " + attribute.Name + ", in mod: {" + mod.Name + "}");
                     gadget.CreateSingleton(gadget);
                     GadgetInfo info = new GadgetInfo(gadget, attribute, mod);
                     gadget.Logger = new GadgetLogger(mod.Name, attribute.Name);
                     gadget.Config = new GadgetConfig(Path.Combine(GadgetPaths.ConfigsPath, mod.Name + ".ini"), attribute.Name);
                     Gadgets.RegisterGadget(info);
                     mod.m_LoadedGadgets.Add(info);
                     if (!BatchLoading)
                     {
                         QueuedGadgets.Add(info);
                         EnableQueuedGadgets();
                     }
                 }
                 catch (Exception e)
                 {
                     Logger.LogWarning("Found Gadget that had an error during registration: " + attribute.Name + ", in mod: {" + mod.Name + "}. Error: " + e);
                     if (Gadgets.GetGadgetInfo(attribute.Name) != null)
                     {
                         Gadgets.UnregisterGadget(Gadgets.GetGadgetInfo(attribute.Name));
                     }
                 }
             }
         }
         else
         {
             int rD = (int)attribute.GadgetCoreVersionSpecificity;
             Logger.LogWarning("Found Gadget with an incompatible version: " + attribute.Name + ", in mod: {" + mod.Name + "}. Requires version: " + new string(attribute.TargetGCVersion.TakeWhile(x => (x == '.' ? --rD : rD) > 0).ToArray()));
         }
     }
     mod.IsLoaded = true;
 }
Exemple #5
0
        public void UpdateInfo(Toggle toggle, int modIndex, int gadgetIndex = -1)
        {
            try
            {
                if (GadgetModConfigs.IsConfigMenuOpen((this.modIndex << 16) + (this.gadgetIndex & 0xFFFF)))
                {
                    GadgetModConfigs.CloseConfigMenu((this.modIndex << 16) + (this.gadgetIndex & 0xFFFF));
                }
                this.toggle      = toggle;
                this.modIndex    = modIndex;
                this.gadgetIndex = gadgetIndex;

                string[] disabledMods = GadgetCoreConfig.enabledMods.Where(x => !x.Value).Select(x => x.Key).ToArray();

                StringBuilder descTextBuilder = new StringBuilder();
                bool          modEnabled      = modEntries[modIndex].Type == ModMenuEntryType.GADGET || modEntries[modIndex].Type == ModMenuEntryType.UMF;

                if (gadgetIndex >= 0)
                {
                    GadgetInfo mod = modEntries[modIndex].Gadgets[gadgetIndex];
                    enableButton.interactable = modEnabled;
                    reloadButton.interactable = mod.Attribute.AllowRuntimeReloading;
                    configButton.interactable = GadgetModConfigs.GetConfigMenuObject((modIndex << 16) + (gadgetIndex & 0xFFFF)) != null;
                    enableButton.GetComponentInChildren <Text>().text = mod.Gadget.Enabled ? "Disable" : "Enable";
                    descTextBuilder.Append((mod.Gadget.Enabled ? "Gadget Enabled" : "Gadget Disabled") + (modEnabled ? "" : " (Disabled by mod) ") + '\n');
                    Dictionary <string, string> gadgetInfo = new Dictionary <string, string>
                    {
                        ["Name"]                = mod.Attribute.Name,
                        ["Version"]             = mod.Mod.Version.ToString(),
                        ["Required On Clients"] = mod.Attribute.RequiredOnClients ? "Yes" : "No",
                        ["Dependencies"]        = mod.Attribute.Dependencies.Length > 0 ? mod.Attribute.Dependencies.Aggregate(new StringBuilder(), (a, b) => { if (a.Length == 0)
                                                                                                                                                                {
                                                                                                                                                                    a.Append(", ");
                                                                                                                                                                }
                                                                                                                                                                a.Append(b); return(a); }).ToString() : "None"
                    };
                    foreach (KeyValuePair <string, string> info in gadgetInfo)
                    {
                        descTextBuilder.Append('\n');
                        descTextBuilder.Append(info.Key + ": " + info.Value);
                    }
                    string gadgetDesc = mod.Gadget.GetModDescription();
                    descTextBuilder.Append("\n\n" + (!string.IsNullOrEmpty(gadgetDesc) ? gadgetDesc : modEntries[modIndex].Description));
                }
                else
                {
                    enableButton.interactable = modEntries[modIndex].Name != "GadgetCore" && (modEnabled || modEntries[modIndex].Type == ModMenuEntryType.DISABLED_UMF);
                    reloadButton.interactable = modEntries[modIndex].Name != "GadgetCore" && (modEntries[modIndex].Type == ModMenuEntryType.GADGET || modEntries[modIndex].Type == ModMenuEntryType.UMF);
                    configButton.interactable = GadgetModConfigs.GetConfigMenuObject((modIndex << 16) + (gadgetIndex & 0xFFFF)) != null;
                    enableButton.GetComponentInChildren <Text>().text = (modEnabled ^ modsToToggle.Contains(modEntries[modIndex].Name)) ? "Disable" : "Enable";
                    descTextBuilder.Append((modEnabled ? "Mod Enabled" : "Mod Disabled") + (modsToToggle.Contains(modEntries[modIndex].Name) ? (modEnabled ? " (Will disable after restart)" : " (Will enable after restart)") : "") + '\n');
                    foreach (KeyValuePair <string, string> info in modEntries[modIndex].Info)
                    {
                        descTextBuilder.Append('\n');
                        descTextBuilder.Append(info.Key + ": " + info.Value);
                    }
                    descTextBuilder.Append("\n\n" + modEntries[modIndex].Description);
                }
                descText.text = descTextBuilder.ToString();
            }
            catch (Exception e)
            {
                descText.text = "An error occured while populating the info panel for this mod! Check GadgetCore.log for details.";
                if (gadgetIndex >= 0)
                {
                    GadgetCore.CoreLogger.LogError("An error occured while populating the info panel for the gadget with the mod index " + modIndex + " and gadget index " + gadgetIndex + ": " + e.ToString(), false);
                }
                else
                {
                    GadgetCore.CoreLogger.LogError("An error occured while populating the info panel for the mod with the index " + modIndex + ": " + e.ToString(), false);
                }
            }
        }
Exemple #6
0
        internal void Build()
        {
            string modInfo = null;

            foreach (GadgetMod mod in GadgetMods.ListAllMods().Where(x => x.LoadedGadgets.Count > 0))
            {
                if (mod.Name == "GadgetCore")
                {
                    modInfo = CoreMod.GadgetCoreMod.GetDesc();
                }
                else if (mod.HasModFile("ModInfo.txt"))
                {
                    using (GadgetModFile infoFile = mod.GetModFile("ModInfo.txt")) modInfo = infoFile.ReadAllText();
                }
                if (string.IsNullOrEmpty(modInfo) || modInfo == "Insert the description for your mod here!")
                {
                    modInfo = "This Gadget mod does not have a ModInfo file.";
                }
                Dictionary <string, string> info = new Dictionary <string, string>
                {
                    ["Name"]         = mod.Name,
                    ["Version"]      = mod.Version.ToString(),
                    ["File Name"]    = Path.GetFileName(mod.ModPath),
                    ["Dependencies"] = mod.ModDependencies.Count > 0 ? mod.ModDependencies.Concat() : "None",
                    ["Gadgets"]      = mod.LoadedGadgets.Select(x => x.Attribute.Name).Concat()
                };
                if (mod.UnloadedGadgets.Count > 0)
                {
                    info.Add("Unloaded/Errored Gadgets", mod.UnloadedGadgets.Select(x => x.Attribute.Name).Concat());
                }
                using (GadgetModFile infoFile = mod.GetModFile("ModInfo.txt"))
                    modEntries.Add(new ModMenuEntry(mod.Name, ModMenuEntryType.GADGET, modInfo, info, mod.LoadedGadgets.ToArray()));
            }
            foreach (GadgetMod mod in GadgetLoader.EmptyMods)
            {
                if (mod.Name == "GadgetCore")
                {
                    modInfo = CoreMod.GadgetCoreMod.GetDesc();
                }
                else if (mod.HasModFile("ModInfo.txt"))
                {
                    using (GadgetModFile infoFile = mod.GetModFile("ModInfo.txt")) modInfo = infoFile.ReadAllText();
                }
                if (string.IsNullOrEmpty(modInfo) || modInfo == "Insert the description for your mod here!")
                {
                    modInfo = "This Gadget mod does not have a ModInfo file.";
                }
                Dictionary <string, string> info = new Dictionary <string, string>
                {
                    ["Name"]         = mod.Name,
                    ["Version"]      = mod.Version.ToString(),
                    ["File Name"]    = Path.GetFileName(mod.ModPath),
                    ["Dependencies"] = mod.ModDependencies.Count > 0 ? mod.ModDependencies.Concat() : "None",
                };
                if (mod.UnloadedGadgets.Count > 0)
                {
                    info.Add("Unloaded/Errored Gadgets", mod.UnloadedGadgets.Select(x => x.Attribute.Name).Concat());
                }
                modEntries.Add(new ModMenuEntry(mod.Name, ModMenuEntryType.EMPTY_GADGET, modInfo, info));
            }
            foreach (GadgetMod mod in GadgetLoader.IncompatibleMods)
            {
                if (mod.Name == "GadgetCore")
                {
                    modInfo = CoreMod.GadgetCoreMod.GetDesc();
                }
                else if (mod.HasModFile("ModInfo.txt"))
                {
                    using (GadgetModFile infoFile = mod.GetModFile("ModInfo.txt")) modInfo = infoFile.ReadAllText();
                }
                if (string.IsNullOrEmpty(modInfo) || modInfo == "Insert the description for your mod here!")
                {
                    modInfo = "This Gadget mod does not have a ModInfo file.";
                }
                Dictionary <string, string> info = new Dictionary <string, string>
                {
                    ["Name"]         = mod.Name,
                    ["Version"]      = mod.Version.ToString(),
                    ["File Name"]    = Path.GetFileName(mod.ModPath),
                    ["Dependencies"] = mod.ModDependencies.Count > 0 ? mod.ModDependencies.Concat() : "None"
                };
                modEntries.Add(new ModMenuEntry(mod.Name, ModMenuEntryType.INCOMPATIBLE_GADGET, modInfo, info));
            }
            foreach (GadgetMod mod in GadgetMods.ListAllMods().Where(x => x.LoadedGadgets.Count == 0))
            {
                if (mod.Name == "GadgetCore")
                {
                    modInfo = CoreMod.GadgetCoreMod.GetDesc();
                }
                else if (mod.HasModFile("ModInfo.txt"))
                {
                    using (GadgetModFile infoFile = mod.GetModFile("ModInfo.txt")) modInfo = infoFile.ReadAllText();
                }
                if (string.IsNullOrEmpty(modInfo) || modInfo == "Insert the description for your mod here!")
                {
                    modInfo = "This Gadget mod does not have a ModInfo file.";
                }
                Dictionary <string, string> info = new Dictionary <string, string>
                {
                    ["Name"]         = mod.Name,
                    ["Version"]      = mod.Version.ToString(),
                    ["File Name"]    = Path.GetFileName(mod.ModPath),
                    ["Dependencies"] = mod.ModDependencies.Count > 0 ? mod.ModDependencies.Concat() : "None",
                };
                if (mod.UnloadedGadgets.Count > 0)
                {
                    info.Add("Unloaded/Errored Gadgets", mod.UnloadedGadgets.Select(x => x.Attribute.Name).Concat());
                }
                modEntries.Add(new ModMenuEntry(mod.Name, ModMenuEntryType.ERRORED_GADGET, modInfo, info));
            }
            foreach (Tuple <string, string> mod in GadgetLoader.ErroredMods)
            {
                if (mod.Item1 == "GadgetCore")
                {
                    modInfo = CoreMod.GadgetCoreMod.GetDesc();
                }
                modInfo = "As there was an error loading this mod, its description could not be loaded.\nThe error that prevented this mod from loading should be in the log.\nYou should report this to the mod author.";
                Dictionary <string, string> info = new Dictionary <string, string>
                {
                    ["Name"]      = mod.Item1,
                    ["File Name"] = Path.GetFileName(mod.Item2)
                };
                modEntries.Add(new ModMenuEntry(mod.Item1, ModMenuEntryType.ERRORED_GADGET, modInfo, info));
            }
            if (GadgetCoreAPI.GetUMFAPI() != null)
            {
                foreach (string mod in GadgetCoreAPI.GetUMFAPI().GetModNames())
                {
                    try
                    {
                        modInfo = File.ReadAllText(GadgetCoreAPI.GetUMFAPI().GetModInfosPath() + "/" + mod + "_v" + GadgetCoreAPI.GetUMFAPI().GetModVersion(mod) + "_ModInfo.txt");
                    }
                    catch (Exception) { }
                    if (string.IsNullOrEmpty(modInfo) || modInfo == "A UMF Mod(umodframework.com) for Roguelands")
                    {
                        modInfo = "This UMF mod does not have a ModInfo file.";
                    }
                    Dictionary <string, string> info = new Dictionary <string, string>()
                    {
                        ["Name"]        = mod,
                        ["Version"]     = GadgetCoreAPI.GetUMFAPI().GetModVersion(mod).ToString(),
                        ["Description"] = GadgetCoreAPI.GetUMFAPI().GetModDescription(mod),
                    };
                    if (GadgetCoreAPI.GetUMFAPI().GetModNamesEnabled().Contains(mod))
                    {
                        modEntries.Add(new ModMenuEntry(mod, ModMenuEntryType.UMF, modInfo, info));
                    }
                    else
                    {
                        modEntries.Add(new ModMenuEntry(mod, ModMenuEntryType.DISABLED_UMF, modInfo, info));
                    }
                    umfModEntries.Add(modEntries[modEntries.Count - 1]);
                }
                foreach (string mod in GadgetCoreAPI.GetUMFAPI().GetModNamesMissingDependencies())
                {
                    try
                    {
                        modInfo = File.ReadAllText(GadgetCoreAPI.GetUMFAPI().GetModInfosPath() + "/" + mod + "_v" + GadgetCoreAPI.GetUMFAPI().GetModVersion(mod) + "_ModInfo.txt");
                    }
                    catch (Exception) { }
                    if (string.IsNullOrEmpty(modInfo) || modInfo == "A UMF Mod(umodframework.com) for Roguelands")
                    {
                        modInfo = "This UMF mod does not have a ModInfo file.";
                    }
                    Dictionary <string, string> info = new Dictionary <string, string>()
                    {
                        ["Name"]        = mod,
                        ["Version"]     = GadgetCoreAPI.GetUMFAPI().GetModVersion(mod).ToString(),
                        ["Description"] = GadgetCoreAPI.GetUMFAPI().GetModDescription(mod),
                    };
                    modEntries.Add(new ModMenuEntry(mod, ModMenuEntryType.INCOMPATIBLE_UMF, modInfo, info));
                }
            }

            int modCount = modEntries.Count + modEntries.Where(x => x.Type == ModMenuEntryType.GADGET).SelectMany(x => x.Gadgets).Count();

            ScrollRect modListScrollView = new GameObject("Scroll View", typeof(RectTransform), typeof(ScrollRect), typeof(CanvasRenderer), typeof(Image)).GetComponent <ScrollRect>();

            modListScrollView.GetComponent <RectTransform>().SetParent(transform);
            modListScrollView.GetComponent <RectTransform>().anchorMin = new Vector2(0f, 0f);
            modListScrollView.GetComponent <RectTransform>().anchorMax = new Vector2(0.3f, 1f);
            modListScrollView.GetComponent <RectTransform>().offsetMin = new Vector2(10, 10);
            modListScrollView.GetComponent <RectTransform>().offsetMax = new Vector2(0, -10);
            modListScrollView.GetComponent <Image>().sprite            = SceneInjector.BoxSprite;
            modListScrollView.GetComponent <Image>().type       = Image.Type.Sliced;
            modListScrollView.GetComponent <Image>().fillCenter = true;
            Mask modListScrollViewMask = new GameObject("Mask", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Mask)).GetComponent <Mask>();

            modListScrollViewMask.GetComponent <RectTransform>().SetParent(modListScrollView.transform);
            modListScrollViewMask.GetComponent <RectTransform>().anchorMin = new Vector2(0f, 0f);
            modListScrollViewMask.GetComponent <RectTransform>().anchorMax = new Vector2(1f, 1f);
            modListScrollViewMask.GetComponent <RectTransform>().offsetMin = Vector2.zero;
            modListScrollViewMask.GetComponent <RectTransform>().offsetMax = Vector2.zero;
            modListScrollViewMask.GetComponent <Image>().sprite            = SceneInjector.BoxMask;
            modListScrollViewMask.GetComponent <Image>().type       = Image.Type.Sliced;
            modListScrollViewMask.GetComponent <Image>().fillCenter = true;
            modListScrollViewMask.showMaskGraphic = false;
            RectTransform modListViewport = new GameObject("Viewport", typeof(RectTransform)).GetComponent <RectTransform>();

            modListViewport.SetParent(modListScrollViewMask.transform);
            modListViewport.anchorMin = new Vector2(0f, 0f);
            modListViewport.anchorMax = new Vector2(1f, 1f);
            modListViewport.offsetMin = new Vector2(10, 10);
            modListViewport.offsetMax = new Vector2(-10, -10);
            RectTransform modList = new GameObject("ModList", typeof(RectTransform), typeof(ToggleGroup)).GetComponent <RectTransform>();

            modList.SetParent(modListViewport);
            modList.anchorMin = new Vector2(0f, modCount <= 6 ? 0f : (1f - (modCount / 6f)));
            modList.anchorMax = new Vector2(1f, 1f);
            modList.offsetMin = Vector2.zero;
            modList.offsetMax = Vector2.zero;
            Scrollbar modListScrollBar = new GameObject("Scrollbar", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Scrollbar)).GetComponent <Scrollbar>();

            modListScrollBar.GetComponent <RectTransform>().SetParent(modListScrollView.transform);
            modListScrollBar.GetComponent <RectTransform>().anchorMin = new Vector2(1f, 0f);
            modListScrollBar.GetComponent <RectTransform>().anchorMax = new Vector2(1.25f, 1f);
            modListScrollBar.GetComponent <RectTransform>().offsetMin = Vector2.zero;
            modListScrollBar.GetComponent <RectTransform>().offsetMax = Vector2.zero;
            modListScrollBar.GetComponent <Image>().sprite            = SceneInjector.BoxSprite;
            modListScrollBar.GetComponent <Image>().type       = Image.Type.Sliced;
            modListScrollBar.GetComponent <Image>().fillCenter = true;
            RectTransform modListScrollBarHandle = new GameObject("Handle", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image)).GetComponent <RectTransform>();

            modListScrollBarHandle.SetParent(modListScrollBar.transform);
            modListScrollBarHandle.anchorMin = new Vector2(0.05f, 0.05f);
            modListScrollBarHandle.anchorMax = new Vector2(0.95f, 0.95f);
            modListScrollBarHandle.offsetMin = Vector2.zero;
            modListScrollBarHandle.offsetMax = Vector2.zero;
            modListScrollBarHandle.GetComponent <Image>().sprite     = SceneInjector.BoxSprite;
            modListScrollBarHandle.GetComponent <Image>().type       = Image.Type.Sliced;
            modListScrollBarHandle.GetComponent <Image>().fillCenter = true;
            modListScrollBar.targetGraphic = modListScrollBarHandle.GetComponent <Image>();
            modListScrollBar.handleRect    = modListScrollBarHandle;
            modListScrollBar.direction     = Scrollbar.Direction.BottomToTop;
            if (modCount <= 5)
            {
                modListScrollBar.interactable = false;
            }
            modListScrollView.content           = modList;
            modListScrollView.horizontal        = false;
            modListScrollView.scrollSensitivity = 5;
            modListScrollView.movementType      = ScrollRect.MovementType.Clamped;
            modListScrollView.viewport          = modListViewport;
            modListScrollView.verticalScrollbar = modListScrollBar;
            float  entryHeight    = modCount <= 6 ? (1f / 6f) : (1f / modCount);
            Toggle selectedToggle = null;
            int    bonusOffset    = 0;

            for (int i = 0; i < modEntries.Count; i++)
            {
                ModMenuEntry modEntry = modEntries[i];
                if (modEntry.Gadgets.Length > 0)
                {
                    int           gadgetCount  = modEntry.Gadgets.Count();
                    float         gadgetHeight = 1f / (gadgetCount + 1);
                    RectTransform modEntryRect = new GameObject("Gadget-Containing Mod Entry: " + modEntry.Name, typeof(RectTransform), typeof(Toggle), typeof(CanvasRenderer), typeof(Image), typeof(Mask)).GetComponent <RectTransform>();
                    modEntryRect.SetParent(modList);
                    modEntryRect.anchorMin = new Vector2(0f, 1 - ((i + bonusOffset + 1 + gadgetCount) * entryHeight));
                    modEntryRect.anchorMax = new Vector2(1f, 1 - ((i + bonusOffset) * entryHeight));
                    modEntryRect.offsetMin = Vector2.zero;
                    modEntryRect.offsetMax = Vector2.zero;
                    Toggle modToggle   = modEntryRect.GetComponent <Toggle>();
                    Image  modSelected = new GameObject("Selected", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image)).GetComponent <Image>();
                    modSelected.rectTransform.SetParent(modEntryRect);
                    modSelected.rectTransform.anchorMin = new Vector2(0f, 0f);
                    modSelected.rectTransform.anchorMax = new Vector2(1f, 1f);
                    modSelected.rectTransform.offsetMin = Vector2.zero;
                    modSelected.rectTransform.offsetMax = Vector2.zero;
                    modSelected.sprite = SceneInjector.BoxSprite;
                    modSelected.type   = Image.Type.Sliced;
                    Text modLabel = new GameObject("Label", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text)).GetComponent <Text>();
                    modLabel.rectTransform.SetParent(modEntryRect);
                    modLabel.rectTransform.anchorMin = new Vector2(0f, 1f - gadgetHeight);
                    modLabel.rectTransform.anchorMax = new Vector2(1f, 1f);
                    modLabel.rectTransform.offsetMin = new Vector2(10, 10);
                    modLabel.rectTransform.offsetMax = new Vector2(-10, -10);
                    modLabel.font                           = SceneInjector.ModConfigMenuText.GetComponent <TextMesh>().font;
                    modLabel.fontSize                       = 12;
                    modLabel.horizontalOverflow             = HorizontalWrapMode.Overflow;
                    modLabel.verticalOverflow               = VerticalWrapMode.Overflow;
                    modLabel.alignment                      = TextAnchor.MiddleLeft;
                    modToggle.GetComponent <Image>().sprite = SceneInjector.BoxSprite;
                    modToggle.GetComponent <Image>().type   = Image.Type.Sliced;
                    modToggle.transition                    = Selectable.Transition.None;
                    modToggle.isOn                          = i == modIndex && gadgetIndex == -1;
                    modToggle.toggleTransition              = Toggle.ToggleTransition.None;
                    modToggle.graphic                       = modSelected;
                    modToggle.group                         = modList.GetComponent <ToggleGroup>();
                    int toggleIndex = i;
                    if (i == modIndex)
                    {
                        selectedToggle = modToggle;
                    }
                    modToggle.onValueChanged.AddListener((toggled) => { if (toggled)
                                                                        {
                                                                            UpdateInfo(modToggle, toggleIndex); GadgetCoreAPI.CloseDialog();
                                                                        }
                                                         });

                    switch (modEntry.Type)
                    {
                    case ModMenuEntryType.GADGET:
                        modSelected.color = new Color(1f, 1f, 0.5f, 1f);
                        modToggle.GetComponent <Image>().color = new Color(1f, 1f, 0.5f, 0.25f);
                        modLabel.color = new Color(1f, 1f, 1f, 1f);
                        modLabel.text  = modEntry.Name + "\nGadget Mod";
                        break;

                    case ModMenuEntryType.EMPTY_GADGET:
                        modSelected.color = new Color(0.5f, 0.5f, 0.125f, 1f);
                        modToggle.GetComponent <Image>().color = new Color(0.5f, 0.5f, 0.125f, 0.25f);
                        modLabel.color = new Color(1f, 1f, 1f, 0.5f);
                        modLabel.text  = modEntry.Name + "\nEmpty Gadget Mod";
                        break;

                    case ModMenuEntryType.INCOMPATIBLE_GADGET:
                        modSelected.color = new Color(1f, 0.5f, 0f, 1f);
                        modToggle.GetComponent <Image>().color = new Color(1f, 0.5f, 0f, 0.25f);
                        modLabel.color = new Color(1f, 1f, 1f, 0.5f);
                        modLabel.text  = modEntry.Name + "\nIncompatible Gadget Mod";
                        break;

                    case ModMenuEntryType.ERRORED_GADGET:
                        modSelected.color = new Color(1f, 0f, 0f, 1f);
                        modToggle.GetComponent <Image>().color = new Color(1f, 0f, 0f, 0.25f);
                        modLabel.color = new Color(1f, 1f, 1f, 0.5f);
                        modLabel.text  = modEntry.Name + "\nErrored Gadget Mod";
                        break;

                    case ModMenuEntryType.UMF:
                        modSelected.color = new Color(139 / 255f, 69 / 255f, 19 / 255f, 1f);
                        modToggle.GetComponent <Image>().color = new Color(139 / 255f, 69 / 255f, 19 / 255f, 0.25f);
                        modLabel.color = new Color(139 / 255f, 69 / 255f, 19 / 255f, 1f);
                        modLabel.text  = modEntry.Name + "\nUMF Mod";
                        break;

                    case ModMenuEntryType.DISABLED_UMF:
                        modSelected.color = new Color(69 / 255f, 34 / 255f, 9 / 255f, 1f);
                        modToggle.GetComponent <Image>().color = new Color(69 / 255f, 34 / 255f, 9 / 255f, 0.25f);
                        modLabel.color = new Color(139 / 255f, 69 / 255f, 19 / 255f, 0.5f);
                        modLabel.text  = modEntry.Name + "\nDisabled UMF Mod";
                        break;

                    case ModMenuEntryType.INCOMPATIBLE_UMF:
                        modSelected.color = new Color(210 / 255f, 105 / 255f, 30 / 255f, 1f);
                        modToggle.GetComponent <Image>().color = new Color(210 / 255f, 105 / 255f, 30 / 255f, 0.25f);
                        modLabel.color = new Color(139 / 255f, 69 / 255f, 19 / 255f, 0.5f);
                        modLabel.text  = modEntry.Name + "\nIncompatible UMF Mod";
                        break;
                    }

                    for (int g = 0; g < gadgetCount; g++)
                    {
                        GadgetInfo    gadget     = modEntry.Gadgets[g];
                        RectTransform gadgetRect = new GameObject("Gadget: " + gadget.Attribute.Name, typeof(RectTransform), typeof(Toggle), typeof(CanvasRenderer), typeof(Image), typeof(Mask)).GetComponent <RectTransform>();
                        gadgetRect.SetParent(modEntryRect);
                        gadgetRect.anchorMin = new Vector2(0f, 1 - ((g + 2) * gadgetHeight));
                        gadgetRect.anchorMax = new Vector2(1f, 1 - ((g + 1) * gadgetHeight));
                        gadgetRect.offsetMin = new Vector2(10f, 10f);
                        gadgetRect.offsetMax = new Vector2(-10f, 10f);
                        Toggle gadgetToggle   = gadgetRect.GetComponent <Toggle>();
                        Image  gadgetSelected = new GameObject("Selected", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image)).GetComponent <Image>();
                        gadgetSelected.rectTransform.SetParent(gadgetRect);
                        gadgetSelected.rectTransform.anchorMin = new Vector2(0f, 0f);
                        gadgetSelected.rectTransform.anchorMax = new Vector2(1f, 1f);
                        gadgetSelected.rectTransform.offsetMin = Vector2.zero;
                        gadgetSelected.rectTransform.offsetMax = Vector2.zero;
                        gadgetSelected.sprite = SceneInjector.BoxSprite;
                        gadgetSelected.type   = Image.Type.Sliced;
                        gadgetSelected.color  = new Color(1f, 1f, 0.5f, 1f);
                        Text gadgetLabel = new GameObject("Label", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text)).GetComponent <Text>();
                        gadgetLabel.rectTransform.SetParent(gadgetRect);
                        gadgetLabel.rectTransform.anchorMin = new Vector2(0f, 0f);
                        gadgetLabel.rectTransform.anchorMax = new Vector2(1f, 1f);
                        gadgetLabel.rectTransform.offsetMin = new Vector2(10, 10);
                        gadgetLabel.rectTransform.offsetMax = new Vector2(-10, -10);
                        gadgetLabel.font                           = SceneInjector.ModConfigMenuText.GetComponent <TextMesh>().font;
                        gadgetLabel.fontSize                       = 12;
                        gadgetLabel.horizontalOverflow             = HorizontalWrapMode.Overflow;
                        gadgetLabel.verticalOverflow               = VerticalWrapMode.Overflow;
                        gadgetLabel.alignment                      = TextAnchor.MiddleLeft;
                        gadgetLabel.text                           = gadget.Attribute.Name + "\n" + (gadget.Gadget.Enabled ? "Enabled" : "Disabled");
                        gadgetLabel.color                          = gadget.Gadget.Enabled ? new Color(1f, 1f, 1f, 1f) : new Color(1f, 1f, 1f, 0.5f);
                        gadgetToggle.GetComponent <Image>().sprite = SceneInjector.BoxSprite;
                        gadgetToggle.GetComponent <Image>().type   = Image.Type.Sliced;
                        gadgetToggle.transition                    = Selectable.Transition.None;
                        gadgetToggle.isOn                          = i == modIndex && g == this.gadgetIndex;
                        gadgetToggle.toggleTransition              = Toggle.ToggleTransition.None;
                        gadgetToggle.graphic                       = gadgetSelected;
                        gadgetToggle.group                         = modList.GetComponent <ToggleGroup>();
                        gadgetToggle.GetComponent <Image>().color  = new Color(1f, 1f, 0.5f, 0.25f);
                        int gadgetIndex = g;
                        gadgetToggle.onValueChanged.AddListener((toggled) => { if (toggled)
                                                                               {
                                                                                   UpdateInfo(modToggle, toggleIndex, gadgetIndex); GadgetCoreAPI.CloseDialog();
                                                                               }
                                                                });
                        bonusOffset++;
                    }
                }
                else
                {
                    RectTransform modEntryRect = new GameObject("Mod Entry: " + modEntry.Name, typeof(RectTransform), typeof(Toggle), typeof(CanvasRenderer), typeof(Image), typeof(Mask)).GetComponent <RectTransform>();
                    modEntryRect.SetParent(modList);
                    modEntryRect.anchorMin = new Vector2(0f, 1 - ((i + bonusOffset + 1) * entryHeight));
                    modEntryRect.anchorMax = new Vector2(1f, 1 - ((i + bonusOffset) * entryHeight));
                    modEntryRect.offsetMin = Vector2.zero;
                    modEntryRect.offsetMax = Vector2.zero;
                    Toggle modToggle   = modEntryRect.GetComponent <Toggle>();
                    Image  modSelected = new GameObject("Selected", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image)).GetComponent <Image>();
                    modSelected.rectTransform.SetParent(modEntryRect);
                    modSelected.rectTransform.anchorMin = new Vector2(0f, 0f);
                    modSelected.rectTransform.anchorMax = new Vector2(1f, 1f);
                    modSelected.rectTransform.offsetMin = Vector2.zero;
                    modSelected.rectTransform.offsetMax = Vector2.zero;
                    modSelected.sprite = SceneInjector.BoxSprite;
                    modSelected.type   = Image.Type.Sliced;
                    Text modLabel = new GameObject("Label", typeof(RectTransform), typeof(CanvasRenderer), typeof(Text)).GetComponent <Text>();
                    modLabel.rectTransform.SetParent(modEntryRect);
                    modLabel.rectTransform.anchorMin = new Vector2(0f, 0f);
                    modLabel.rectTransform.anchorMax = new Vector2(1f, 1f);
                    modLabel.rectTransform.offsetMin = new Vector2(10, 10);
                    modLabel.rectTransform.offsetMax = new Vector2(-10, -10);
                    modLabel.font                           = SceneInjector.ModConfigMenuText.GetComponent <TextMesh>().font;
                    modLabel.fontSize                       = 12;
                    modLabel.horizontalOverflow             = HorizontalWrapMode.Overflow;
                    modLabel.verticalOverflow               = VerticalWrapMode.Overflow;
                    modLabel.alignment                      = TextAnchor.MiddleLeft;
                    modToggle.GetComponent <Image>().sprite = SceneInjector.BoxSprite;
                    modToggle.GetComponent <Image>().type   = Image.Type.Sliced;
                    modToggle.transition                    = Selectable.Transition.None;
                    modToggle.isOn                          = i == modIndex;
                    modToggle.toggleTransition              = Toggle.ToggleTransition.None;
                    modToggle.graphic                       = modSelected;
                    modToggle.group                         = modList.GetComponent <ToggleGroup>();
                    int toggleIndex = i;
                    if (i == modIndex)
                    {
                        selectedToggle = modToggle;
                    }
                    modToggle.onValueChanged.AddListener((toggled) => { if (toggled)
                                                                        {
                                                                            UpdateInfo(modToggle, toggleIndex); GadgetCoreAPI.CloseDialog();
                                                                        }
                                                         });

                    switch (modEntry.Type)
                    {
                    case ModMenuEntryType.EMPTY_GADGET:
                        modSelected.color = new Color(0.5f, 0.5f, 0.125f, 1f);
                        modToggle.GetComponent <Image>().color = new Color(0.5f, 0.5f, 0.125f, 0.25f);
                        modLabel.color = new Color(1f, 1f, 1f, 0.5f);
                        modLabel.text  = modEntry.Name + "\nEmpty Gadget Mod";
                        break;

                    case ModMenuEntryType.INCOMPATIBLE_GADGET:
                        modSelected.color = new Color(1f, 0.5f, 0f, 1f);
                        modToggle.GetComponent <Image>().color = new Color(1f, 0.5f, 0f, 0.25f);
                        modLabel.color = new Color(1f, 1f, 1f, 0.5f);
                        modLabel.text  = modEntry.Name + "\nIncompatible Gadget Mod";
                        break;

                    case ModMenuEntryType.ERRORED_GADGET:
                        modSelected.color = new Color(1f, 0f, 0f, 1f);
                        modToggle.GetComponent <Image>().color = new Color(1f, 0f, 0f, 0.25f);
                        modLabel.color = new Color(1f, 1f, 1f, 0.5f);
                        modLabel.text  = modEntry.Name + "\nErrored Gadget Mod";
                        break;

                    case ModMenuEntryType.UMF:
                        modSelected.color = new Color(139 / 255f, 69 / 255f, 19 / 255f, 1f);
                        modToggle.GetComponent <Image>().color = new Color(139 / 255f, 69 / 255f, 19 / 255f, 0.25f);
                        modLabel.color = new Color(139 / 255f, 69 / 255f, 19 / 255f, 1f);
                        modLabel.text  = modEntry.Name + "\nUMF Mod";
                        break;

                    case ModMenuEntryType.DISABLED_UMF:
                        modSelected.color = new Color(69 / 255f, 34 / 255f, 9 / 255f, 1f);
                        modToggle.GetComponent <Image>().color = new Color(69 / 255f, 34 / 255f, 9 / 255f, 0.25f);
                        modLabel.color = new Color(139 / 255f, 69 / 255f, 19 / 255f, 0.5f);
                        modLabel.text  = modEntry.Name + "\nDisabled UMF Mod";
                        break;

                    case ModMenuEntryType.INCOMPATIBLE_UMF:
                        modSelected.color = new Color(210 / 255f, 105 / 255f, 30 / 255f, 1f);
                        modToggle.GetComponent <Image>().color = new Color(210 / 255f, 105 / 255f, 30 / 255f, 0.25f);
                        modLabel.color = new Color(139 / 255f, 69 / 255f, 19 / 255f, 0.5f);
                        modLabel.text  = modEntry.Name + "\nIncompatible UMF Mod";
                        break;
                    }
                }
            }

            modListScrollView.GetComponent <RectTransform>().localScale = Vector3.one;
            if (scrollPositionCache >= 0)
            {
                modListScrollView.verticalNormalizedPosition = 1 - (scrollPositionCache / (modList.anchorMax.y - modList.anchorMin.y));
            }
            scrollPositionCache = -1;

            if (GadgetModConfigs.GetConfigMenuObject(0) != null)
            {
                GadgetModConfigs.ResetAllConfigMenus();
            }
            else
            {
                GadgetModConfigs.BuildConfigMenus(SceneInjector.ModConfigMenus);
            }

            if (modCount > 0)
            {
                UpdateInfo(selectedToggle, modIndex, gadgetIndex);
            }
        }
        internal static void BuildConfigMenus(RectTransform parent)
        {
            ConfigMenus.Clear();

            if (GadgetCoreAPI.GetUMFAPI() != null)
            {
                if (UMFConfigMenu == null || UMFConfigMenu.Item2 == null)
                {
                    UMFConfigMenu umfConfigMenu = new UMFConfigMenu();
                    RectTransform menuParent    = new GameObject("uModFramework", typeof(RectTransform)).GetComponent <RectTransform>();
                    menuParent.gameObject.SetActive(false);
                    menuParent.SetParent(parent);
                    menuParent.anchorMin = new Vector2(0f, 0f);
                    menuParent.anchorMax = new Vector2(1f, 1f);
                    menuParent.offsetMin = Vector2.zero;
                    menuParent.offsetMax = Vector2.zero;
                    umfConfigMenu.Build(menuParent);
                    UMFConfigMenu = Tuple.Create <IGadgetConfigMenu, RectTransform>(umfConfigMenu, menuParent);
                }
                else
                {
                    UMFConfigMenu.Item1.Reset();
                }
            }

            for (int i = 0; i < ModMenuController.modEntries.Count; i++)
            {
                ModMenuEntry entry = ModMenuController.modEntries[i];
                if (entry.Type == ModMenuEntryType.GADGET || entry.Type == ModMenuEntryType.UMF || entry.Type == ModMenuEntryType.DISABLED_UMF)
                {
                    try
                    {
                        GadgetInfo        gadgetForConfig = entry.Gadgets.FirstOrDefault(x => x.Attribute.Name == entry.Name);
                        IGadgetConfigMenu configMenu      = gadgetForConfig?.Gadget.GetConfigMenu();
                        if (configMenu != null)
                        {
                            RectTransform menuParent = new GameObject(entry.Name, typeof(RectTransform)).GetComponent <RectTransform>();
                            menuParent.gameObject.SetActive(false);
                            menuParent.SetParent(parent);
                            menuParent.anchorMin = new Vector2(0f, 0f);
                            menuParent.anchorMax = new Vector2(1f, 1f);
                            menuParent.offsetMin = Vector2.zero;
                            menuParent.offsetMax = Vector2.zero;
                            configMenu.Build(menuParent);
                            ConfigMenus.Add((i << 16) + 0xFFFF, Tuple.Create(configMenu, menuParent));
                        }
                        else
                        {
                            ConfigMenus.Add((i << 16) + 0xFFFF, null);
                        }
                    }
                    catch (InvalidOperationException e)
                    {
                        if (e.Message == INIGadgetConfigMenu.NO_CONFIGURABLE_DATA)
                        {
                            ConfigMenus.Add((i << 16) + 0xFFFF, null);
                        }
                        else
                        {
                            throw;
                        }
                    }

                    for (int g = 0; g < entry.Gadgets.Length; g++)
                    {
                        GadgetInfo gadget = entry.Gadgets[g];
                        try
                        {
                            IGadgetConfigMenu configMenu = gadget?.Gadget.GetConfigMenu();
                            if (configMenu != null)
                            {
                                RectTransform menuParent = new GameObject(gadget.Attribute.Name, typeof(RectTransform)).GetComponent <RectTransform>();
                                menuParent.gameObject.SetActive(false);
                                menuParent.SetParent(parent);
                                menuParent.anchorMin = new Vector2(0f, 0f);
                                menuParent.anchorMax = new Vector2(1f, 1f);
                                menuParent.offsetMin = Vector2.zero;
                                menuParent.offsetMax = Vector2.zero;
                                configMenu.Build(menuParent);
                                ConfigMenus.Add((i << 16) + g, Tuple.Create(configMenu, menuParent));
                            }
                            else
                            {
                                ConfigMenus.Add((i << 16) + g, null);
                            }
                        }
                        catch (InvalidOperationException e)
                        {
                            if (e.Message == INIGadgetConfigMenu.NO_CONFIGURABLE_DATA)
                            {
                                ConfigMenus.Add((i << 16) + g, null);
                            }
                            else
                            {
                                throw;
                            }
                        }
                    }
                }
            }
        }
        internal static void EnableQueuedGadgets()
        {
            List <GadgetInfo> preFilteredQueuedGadgets = QueuedGadgets;

            QueuedGadgets = new List <GadgetInfo>();
            foreach (GadgetInfo gadget in preFilteredQueuedGadgets)
            {
                bool valid = true;
                foreach (string dependency in gadget.Attribute.Dependencies)
                {
                    string[]   splitDependency  = dependency.Split(':');
                    GadgetInfo dependencyGadget = Gadgets.GetGadgetInfo(splitDependency[0]);
                    valid = dependencyGadget != null && dependencyGadget.Gadget.Enabled;
                    if (valid)
                    {
                        if (splitDependency.Length == 2)
                        {
                            string versionString     = splitDependency[1].TrimStart('v');
                            int[]  targetVersionNums = versionString.Split('.').Select(x => int.Parse(x)).ToArray();
                            if (targetVersionNums.Length > 4)
                            {
                                continue;
                            }
                            int[] actualVersionNums = dependencyGadget.Mod.Version.ToString().Split('.').Select(x => int.Parse(x)).ToArray();
                            if (targetVersionNums.Length != 4)
                            {
                                Array.Resize(ref targetVersionNums, 4);
                            }
                            if (actualVersionNums.Length != 4)
                            {
                                Array.Resize(ref actualVersionNums, 4);
                            }
                            valid = actualVersionNums[0] == targetVersionNums[0] && actualVersionNums[1] == targetVersionNums[1] && (actualVersionNums[2] > targetVersionNums[2] || (actualVersionNums[2] == targetVersionNums[2] && actualVersionNums[3] >= targetVersionNums[3]));
                        }
                        else if (splitDependency.Length == 3)
                        {
                            string versionString     = splitDependency[1].TrimStart('v');
                            int[]  targetVersionNums = versionString.Split('.').Select(x => int.Parse(x)).ToArray();
                            if (targetVersionNums.Length > 4)
                            {
                                continue;
                            }
                            int[] actualVersionNums = dependencyGadget.Mod.Version.ToString().Split('.').Select(x => int.Parse(x)).ToArray();
                            VersionSpecificity versionSpecificity;
                            try
                            {
                                versionSpecificity = (VersionSpecificity)Enum.Parse(typeof(VersionSpecificity), splitDependency[2], true);
                            }
                            catch (ArgumentException)
                            {
                                Logger.LogWarning("Gadget " + gadget.Attribute.Name + " has an improperly-formatted dependency version string: " + dependency);
                                versionSpecificity = VersionSpecificity.MINOR;
                            }
                            if (targetVersionNums.Length != 4)
                            {
                                Array.Resize(ref targetVersionNums, 4);
                            }
                            if (actualVersionNums.Length != 4)
                            {
                                Array.Resize(ref actualVersionNums, 4);
                            }
                            valid = (versionSpecificity == VersionSpecificity.MAJOR && actualVersionNums[0] == targetVersionNums[0] && (actualVersionNums[1] > targetVersionNums[1] || (actualVersionNums[1] == targetVersionNums[1] && (actualVersionNums[2] > targetVersionNums[2] || (actualVersionNums[2] == targetVersionNums[2] && actualVersionNums[3] >= targetVersionNums[3]))))) ||
                                    (versionSpecificity == VersionSpecificity.MINOR && actualVersionNums[0] == targetVersionNums[0] && actualVersionNums[1] == targetVersionNums[1] && (actualVersionNums[2] > targetVersionNums[2] || (actualVersionNums[2] == targetVersionNums[2] && actualVersionNums[3] >= targetVersionNums[3]))) ||
                                    (versionSpecificity == VersionSpecificity.NONBREAKING && actualVersionNums[0] == targetVersionNums[0] && actualVersionNums[1] == targetVersionNums[1] && actualVersionNums[2] == targetVersionNums[2] && actualVersionNums[3] >= targetVersionNums[3]) ||
                                    (versionSpecificity == VersionSpecificity.BUGFIX && actualVersionNums[0] == targetVersionNums[0] && actualVersionNums[1] == targetVersionNums[1] && actualVersionNums[2] == targetVersionNums[2] && actualVersionNums[3] == targetVersionNums[3]);
                        }
                    }
                }
                if (valid)
                {
                    QueuedGadgets.Add(gadget);
                }
                else
                {
                    gadget.Gadget.Enabled = false;
                    GadgetCoreConfig.enabledGadgets[gadget.Attribute.Name] = false;
                    Gadgets.UnregisterGadget(gadget);
                    Logger.LogWarning("Aborted loading Gadget " + gadget.Attribute.Name + " due to missing dependencies.");
                }
            }
            Logger.Log("Loading Gadget configs...");
            foreach (GadgetInfo gadget in QueuedGadgets.ToList())
            {
                Logger.Log("Loading Config for Gadget '" + gadget.Attribute.Name + "'");
                try
                {
                    gadget.Gadget.LoadConfig();
                }
                catch (Exception e)
                {
                    gadget.Gadget.Enabled = false;
                    GadgetCoreConfig.enabledGadgets[gadget.Attribute.Name] = false;
                    Gadgets.UnregisterGadget(gadget);
                    QueuedGadgets.Remove(gadget);
                    Logger.LogError("Exception Loading Config For Gadget '" + gadget.Attribute.Name + "':" + Environment.NewLine + e.ToString());
                }
            }
            Logger.Log("Done loading Gadget configs.");
            Logger.Log("Preparing Gadgets for patching...");
            foreach (GadgetInfo gadget in QueuedGadgets.ToList())
            {
                Logger.Log("PrePatching Gadget '" + gadget.Attribute.Name + "'");
                try
                {
                    gadget.Gadget.HarmonyInstance = new Harmony(gadget.Mod.Name + "." + gadget.Attribute.Name + ".gadget");
                    gadget.Gadget.PrePatch();
                }
                catch (Exception e)
                {
                    gadget.Gadget.Enabled = false;
                    GadgetCoreConfig.enabledGadgets[gadget.Attribute.Name] = false;
                    Gadgets.UnregisterGadget(gadget);
                    QueuedGadgets.Remove(gadget);
                    Logger.LogError("Exception PrePatching Gadget '" + gadget.Attribute.Name + "':" + Environment.NewLine + e.ToString());
                }
            }
            Logger.Log("Done preparing Gadgets for patching.");
            Logger.Log("Patching Gadgets...");
            foreach (GadgetInfo gadget in QueuedGadgets.ToList())
            {
                int patches = 0, totalMethods = 0, errorPatching = 0, targetMissing = 0;
                try
                {
                    gadget.Mod.Assembly.GetExportedTypes().Do(delegate(Type type)
                    {
                        HarmonyGadgetAttribute attribute = type.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(HarmonyGadgetAttribute)) as HarmonyGadgetAttribute;
                        if (attribute?.Gadget == gadget.Attribute.Name && attribute.RequiredGadgets.All(x => Gadgets.GetGadget(x) != null))
                        {
                            try
                            {
                                List <MethodInfo> methods = gadget.Gadget.HarmonyInstance.CreateClassProcessor(type).Patch();
                                totalMethods += methods?.Count ?? 0;
                                if (methods == null || methods.Count == 0)
                                {
                                    Logger.Log("Skipping patch '" + type.Name + "' for Gadget '" + gadget.Attribute.Name + "': Attribute target does not exist.");
                                    targetMissing++;
                                }
                                else
                                {
                                    patches++;
                                }
                            }
                            catch (Exception e)
                            {
                                if (e.InnerException == null || !e.InnerException.Message.EndsWith("returned an unexpected result: null"))
                                {
                                    Logger.LogError("Exception running patch '" + type.Name + "' for Gadget '" + gadget.Attribute.Name + "': " + Environment.NewLine + e.ToString());
                                    errorPatching++;
                                }
                                else
                                {
                                    Logger.Log("Skipping patch '" + type.Name + "' for Gadget '" + gadget.Attribute.Name + "': TargetMethod returned null.");
                                    targetMissing++;
                                }
                            }
                        }
                    });
                    Logger.Log("Performed " + patches + " patches for '" + gadget.Attribute.Name + "'" + (totalMethods > 0 ? $" ({totalMethods} total methods patched)" : string.Empty) + (errorPatching > 0 ? targetMissing > 0 ? $" ({errorPatching} skipped due to errors, {targetMissing} skipped due to missing targets)" : $" ({errorPatching} skipped due to errors)" : targetMissing > 0 ? $" ({targetMissing} skipped due to missing targets)" : string.Empty));
                }
                catch (Exception e)
                {
                    gadget.Gadget.Enabled = false;
                    GadgetCoreConfig.enabledGadgets[gadget.Attribute.Name] = false;
                    Gadgets.UnregisterGadget(gadget);
                    QueuedGadgets.Remove(gadget);
                    Logger.LogError("Exception patching Gadget '" + gadget.Attribute.Name + "':" + Environment.NewLine + e.ToString());
                }
            }
            Logger.Log("Done patching Gadgets.");
            bool logOverrideWarnings = false;

            foreach (MethodBase patchedMethod in Harmony.GetAllPatchedMethods())
            {
                HarmonyLib.Patches patches = Harmony.GetPatchInfo(patchedMethod);
                if (patches == null)
                {
                    continue;
                }
                Dictionary <string, string> owners = new Dictionary <string, string>();
                List <Patch> overridingPrefixes    = patches.Prefixes.Where(x => x.PatchMethod.ReturnType == typeof(bool) && (x.PatchMethod.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(HarmonyOverridesAttribute)) as HarmonyOverridesAttribute)?.Overrides.Length != 0).ToList();
                if (overridingPrefixes != null && overridingPrefixes.Count > 0)
                {
                    foreach (string ownerID in patches.Owners)
                    {
                        if (ownerID == GadgetCore.HarmonyInstance.Id)
                        {
                            owners[ownerID] = "GadgetCore";
                        }
                        else
                        {
                            string[] splitOwnerID = ownerID.Split('.');
                            if (splitOwnerID.Length == 3 && splitOwnerID[2] == "gadget")
                            {
                                GadgetMod  mod    = GadgetMods.GetModByName(splitOwnerID[0]);
                                GadgetInfo gadget = mod?.LoadedGadgets.SingleOrDefault(x => x.Attribute.Name == splitOwnerID[1]);
                                if (gadget != null)
                                {
                                    owners[ownerID] = $"'{splitOwnerID[1]}' from the mod {{{splitOwnerID[0]}}}";
                                }
                                else
                                {
                                    owners[ownerID] = $"Unrecognized patcher '{ownerID}'";
                                }
                            }
                            else
                            {
                                owners[ownerID] = $"Unrecognized patcher '{ownerID}'";
                            }
                        }
                    }
                    IEnumerable <Patch> problematicPrefixes = patches.Prefixes.Where(x => !overridingPrefixes.Contains(x) && (x.PatchMethod.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(HarmonyOverriddenAttribute)) as HarmonyOverriddenAttribute)?.Overrides.Length != 0 && overridingPrefixes.Any(
                                                                                         p => x.owner != p.owner &&
                                                                                         (x.PatchMethod.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(HarmonyOverriddenAttribute)) as HarmonyOverriddenAttribute)?.Overrides.Contains(p.owner) != true &&
                                                                                         (p.PatchMethod.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(HarmonyOverridesAttribute)) as HarmonyOverridesAttribute)?.Overrides.Contains(x.owner) != true &&
                                                                                         x.priority <= p.priority &&
                                                                                         !x.before.Contains(p.owner) &&
                                                                                         !p.after.Contains(x.owner)));
                    IEnumerable <Patch> problematicTranspilers = patches.Transpilers.Where(x => (x.PatchMethod.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(HarmonyOverriddenAttribute)) as HarmonyOverriddenAttribute)?.Overrides.Length != 0 && overridingPrefixes.Any(
                                                                                               p => x.owner != p.owner &&
                                                                                               (x.PatchMethod.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(HarmonyOverriddenAttribute)) as HarmonyOverriddenAttribute)?.Overrides.Contains(p.owner) != true &&
                                                                                               (p.PatchMethod.GetCustomAttributes(true).FirstOrDefault(a => a.GetType() == typeof(HarmonyOverridesAttribute)) as HarmonyOverridesAttribute)?.Overrides.Contains(x.owner) != true));
                    if (problematicPrefixes != null && problematicPrefixes.Any() || problematicTranspilers != null && problematicTranspilers.Any())
                    {
                        if (!logOverrideWarnings)
                        {
                            logOverrideWarnings = true;
                            Logger.LogWarning("Possibly problematic patch overrides detected!", false);
                        }
                        Logger.Log($"Patches to {patchedMethod.DeclaringType.FullName}.{patchedMethod.Name} by {overridingPrefixes.Select(x => owners[x.owner]).Concat()} may override the following patch{(problematicPrefixes.Count() + problematicTranspilers.Count() > 1 ? "es" : "")}:");
                        if (problematicPrefixes != null)
                        {
                            foreach (Patch patch in problematicPrefixes)
                            {
                                Logger.Log($" - Prefix from {owners[patch.owner]}");
                            }
                        }
                        if (problematicTranspilers != null)
                        {
                            foreach (Patch patch in problematicTranspilers)
                            {
                                Logger.Log($" - Transpiler from {owners[patch.owner]}");
                            }
                        }
                    }
                }
            }
            if (logOverrideWarnings)
            {
                Logger.Log("End of possibly problematic patch overrides.");
            }
            Logger.Log("Creating registries...");
            foreach (GadgetInfo gadget in QueuedGadgets.ToList())
            {
                if (gadget.Gadget.Enabled)
                {
                    foreach (Registry registry in gadget.Gadget.CreateRegistries())
                    {
                        GameRegistry.RegisterRegistry(registry);
                    }
                }
            }
            Logger.Log("Done creating registries.");
            Logger.Log("Initializing Gadgets...");
            foreach (GadgetInfo gadget in QueuedGadgets.ToList())
            {
                Logger.Log("Initializing Gadget '" + gadget.Attribute.Name + "'");
                try
                {
                    Registry.gadgetRegistering = gadget.Gadget.ModID;
                    gadget.Gadget.LoadInternal();
                }
                catch (Exception e)
                {
                    gadget.Gadget.Enabled = false;
                    GadgetCoreConfig.enabledGadgets[gadget.Attribute.Name] = false;
                    Gadgets.UnregisterGadget(gadget);
                    QueuedGadgets.Remove(gadget);
                    Logger.LogError("Exception Initializing Gadget '" + gadget.Attribute.Name + "':" + Environment.NewLine + e.ToString());
                }
                finally
                {
                    Registry.gadgetRegistering = -1;
                }
            }
            Logger.Log("Done initializing Gadgets.");
            QueuedGadgets.Clear();
            GadgetCoreConfig.Update();
        }