/// <summary>
        /// Loads and registers the <see cref="GadgetMod"/> represented by the given directory. Will log a warning and return null if the given directory is not a valid <see cref="GadgetMod"/>. Will also add the name to <see cref="ErroredMods"/> if the directory was valid, but an error occured during the mod-loading process.
        /// </summary>
        public static GadgetMod LoadModDir(string modDir)
        {
            string modName = null;

            try
            {
                string manifestFile = Path.Combine(modDir, "Manifest.ini");
                if (File.Exists(manifestFile))
                {
                    if (Directory.Exists(Path.Combine(modDir, "Libs")))
                    {
                        foreach (string lib in Directory.GetFiles(Path.Combine(modDir, "Libs")))
                        {
                            string   existingFilePath = Path.Combine(GadgetPaths.LibsPath, Path.GetFileName(lib));
                            FileInfo libFile          = new FileInfo(lib);
                            if (!File.Exists(existingFilePath) || libFile.LastWriteTime > new FileInfo(existingFilePath).LastWriteTime)
                            {
                                libFile.CopyTo(existingFilePath, true);
                            }
                        }
                    }
                    IniData manifest = manifestIniParser.ReadFile(manifestFile);
                    modName = manifest["Metadata"]["Name"];
                    Assembly modAssembly = Assembly.LoadFile(Path.Combine(modDir, manifest["Metadata"]["Assembly"]));
                    GadgetCore.LoadedAssemblies[modAssembly.GetName().Name] = modAssembly;
                    GadgetMod mod = new GadgetMod(modDir, manifest, modAssembly);
                    GadgetMods.RegisterMod(mod);
                    if (!BatchLoading)
                    {
                        LoadGadgetMod(mod);
                    }
                    return(mod);
                }
                else
                {
                    Logger.LogWarning("Invalid or non-mod directory '" + Path.GetFileName(modDir) + "' in Mods directory!");
                }
            }
            catch (Exception e)
            {
                Logger.LogError("Exception loading mod directory '" + Path.GetFileName(modDir) + "':");
                Logger.LogError(e.ToString());
                if (modName != null)
                {
                    Tuple <string, string> erroredMod = Tuple.Create(modName, modDir);
                    if (!m_ErroredMods.Contains(erroredMod))
                    {
                        m_ErroredMods.Add(erroredMod);
                    }
                }
            }
            return(null);
        }
        /// <summary>
        /// Loads and registers the <see cref="GadgetMod"/> represented by the given file. Will log a warning and return null if the given file is not a valid <see cref="GadgetMod"/>. Will also add the name to <see cref="ErroredMods"/> if the file was valid, but an error occured during the mod-loading process.
        /// </summary>
        public static GadgetMod LoadModFile(string modFile)
        {
            string  modName = null;
            ZipFile modZip  = null;

            try
            {
                modZip = new ZipFile(modFile);
                if (Path.GetExtension(modFile) == ".umfmod")
                {
                    GadgetCore.CoreLib.DecryptUMFModFile(modZip);
                }
                if (modZip.ContainsEntry("Manifest.ini"))
                {
                    if (modZip.ContainsEntry("Libs"))
                    {
                        foreach (ZipEntry lib in modZip.Entries.Where(x => !x.IsDirectory && x.FileName.StartsWith("Libs")))
                        {
                            string existingFilePath = Path.Combine(GadgetPaths.GadgetCorePath, lib.FileName);
                            if (!File.Exists(existingFilePath) || lib.ModifiedTime > new FileInfo(existingFilePath).LastWriteTime)
                            {
                                lib.Extract(GadgetPaths.GadgetCorePath, ExtractExistingFileAction.OverwriteSilently);
                            }
                        }
                    }
                    using (MemoryStream stream = new MemoryStream())
                    {
                        modZip["Manifest.ini"].Extract(stream);
                        stream.Position = 0;
                        IniData manifest = manifestIniParser.ReadData(new StreamReader(stream));
                        modName = manifest["Metadata"]["Name"];
                        stream.SetLength(0);
                        if (manifest["Metadata"]["Assembly"] != null && modZip.ContainsEntry(manifest["Metadata"]["Assembly"]))
                        {
                            modZip[manifest["Metadata"]["Assembly"]].Extract(stream);
                        }
                        else
                        {
                            Logger.LogWarning("Failed to load mod `" + modName + "` because " + (manifest["Metadata"]["Assembly"] != null ? "the assembly name in its manifest is not valid!" : "its manifest does not contain the name of its asembly!"));
                            return(null);
                        }
                        Assembly modAssembly = Assembly.Load(stream.ToArray());
                        GadgetCore.LoadedAssemblies[modAssembly.GetName().Name] = modAssembly;
                        GadgetMod mod = new GadgetMod(modFile, manifest, modAssembly, modZip);
                        GadgetMods.RegisterMod(mod);
                        if (!BatchLoading)
                        {
                            LoadGadgetMod(mod);
                        }
                        modZip = null;
                        return(mod);
                    }
                }
                else
                {
                    Logger.LogWarning("Invalid or non-mod file '" + Path.GetFileName(modFile) + "' in Mods directory!");
                }
            }
            catch (Exception e)
            {
                Logger.LogError("Exception loading mod file '" + Path.GetFileName(modFile) + "':");
                Logger.LogError(e.ToString());
                if (modName != null)
                {
                    Tuple <string, string> erroredMod = Tuple.Create(modName, modFile);
                    if (!m_ErroredMods.Contains(erroredMod))
                    {
                        m_ErroredMods.Add(erroredMod);
                    }
                }
            }
            finally
            {
                modZip?.Dispose();
            }
            return(null);
        }