Exemplo n.º 1
0
        private void InitializeFolderComboBox()
        {
            FolderComboBox.SelectedIndex = 0;
            List <string> sModFolders = new List <string> {
                "All Folders"
            };
            var allModConfigs = GameConfig.ModConfigs.ToList();

            Mods = ModDatabase.Get(SelectedGame)
                   .OrderBy(x => GameConfig.GetModPriority(x.Id))
                   .Select(x => new ModViewModel(x, SelectedGame))
                   .ToList();

            foreach (ModViewModel modViewModel in Mods)
            {
                Mod    mod       = (Mod)modViewModel;
                string modFolder = Path.GetFileName(Path.GetDirectoryName(mod.BaseDirectory));
                if (modFolder != "Mods" && !Regex.IsMatch(modFolder, "Persona[^ ]*") && !sModFolders.Contains(modFolder)) //Any folder containing mod that isn't for sorting mods by game
                {
                    sModFolders.Add(modFolder);
                }
            }

            FolderComboBox.ItemsSource = sModFolders.ToArray();
        }
Exemplo n.º 2
0
        /*********
        ** Public methods
        *********/
        /// <summary>Get manifest metadata for each folder in the given root path.</summary>
        /// <param name="toolkit">The mod toolkit.</param>
        /// <param name="rootPath">The root path to search for mods.</param>
        /// <param name="modDatabase">Handles access to SMAPI's internal mod metadata list.</param>
        /// <returns>Returns the manifests by relative folder.</returns>
        public IEnumerable <IModMetadata> ReadManifests(ModToolkit toolkit, string rootPath, ModDatabase modDatabase)
        {
            foreach (ModFolder folder in toolkit.GetModFolders(rootPath))
            {
                Manifest manifest = folder.Manifest;

                // parse internal data record (if any)
                ModDataRecordVersionedFields dataRecord = modDatabase.Get(manifest?.UniqueID)?.GetVersionedFields(manifest);

                // apply defaults
                if (manifest != null && dataRecord != null)
                {
                    if (dataRecord.UpdateKey != null)
                    {
                        manifest.UpdateKeys = new[] { dataRecord.UpdateKey }
                    }
                    ;
                }

                // build metadata
                bool shouldIgnore        = folder.Type == ModType.Ignored;
                ModMetadataStatus status = folder.ManifestParseError == ModParseError.None || shouldIgnore
                    ? ModMetadataStatus.Found
                    : ModMetadataStatus.Failed;

                yield return(new ModMetadata(folder.DisplayName, folder.Directory.FullName, rootPath, manifest, dataRecord, isIgnored: shouldIgnore)
                             .SetStatus(status, shouldIgnore ? "disabled by dot convention" : folder.ManifestParseErrorText));
            }
        }
Exemplo n.º 3
0
        /*********
        ** Public methods
        *********/
        /// <summary>Get manifest metadata for each folder in the given root path.</summary>
        /// <param name="toolkit">The mod toolkit.</param>
        /// <param name="rootPath">The root path to search for mods.</param>
        /// <param name="modDatabase">Handles access to SMAPI's internal mod metadata list.</param>
        /// <returns>Returns the manifests by relative folder.</returns>
        public IEnumerable <IModMetadata> ReadManifests(ModToolkit toolkit, string rootPath, ModDatabase modDatabase)
        {
            foreach (ModFolder folder in toolkit.GetModFolders(rootPath))
            {
                Manifest manifest = folder.Manifest;

                // parse internal data record (if any)
                ModDataRecordVersionedFields dataRecord = modDatabase.Get(manifest?.UniqueID)?.GetVersionedFields(manifest);

                // apply defaults
                if (manifest != null && dataRecord != null)
                {
                    if (dataRecord.UpdateKey != null)
                    {
                        manifest.UpdateKeys = new[] { dataRecord.UpdateKey }
                    }
                    ;
                }

                // build metadata
                ModMetadataStatus status = folder.ManifestParseError == null || !folder.ShouldBeLoaded
                    ? ModMetadataStatus.Found
                    : ModMetadataStatus.Failed;
                string relativePath = PathUtilities.GetRelativePath(rootPath, folder.Directory.FullName);

                yield return(new ModMetadata(folder.DisplayName, folder.Directory.FullName, relativePath, manifest, dataRecord, isIgnored: !folder.ShouldBeLoaded)
                             .SetStatus(status, !folder.ShouldBeLoaded ? "disabled by dot convention" : folder.ManifestParseError));
            }
        }
Exemplo n.º 4
0
        /*********
        ** Public methods
        *********/
        /// <summary>Get manifest metadata for each folder in the given root path.</summary>
        /// <param name="toolkit">The mod toolkit.</param>
        /// <param name="rootPath">The root path to search for mods.</param>
        /// <param name="modDatabase">Handles access to SMAPI's internal mod metadata list.</param>
        /// <returns>Returns the manifests by relative folder.</returns>
        public IEnumerable <IModMetadata> ReadManifests(ModToolkit toolkit, string rootPath, ModDatabase modDatabase)
        {
            foreach (ModFolder folder in toolkit.GetModFolders(rootPath))
            {
                Manifest manifest = folder.Manifest;

                // parse internal data record (if any)
                ModDataRecordVersionedFields dataRecord = modDatabase.Get(manifest?.UniqueID)?.GetVersionedFields(manifest);

                // apply defaults
                if (manifest != null && dataRecord != null)
                {
                    if (dataRecord.UpdateKey != null)
                    {
                        manifest.UpdateKeys = new[] { dataRecord.UpdateKey }
                    }
                    ;
                }

                // build metadata
                ModMetadataStatus status = folder.ManifestParseError == null
                    ? ModMetadataStatus.Found
                    : ModMetadataStatus.Failed;
                yield return(new ModMetadata(folder.DisplayName, folder.Directory.FullName, manifest, dataRecord).SetStatus(status, folder.ManifestParseError));
            }
        }
Exemplo n.º 5
0
 public void RefreshMods()
 {
     Mods = ModDatabase.Get(SelectedGame)
            .OrderBy(x => GameConfig.GetModPriority(x.Id))
            .Select(x => new ModViewModel(x))
            .ToList();
 }
Exemplo n.º 6
0
        private void RefreshMods()
        {
            Mods = ModDatabase.Get(SelectedGame)
                   .OrderBy(x => GameConfig.GetModPriority(x.Id))
                   .Select(x => new ModViewModel(x, SelectedGame))
                   .ToList();

            ModGrid.ItemsSource = Mods;
        }
Exemplo n.º 7
0
 /// <summary>
 /// Refreshes the mods.
 /// Clears the nodestore,
 /// Sets up the modviewmodels from the moddatabase
 /// adds them to a list and the nodestore
 /// </summary>
 private void RefreshMods()
 {
     ModGrid.NodeStore.Clear();
     Mods = ModDatabase.Get(SelectedGame)
            .OrderBy(x => GameConfig.GetModPriority(x.Id))
            .Select(x => new ModViewModel(x))
            .ToList();
     Mods.ForEach(x => ModGrid.NodeStore.AddNode(x));
 }
        protected GameConfig()
        {
            OutputDirectoryPath = Path.Combine("Output", $"{Game}");

            mModConfigs = new Dictionary <Guid, ModConfig>();

            // Initialize default mod configs for game mods in the database
            int priority = 0;

            foreach (var mod in ModDatabase.Get(Game))
            {
                AddModConfig(mod.Id, priority++, false);
            }
        }
Exemplo n.º 9
0
        /// <summary>
        /// Resolves a dependency and returns all DLL files that the dependency uses
        /// </summary>
        /// <param name="name"></param>
        /// <param name="verMajor"></param>
        /// <param name="verMinor"></param>
        /// <param name="canBeNewer"></param>
        /// <param name="searchDirs"></param>
        /// <param name="dllDir"></param>
        /// <param name="imageDir"></param>
        /// <param name="soundDir"></param>
        /// <param name="caller"></param>
        /// <returns></returns>
        public static IEnumerable <string> LoadDependency(string name, int verMajor, int verMinor,
                                                          string dllDir, string mapDir, string assetDir, string caller, bool overwriteExisting)
        {
            List <string> _dependencyDlls = new List <string>();

            if (!ModDatabase.Contains(name, verMajor))
            {
                return(Enumerable.Empty <string>());
            }

            var  dbItem    = ModDatabase.Get(name, verMajor);
            bool versionOk = true;

            if (verMajor > dbItem.Major)
            {
                versionOk = false;
            }
            else if (verMajor == dbItem.Major && verMinor < dbItem.Minor)
            {
                versionOk = false;
            }

            if (!versionOk)
            {
                throw new Exception("Could not resolve to an appropriate version of a dependency. All versions are too old.");
            }

            if (IsCircular(dbItem.File, caller))
            {
                throw new Exception($"{name} and {caller} reference each other circularly. Cannot load either.");
            }

            //Resolve and load the dependency
            string errors;
            var    module = ModLoader.LoadCompressedModFile(dbItem.File, dllDir, mapDir, assetDir, out errors,
                                                            dbItem.UsesWhitelist, overwriteExisting);

            if (errors != null)
            {
                throw new Exception(errors);
            }
            //And then add all the dependencies
            _dependencyDlls.AddRange(module.Assemblies.Select(a => a.Location));
            _dependencyDlls.AddRange(module.Dependencies.Select(a => a.Location));

            return(_dependencyDlls.Distinct());
        }
Exemplo n.º 10
0
        /*********
        ** Public methods
        *********/
        /// <summary>Get manifest metadata for each folder in the given root path.</summary>
        /// <param name="toolkit">The mod toolkit.</param>
        /// <param name="rootPath">The root path to search for mods.</param>
        /// <param name="modDatabase">Handles access to SMAPI's internal mod metadata list.</param>
        /// <returns>Returns the manifests by relative folder.</returns>
        public IEnumerable <IModMetadata> ReadManifests(ModToolkit toolkit, string rootPath, ModDatabase modDatabase)
        {
            foreach (ModFolder folder in toolkit.GetModFolders(rootPath))
            {
                Manifest manifest = folder.Manifest;

                // parse internal data record (if any)
                ModDataRecordVersionedFields dataRecord = modDatabase.Get(manifest?.UniqueID)?.GetVersionedFields(manifest);

                // get display name
                string displayName = manifest?.Name;
                if (string.IsNullOrWhiteSpace(displayName))
                {
                    displayName = dataRecord?.DisplayName;
                }
                if (string.IsNullOrWhiteSpace(displayName))
                {
                    displayName = PathUtilities.GetRelativePath(rootPath, folder.ActualDirectory?.FullName ?? folder.SearchDirectory.FullName);
                }

                // apply defaults
                if (manifest != null && dataRecord != null)
                {
                    if (dataRecord.UpdateKey != null)
                    {
                        manifest.UpdateKeys = new[] { dataRecord.UpdateKey }
                    }
                    ;
                }

                // build metadata
                ModMetadataStatus status = folder.ManifestParseError == null
                    ? ModMetadataStatus.Found
                    : ModMetadataStatus.Failed;
                yield return(new ModMetadata(displayName, folder.ActualDirectory?.FullName, manifest, dataRecord).SetStatus(status, folder.ManifestParseError));
            }
        }
Exemplo n.º 11
0
        private void BuildButton_Click(object sender, RoutedEventArgs e)
        {
            if (string.IsNullOrWhiteSpace(GameConfig.OutputDirectoryPath))
            {
                MessageBox.Show(this, "Please specify an output directory in the settings.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (Mods.Count == 0)
            {
                MessageBox.Show(this, "No mods are available.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (!UpdateGameConfigEnabledMods())
            {
                MessageBox.Show(this, "No mods are enabled.", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            var task = Task.Factory.StartNew(() =>
            {
                gEnabledMods = GameConfig.ModConfigs.Where(x => x.Enabled)
                               .OrderBy(x => x.Priority)
                               .Select(x => x.ModId)
                               .Select(x => ModDatabase.Get(x))
                               .ToList();

                Log.General.Info("Building mods:");
                foreach (var enabledMod in gEnabledMods)
                {
                    Log.General.Info($"\t{enabledMod.Title}");
                }

                // Run prebuild scripts
                RunModScripts(gEnabledMods, "prebuild.bat");

                var merger = new TopToBottomModMerger();
                var merged = merger.Merge(gEnabledMods);

                // Todo
                var builder = ModBuilderManager.GetCompatibleModBuilders(SelectedGame).First().Create();
                if (UltraISOUtility.Available)
                {
                    if (SelectedGame == Game.Persona3)
                    {
                        builder = new Persona3IsoModBuilder();
                    }
                    else if (SelectedGame == Game.Persona4)
                    {
                        builder = new Persona4IsoModBuilder();
                    }
                }

                Log.General.Info($"Output path: {GameConfig.OutputDirectoryPath}");

#if !DEBUG
                try
#endif
                {
                    builder.Build(merged, gEnabledMods, GameConfig.OutputDirectoryPath);
                }
 #if !DEBUG
                catch (InvalidConfigException exception)
                {
                    InvokeOnUIThread(
                        () => MessageBox.Show(this, $"SelectedGame configuration is invalid.\n{exception.Message}", "Error",
                                              MessageBoxButton.OK, MessageBoxImage.Error));

                    return(false);
                }
                catch (MissingFileException exception)
                {
                    InvokeOnUIThread(
                        () => MessageBox.Show(this, $"A file is missing:\n{exception.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error));

                    return(false);
                }
                catch (Exception exception)
                {
                    InvokeOnUIThread(
                        () =>
                        MessageBox.Show(
                            this, $"Unhandled exception occured while building:\n{exception.Message}\n{exception.StackTrace}", "Error",
                            MessageBoxButton.OK, MessageBoxImage.Error));

#if DEBUG
                    throw;
#endif

#pragma warning disable 162
                    return(false);

#pragma warning restore 162
                }
#endif

                return(true);
            }, TaskCreationOptions.AttachedToParent);

            task.ContinueWith((t) =>
            {
                InvokeOnUIThread(() =>
                {
                    if (t.Result)
                    {
                        MessageBox.Show(this, "Done building!", "Done", MessageBoxButton.OK, MessageBoxImage.None);
                        RunModScripts(gEnabledMods, "postbuild.bat");
                    }
                });
            });
        }
Exemplo n.º 12
0
        /*********
        ** Private methods
        *********/
        /// <summary>Sort a mod's dependencies by the order they should be loaded, and remove any mods that can't be loaded due to missing or conflicting dependencies.</summary>
        /// <param name="mods">The full list of mods being validated.</param>
        /// <param name="modDatabase">Handles access to SMAPI's internal mod metadata list.</param>
        /// <param name="mod">The mod whose dependencies to process.</param>
        /// <param name="states">The dependency state for each mod.</param>
        /// <param name="sortedMods">The list in which to save mods sorted by dependency order.</param>
        /// <param name="currentChain">The current change of mod dependencies.</param>
        /// <returns>Returns the mod dependency status.</returns>
        private ModDependencyStatus ProcessDependencies(IModMetadata[] mods, ModDatabase modDatabase, IModMetadata mod, IDictionary <IModMetadata, ModDependencyStatus> states, Stack <IModMetadata> sortedMods, ICollection <IModMetadata> currentChain)
        {
            // check if already visited
            switch (states[mod])
            {
            // already sorted or failed
            case ModDependencyStatus.Sorted:
            case ModDependencyStatus.Failed:
                return(states[mod]);

            // dependency loop
            case ModDependencyStatus.Checking:
                // This should never happen. The higher-level mod checks if the dependency is
                // already being checked, so it can fail without visiting a mod twice. If this
                // case is hit, that logic didn't catch the dependency loop for some reason.
                throw new InvalidModStateException($"A dependency loop was not caught by the calling iteration ({string.Join(" => ", currentChain.Select(p => p.DisplayName))} => {mod.DisplayName})).");

            // not visited yet, start processing
            case ModDependencyStatus.Queued:
                break;

            // sanity check
            default:
                throw new InvalidModStateException($"Unknown dependency status '{states[mod]}'.");
            }

            // collect dependencies
            ModDependency[] dependencies = this.GetDependenciesFrom(mod.Manifest, mods).ToArray();

            // mark sorted if no dependencies
            if (!dependencies.Any())
            {
                sortedMods.Push(mod);
                return(states[mod] = ModDependencyStatus.Sorted);
            }

            // mark failed if missing dependencies
            {
                string[] failedModNames = (
                    from entry in dependencies
                    where entry.IsRequired && entry.Mod == null
                    let displayName = modDatabase.Get(entry.ID)?.DisplayName ?? entry.ID
                                      let modUrl = modDatabase.GetModPageUrlFor(entry.ID)
                                                   orderby displayName
                                                   select modUrl != null
                        ? $"{displayName}: {modUrl}"
                        : displayName
                    ).ToArray();
                if (failedModNames.Any())
                {
                    sortedMods.Push(mod);
                    mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.MissingDependencies, $"it requires mods which aren't installed ({string.Join(", ", failedModNames)}).");
                    return(states[mod] = ModDependencyStatus.Failed);
                }
            }

            // dependency min version not met, mark failed
            {
                string[] failedLabels =
                    (
                        from entry in dependencies
                        where entry.Mod != null && entry.MinVersion != null && entry.MinVersion.IsNewerThan(entry.Mod.Manifest.Version)
                        select $"{entry.Mod.DisplayName} (needs {entry.MinVersion} or later)"
                    )
                    .ToArray();
                if (failedLabels.Any())
                {
                    sortedMods.Push(mod);
                    mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.MissingDependencies, $"it needs newer versions of some mods: {string.Join(", ", failedLabels)}.");
                    return(states[mod] = ModDependencyStatus.Failed);
                }
            }

            // process dependencies
            {
                states[mod] = ModDependencyStatus.Checking;

                // recursively sort dependencies
                foreach (var dependency in dependencies)
                {
                    IModMetadata requiredMod = dependency.Mod;
                    var          subchain    = new List <IModMetadata>(currentChain)
                    {
                        mod
                    };

                    // ignore missing optional dependency
                    if (!dependency.IsRequired && requiredMod == null)
                    {
                        continue;
                    }

                    // detect dependency loop
                    if (states[requiredMod] == ModDependencyStatus.Checking)
                    {
                        sortedMods.Push(mod);
                        mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.MissingDependencies, $"its dependencies have a circular reference: {string.Join(" => ", subchain.Select(p => p.DisplayName))} => {requiredMod.DisplayName}).");
                        return(states[mod] = ModDependencyStatus.Failed);
                    }

                    // recursively process each dependency
                    var substatus = this.ProcessDependencies(mods, modDatabase, requiredMod, states, sortedMods, subchain);
                    switch (substatus)
                    {
                    // sorted successfully
                    case ModDependencyStatus.Sorted:
                    case ModDependencyStatus.Failed when !dependency.IsRequired:     // ignore failed optional dependency
                        break;

                    // failed, which means this mod can't be loaded either
                    case ModDependencyStatus.Failed:
                        sortedMods.Push(mod);
                        mod.SetStatus(ModMetadataStatus.Failed, ModFailReason.MissingDependencies, $"it needs the '{requiredMod.DisplayName}' mod, which couldn't be loaded.");
                        return(states[mod] = ModDependencyStatus.Failed);

                    // unexpected status
                    case ModDependencyStatus.Queued:
                    case ModDependencyStatus.Checking:
                        throw new InvalidModStateException($"Something went wrong sorting dependencies: mod '{requiredMod.DisplayName}' unexpectedly stayed in the '{substatus}' status.");

                    // sanity check
                    default:
                        throw new InvalidModStateException($"Unknown dependency status '{states[mod]}'.");
                    }
                }

                // all requirements sorted successfully
                sortedMods.Push(mod);
                return(states[mod] = ModDependencyStatus.Sorted);
            }
        }