示例#1
0
        public ModEntry Import(string modDir, ModSource source = ModSource.Unknown)
        {
            if (FindByPath(modDir) != null)
            {
                // Mod already loaded
                return(null);
            }

            // look for .XComMod file
            string infoFile;

            try
            {
                infoFile = Directory.GetFiles(modDir, "*.XComMod", SearchOption.TopDirectoryOnly).SingleOrDefault();
            }
            catch (InvalidOperationException)
            {
                MessageBox.Show(
                    $"A mod could not be loaded since it contains multiple .xcommod files\r\nPlease notify the mod creator.\r\n\r\nPath: {modDir}");
                return(null);
            }

            if (infoFile == null)
            {
                return(null);
            }

            var modID  = Path.GetFileNameWithoutExtension(infoFile);
            var isDupe = All.Any(m => m.ID == modID && string.Compare(m.Path, modDir, StringComparison.OrdinalIgnoreCase) == 0);

            // Parse .XComMod file
            var modinfo = new ModInfo(infoFile);

            var mod = new ModEntry
            {
                ID        = modID,
                Name      = modinfo.Title ?? "Unnamed Mod",
                Path      = modDir,
                Source    = source,
                isActive  = false,
                DateAdded = DateTime.Now,
                State     = ModState.New
            };

            AddMod(modinfo.Category, mod);

            // mark dupes
            if (isDupe)
            {
                foreach (var m in All.Where(m => m.ID == modID))
                {
                    m.State |= ModState.DuplicateID;
                }
            }

            return(mod);
        }
示例#2
0
        /// <summary>
        /// Maps the type of the PDX.
        /// </summary>
        /// <param name="modSource">The mod source.</param>
        /// <returns>System.String.</returns>
        protected virtual string MapPdxType(ModSource modSource)
        {
            var pdxSource = modSource switch
            {
                ModSource.Paradox => "pdx",
                ModSource.Steam => "steam",
                _ => "local",
            };

            return(pdxSource);
        }
示例#3
0
        public ModEntry Import(string modDir, ModSource source = ModSource.Unknown)
        {
            // Check if Mod already loaded
            if (FindByPath(modDir) != null)
            {
                return(null);
            }

            // look for .XComMod file
            string infoFile = FindModInfo(modDir);

            if (infoFile == null)
            {
                return(null);
            }

            var modID = Path.GetFileNameWithoutExtension(infoFile);

            // Parse .XComMod file
            var modinfo = new ModInfo(infoFile);

            var mod = new ModEntry
            {
                Name      = modinfo.Title ?? "Unnamed Mod",
                ID        = modID,
                Path      = modDir,
                isActive  = false,
                DateAdded = DateTime.Now
            };

            mod.AddState(ModState.New);
            mod.SetRequiresWOTC(modinfo.RequiresXPACK);
            mod.SetSource(source);

            if (source == ModSource.SteamWorkshop)
            {
                var s = modDir.Split(Path.DirectorySeparatorChar).Last();

                if (long.TryParse(s, out var workShopId))
                {
                    mod.WorkshopID = workShopId;
                }
                else
                {
                    MessageBox.Show("A mod could not be loaded because the workshop ID failed to parse." +
                                    $"\nPlease check that the following directory conforms to valid workshop numbering.\n\nPath: {modDir}");
                    return(null);
                }
            }

            AddMod(modinfo.Category, mod);

            return(mod);
        }
示例#4
0
            public ModInfo(L4D2MM manager, string key, ModState state, ModSource source, L4D2Mod mod)
            {
                Manager         = manager;
                Key             = key;
                ModState        = state;
                Source          = source;
                Mod             = mod;
                IsHaveCollision = false;

                base.CollisionCountChangeHandler = (count) => { IsHaveCollision = count > 0; };
                RefreshResources();
            }
示例#5
0
        private void GetDelegateSteamResult(List <ulong> ids, ModSource source, bool create, int start = 0)
        {
            bool delegateSuccess = false;
            int  delegateCount   = 5;

            while (!delegateSuccess && delegateCount-- > 0)
            {
                using (var steamDelegate = new SteamDelegate(ids))
                {
                    steamDelegate.RunDelegate();
                    if (!steamDelegate.Timeout)
                    {
                        delegateSuccess = true;
                        if (!steamDelegate.Result)
                        {
                            WindowCallbacks.Print(StringAdapter.GetInfo("LinkToSteamFailed"));
                        }
                        else
                        {
                            int count = 0;
                            while (true)
                            {
                                count++;
                                var delegateResult = steamDelegate.Read();
                                if (delegateResult == null)
                                {
                                    break;
                                }
                                if (delegateResult.FileId > 0)
                                {
                                    //if(source == ModSource.Workshop)
                                    WindowCallbacks.Process(start + count, start + ids.Count, StringAdapter.GetInfo("LoadSteamWorkshop") + " : " + delegateResult.FileId.ToString());
                                    string key = (source == ModSource.Workshop ? @"workshop\" : "") + delegateResult.FileId.ToString() + ".vpk";
                                    if (!m_modStates.ContainsKey(key))
                                    {
                                        if (create)
                                        {
                                            m_modStates.Add(key, new ModInfo(this, key, ModState.On, source, new L4D2Mod(delegateResult.Json, delegateResult.Description)));
                                        }
                                    }
                                    else
                                    {
                                        m_modStates[key].Mod.LoadJson(delegateResult.Json, delegateResult.Description);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        private static List <ModMetaData> EnumerateMods(String Path, ModSource Source)
        {
            var r = new List <ModMetaData>();

            if (!Directory.Exists(Path))
            {
                return(r);
            }

            foreach (var dir in Directory.EnumerateDirectories(Path))
            {
                var metaData = GetMod(dir, Source);
                if (metaData != null)
                {
                    r.Add(metaData);
                }
            }

            return(r);
        }
        private static ModMetaData GetMod(string dir, ModSource Source)
        {
            try
            {
                var metaDataPath = dir + Path.DirectorySeparatorChar + "meta.json";
                var metaData     = FileUtils.LoadJsonFromAbsolutePath <ModMetaData>(metaDataPath);
                metaData.Directory = dir;
                metaData.Source    = Source;

                if (dir.StartsWith(GameSettings.Current.SteamModDirectory))
                {
                    metaData.SteamID = ulong.Parse(global::System.IO.Path.GetFileName(dir));
                }
                return(metaData);
            }
            catch (Exception e)
            {
                Console.WriteLine("Invalid mod: {0} {1}", dir, e.Message);
                return(null);
            }
        }
示例#8
0
 public Mod(string path, ModSource source)
 {
     Path   = path;
     Source = source;
     if (File.Exists(Path) && Path.EndsWith(".zip"))
     {
         m_contents = new ZipArchiveFileStore(Path, "");
         m_assets   = new FileAssetSource("Untitled Mod", new ZipArchiveFileStore(Path, "assets"));
     }
     else if (Directory.Exists(Path))
     {
         m_contents = new FolderFileStore(Path);
         m_assets   = new FileAssetSource("Untitled Mod", new FolderFileStore(System.IO.Path.Combine(Path, "assets")));
     }
     else
     {
         m_contents = null;
         m_assets   = new FileAssetSource("Untitled Mod", new EmptyFileStore());
     }
     m_assets.Mod = this;
     ReloadInfo();
     Loaded = false;
 }
示例#9
0
    private static void Start(CSharpBoxClass cSharpBox)
    {
        cSharpBox.ClearLogs();

        MainConstruct mainConstruct = cSharpBox.MainConstruct as MainConstruct;

        string outPutFolderPath = Get.ProfilePaths.ProfileRootDir().Append(mainConstruct.GetBlueprintName()).ToString() + string.Format("-{0:yyyy-MM-dd_hh-mm-ss-tt}", DateTime.Now);
        string texFolderPath    = Path.Combine(outPutFolderPath, "Textures");

        Directory.CreateDirectory(outPutFolderPath);
        Directory.CreateDirectory(texFolderPath);

        //TextureDefinition getTexDef(MaterialDefinition I) => Configured.i.Textures.Find(I.ColorTextureReference.Reference.Guid);
        IEnumerable <TextureDefinition> textureDefinitionList = Configured.i.Materials.Components
                                                                .Select(I => Configured.i.Textures.Find(I.ColorTextureReference.Reference.Guid))
                                                                .Where(I => I != null)
                                                                .Distinct();

        StringBuilder sb = new StringBuilder();

        foreach (TextureDefinition textureDefinition in textureDefinitionList)
        {
            ModSource modSource = textureDefinition.Source;

            if (modSource != ModSource.File && modSource != ModSource.Resources)
            {
                continue;
            }

            byte[] encodeResult = null;

            try
            {
                encodeResult = ForcedEncodeToJPG(textureDefinition.Texture.GetTexture());
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                continue;
            }

            if (encodeResult != null)
            {
                string texName = TexNameGenerate(textureDefinition);

                File.WriteAllBytes(Path.Combine(texFolderPath, texName + ".jpg"), encodeResult);

                cSharpBox.Log("newmtl " + texName);
                cSharpBox.Log("map_Kd " + Path.Combine("Textures", texName + ".jpg"));
                cSharpBox.Log(string.Empty);

                sb.Append("newmtl " + texName + "\n");
                sb.Append("map_Kd " + Path.Combine("Textures", texName + ".jpg") + "\n");
                sb.Append("\n");
            }
        }

        using (StreamWriter sw = new StreamWriter(Path.Combine(outPutFolderPath, "Test.mtl")))
        {
            sw.Write(sb.ToString());
        }



        List <AllConstruct> allConstructList = new List <AllConstruct>();

        mainConstruct.AllBasicsRestricted.GetAllConstructsBelowUsAndIncludingUs(allConstructList);

        foreach (AllConstruct allConstruct in allConstructList)
        {
            if (!(allConstruct.Chunks is ConstructableMeshMerger))
            {
                continue;
            }

            Dictionary <MaterialDefinition, List <Mesh> > meshListDictionary = new Dictionary <MaterialDefinition, List <Mesh> >();

            foreach (ICarriedObjectReference iCOR in allConstruct.CarriedObjects.Objects)
            {
                MeshRenderer meshRenderer = iCOR.ObjectItself.GetComponent <MeshRenderer>();
                MeshFilter   meshFilter   = iCOR.ObjectItself.GetComponent <MeshFilter>();

                if (meshRenderer == null || meshFilter == null)
                {
                    continue;
                }

                KeyValuePair <Guid, MaterialDefinition> item = Configured.i.Materials.DictionaryOfComponents.FirstOrDefault(d => d.Value.Material == meshRenderer.sharedMaterial);

                if (item.Equals(default(KeyValuePair <Guid, MaterialDefinition>)))
                {
                    continue;
                }

                if (!meshListDictionary.ContainsKey(item.Value))
                {
                    meshListDictionary.Add(item.Value, new List <Mesh>());
                }

                Mesh newMesh = UnityEngine.Object.Instantiate(meshFilter.sharedMesh);
                IEnumerable <Vector3> newVertexList = newMesh.vertices.Select(d => meshFilter.transform.localToWorldMatrix.MultiplyPoint(d));
                newVertexList = newVertexList.Select(d => allConstruct.myTransform.worldToLocalMatrix.MultiplyPoint(d));
                newMesh.SetVertices(newVertexList.ToList());

                meshListDictionary[item.Value].Add(newMesh);
            }

            ConstructableMeshMerger meshMerger = allConstruct.Chunks as ConstructableMeshMerger;
            cSharpBox.Log("AllBlockVertex Count : " + meshMerger.VertexCount);

            foreach (KeyValuePair <int, List <ChunkMesh> > chunkMeshListDictionary in meshMerger.D)
            {
                bool found;
                MaterialDefinition materialDefinition = Configured.i.Materials.FindUsingTheRuntimeId(chunkMeshListDictionary.Key, out found);

                bool flag_0 = chunkMeshListDictionary.Value.Count == 0;
                bool flag_1 = chunkMeshListDictionary.Value.All(d => d.VertCount == 0);

                if (!found || flag_0 || flag_1)
                {
                    continue;
                }

                if (!meshListDictionary.ContainsKey(materialDefinition))
                {
                    meshListDictionary.Add(materialDefinition, new List <Mesh>());
                }

                meshListDictionary[materialDefinition].AddRange(chunkMeshListDictionary.Value.Select(d => d.GetMesh()));
            }



            int  subConstructIndex = allConstruct.PersistentSubConstructIndex;
            bool isSubConstruct    = subConstructIndex != -1;

            Vector3    localPosition = Vector3.zero;
            Quaternion localRotation = Quaternion.identity;

            if (isSubConstruct)
            {
                MainConstruct mc = allConstruct.Main;
                localPosition = mc.SafeGlobalToLocal(allConstruct.SafePosition);
                localRotation = mc.SafeGlobalRotationToLocalRotation(allConstruct.SafeRotation);
            }

            List <Mesh> meshList = new List <Mesh>();

            foreach (KeyValuePair <MaterialDefinition, List <Mesh> > meshListDictionaryData in meshListDictionary)
            {
                List <Vector3> vertices        = new List <Vector3>();
                List <Vector3> normals         = new List <Vector3>();
                List <Vector2> uv              = new List <Vector2>();
                List <int>     triangles       = new List <int>();
                int            loadVertexCount = 0;

                foreach (Mesh mesh in meshListDictionaryData.Value)
                {
                    vertices.AddRange(mesh.vertices);
                    normals.AddRange(mesh.normals);
                    uv.AddRange(mesh.uv);
                    triangles.AddRange(mesh.triangles.Select(d => d + loadVertexCount));
                    loadVertexCount += mesh.vertexCount;
                }

                if (isSubConstruct)
                {
                    vertices = vertices.Select(I => localRotation * I + localPosition).ToList();
                }

                TextureDefinition textureDefinition = Configured.i.Textures.Find(meshListDictionaryData.Key.ColorTextureReference.Reference.Guid);

                Mesh newMesh = new Mesh
                {
                    indexFormat = UnityEngine.Rendering.IndexFormat.UInt32,
                    name        = TexNameGenerate(textureDefinition)
                };

                newMesh.SetVertices(vertices);
                newMesh.SetNormals(normals);
                newMesh.SetUVs(0, uv);
                newMesh.SetTriangles(triangles, 0);

                FlipHorizontal(newMesh);
                meshList.Add(newMesh);

                cSharpBox.Log("vertexCount : " + newMesh.vertices.Length);
            }



            string fileName = $"Test ({OutputCount++})";

            if (subConstructIndex == -1)
            {
                fileName = "MainConstruct";
            }
            else
            {
                fileName = $"SubConstruct_{subConstructIndex}";
            }

            MeshToFile(meshList, Path.Combine(outPutFolderPath, fileName + ".obj"));
        }
    }
示例#10
0
 public void SetSource(ModSource newSource)
 {
     Source = newSource;
 }
示例#11
0
        public ModEntry Import(string modDir, ModSource source = ModSource.Unknown)
        {
            if (FindByPath(modDir) != null)
            {
                // Mod already loaded
                return(null);
            }

            // look for .XComMod file
            string infoFile = FindModInfo(modDir);

            if (infoFile == null)
            {
                return(null);
            }

            var modID  = Path.GetFileNameWithoutExtension(infoFile);
            var isDupe = All.Any(m => m.ID == modID && string.Compare(m.Path, modDir, StringComparison.OrdinalIgnoreCase) == 0);

            // Parse .XComMod file
            var modinfo = new ModInfo(infoFile);

            var mod = new ModEntry
            {
                ID           = modID,
                Name         = modinfo.Title ?? "Unnamed Mod",
                Path         = modDir,
                Source       = source,
                isActive     = false,
                DateAdded    = DateTime.Now,
                BuiltForWOTC = modinfo.RequiresXPACK,
                State        = ModState.New
            };

            if (source == ModSource.SteamWorkshop)
            {
                var s = modDir.Split(Path.DirectorySeparatorChar).Last();
                try
                {
                    mod.WorkshopID = Convert.ToInt64(s);
                }
                catch (Exception)
                {
                    MessageBox.Show(
                        $"A mod could not be loaded because the workshop ID failed to parse.\r\nPlease check that the following directory conforms to valid workshop numbering.\r\n\r\nPath: {modDir}");
                    return(null);
                }
            }

            AddMod(modinfo.Category, mod);

            // mark dupes
            if (isDupe)
            {
                foreach (var m in All.Where(m => m.ID == modID))
                {
                    m.State |= ModState.DuplicateID;
                }
            }

            return(mod);
        }
示例#12
0
        public ModEntry Import(string modDir, ModSource source = ModSource.Unknown)
        {
            // Check if Mod already loaded
            if (FindByPath(modDir) != null)
            {
                return(null);
            }

            // look for .XComMod file
            string infoFile = FindModInfo(modDir);

            if (infoFile == null)
            {
                return(null);
            }

            var modID = Path.GetFileNameWithoutExtension(infoFile);

            // Parse .XComMod file
            var modinfo = new ModInfo(infoFile);

            var mod = new ModEntry
            {
                Name      = modinfo.Title ?? "Unnamed Mod",
                ID        = modID,
                Path      = modDir,
                isActive  = false,
                DateAdded = DateTime.Now
            };

            mod.AddState(ModState.New);
            mod.SetRequiresWOTC(modinfo.RequiresXPACK);
            mod.SetSource(source);

            if (source == ModSource.SteamWorkshop)
            {
                // for Workshop mods, the folder name represents the Workshop-Id
                var s = modDir.Split(Path.DirectorySeparatorChar).Last();

                if (long.TryParse(s, out var workShopId))
                {
                    mod.WorkshopID = workShopId;
                }
                else
                {
                    Log.Warn($"Unable to parse WorkShop-Id ({s}) from Steam mod directory " + modDir);
                    MessageBox.Show("A mod could not be loaded, because the workshop mod folder does not correspond to a valid workshop ID." +
                                    $"\n\nPlease check the following directory:\n{modDir}", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return(null);
                }
            }
            else if (source == ModSource.Manual)
            {
                // for mods from a custom mod path with custom folder names, we check if the XComMod file contains a Workshop-Id (i.e. publishedfileid)
                mod.WorkshopID = modinfo.PublishedFileID;
            }

            AddMod(modinfo.Category, mod);

            return(mod);
        }
示例#13
0
        public double GetModSource(ModSource source)
        {
            switch (source)
            {
            case (ModSource.Aftertouch):
                return(MidiInput.Aftertouch);

            case (ModSource.AmpEnv):
                return(AmpEnv.Output);

            case (ModSource.Filter1Env):
                return(Filter1Env.Output);

            case (ModSource.Filter2Env):
                return(Filter2Env.Output);

            case (ModSource.Mod1):
                return(Mod1.Output);

            case (ModSource.Mod2):
                return(Mod2.Output);

            case (ModSource.Mod3):
                return(Mod3.Output);

            case (ModSource.Mod4):
                return(Mod4.Output);

            case (ModSource.Mod5):
                return(Mod5.Output);

            case (ModSource.Mod6):
                return(Mod6.Output);

            case (ModSource.ModWheel):
                return(MidiInput.ModWheel);

            case (ModSource.Note):
                return(MidiInput.Note);

            case (ModSource.Pitch):
                return(MidiInput.Pitch);

            case (ModSource.PitchBend):
                return(MidiInput.PitchBend);

            case (ModSource.Seq1):
                return(0.0);

            case (ModSource.Seq2):
                return(0.0);

            case (ModSource.Spread):
                return(MidiInput.Spread);

            case (ModSource.Velocity):
                return(MidiInput.Velocity);


            default:
                return(0.0);
            }
        }
示例#14
0
            void parseModFiles(string path, ModSource source, bool isDirectory)
            {
                var fileInfo = Reader.GetFileInfo(path, Shared.Constants.DescriptorFile);

                if (fileInfo == null)
                {
                    fileInfo = Reader.GetFileInfo(path, $"*{Shared.Constants.ModExtension}");
                    if (fileInfo == null)
                    {
                        return;
                    }
                }
                var mod = Mapper.Map <IMod>(ModParser.Parse(fileInfo.Content));

                mod.FileName = path.Replace("\\", "/");
                mod.FullPath = path.StandardizeDirectorySeparator();
                mod.IsLocked = fileInfo.IsReadOnly;
                mod.Source   = source;
                var cleanedPath = path;

                if (!isDirectory)
                {
                    cleanedPath = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path));
                }

                var localPath = $"{Shared.Constants.ModDirectory}/{cleanedPath.Split(Path.DirectorySeparatorChar, StringSplitOptions.RemoveEmptyEntries).LastOrDefault()}{Shared.Constants.ModExtension}";

                switch (mod.Source)
                {
                case ModSource.Local:
                    setDescriptorPath(mod, localPath, localPath);
                    break;

                case ModSource.Steam:
                    if (mod.RemoteId.GetValueOrDefault() == 0)
                    {
                        if (!isDirectory)
                        {
                            var modParentDirectory = Path.GetDirectoryName(path);
                            mod.RemoteId = GetSteamModId(modParentDirectory, isDirectory);
                        }
                        else
                        {
                            mod.RemoteId = GetSteamModId(path, isDirectory);
                        }
                    }
                    setDescriptorPath(mod, $"{Shared.Constants.ModDirectory}/{Constants.Steam_mod_id}{mod.RemoteId}{Shared.Constants.ModExtension}", localPath);
                    break;

                case ModSource.Paradox:
                    if (!isDirectory)
                    {
                        var modParentDirectory = Path.GetDirectoryName(path);
                        mod.RemoteId = GetPdxModId(modParentDirectory, isDirectory);
                    }
                    else
                    {
                        mod.RemoteId = GetPdxModId(path, isDirectory);
                    }
                    setDescriptorPath(mod, $"{Shared.Constants.ModDirectory}/{Constants.Paradox_mod_id}{mod.RemoteId}{Shared.Constants.ModExtension}", localPath);
                    break;

                default:
                    break;
                }
                mods.Add(mod);
            }
示例#15
0
 /// <summary>
 /// Gets all mod descriptors.
 /// </summary>
 /// <param name="path">The path.</param>
 /// <param name="modSource">The mod source.</param>
 /// <returns>IEnumerable&lt;IMod&gt;.</returns>
 protected virtual IEnumerable <IMod> GetAllModDescriptors(string path, ModSource modSource)
 {
     var files       = Directory.Exists(path) ? Directory.EnumerateFiles(path, $"*{Shared.Constants.ZipExtension}").Union(Directory.EnumerateFiles(path, $"*{Shared.Constants.BinExtension}")) : Array.Empty <string>();
     var directories = Directory.Exists(path) ? Directory.EnumerateDirectories(path) : Array.Empty <string>();
     var mods        = new List <IMod>();
示例#16
0
        private static bool TryAddMod(string path, ModSource source, out Mod o_mod)
        {
            Mod mod;

            try
            {
                // (re)load the mod
                if (s_availableMods.ContainsKey(path))
                {
                    mod = s_availableMods[path];
                    mod.ReloadInfo();
                }
                else
                {
                    mod = new Mod(path, source);
                }
            }
            catch (Exception)
            {
                bool changed = false;
                if (!s_ignoredMods.Contains(path))
                {
                    App.Log("Error loading mod {0}. Ignoring", path);
                    s_ignoredMods.Add(path);
                    changed = true;
                }
                if (s_availableMods.ContainsKey(path))
                {
                    App.Log("Error loading mod {0}. Disabling", path);
                    mod = s_availableMods[path];
                    s_availableMods.Remove(path);
                    if (mod.Loaded)
                    {
                        s_removedLoadedMods.Add(mod);
                    }
                    changed = true;
                }
                o_mod = null;
                return(changed);
            }

            // Check if the mod is too new
            if (App.Info.Version < mod.MinimumGameVersion)
            {
                // Remove and ignore the mod
                bool changed = false;
                if (!s_ignoredMods.Contains(path))
                {
                    App.Log("Error: Ignoring mod {0} ({1}): Requires game version {2}", mod.Title, mod.Path, mod.MinimumGameVersion);
                    s_ignoredMods.Add(path);
                    changed = true;
                }
                if (s_availableMods.ContainsKey(path))
                {
                    App.Log("Error: Disabling mod {0} ({1}): Requires game version {2}", mod.Title, mod.Path, mod.MinimumGameVersion);
                    s_availableMods.Remove(path);
                    if (mod.Loaded)
                    {
                        s_removedLoadedMods.Add(mod);
                    }
                    changed = true;
                }
                o_mod = null;
                return(changed);
            }
            else
            {
                // Add the mod
                bool changed = false;
                if (s_ignoredMods.Contains(path))
                {
                    s_ignoredMods.Remove(path);
                    changed = true;
                }
                if (!s_availableMods.ContainsKey(path))
                {
                    App.Log("Found mod {0} ({1})", mod.Title, mod.Path);
                    s_availableMods[path] = mod;
                    changed = true;
                }
                o_mod = mod;
                return(changed);
            }
        }