Exemple #1
0
        /// <summary>Get whether the token always chooses from a set of known values for the given input. Mutually exclusive with <see cref="HasBoundedRangeValues"/>.</summary>
        /// <param name="input">The input argument, if applicable.</param>
        /// <param name="allowedValues">The possible values for the input.</param>
        public bool HasBoundedValues(string input, out IEnumerable <string> allowedValues)
        {
            RecolorTokenArguments inputData = RecolorTokenArguments.Parse(input);
            string generatedFilePath        = GenerateFilePath(inputData);

            allowedValues = new[] { generatedFilePath };

            return(true);
        }
Exemple #2
0
        /// <summary>Generates a file path from token arguments.</summary>
        private string GenerateFilePath(RecolorTokenArguments inputData)
        {
            // "gamecontent" has no file extension, append ".png".
            string outputPath = inputData.SourcePath.ToLowerInvariant() == "gamecontent"
                              ? $"{inputData.AssetName}.png"
                              : inputData.SourcePath;

            // Encode configuration to avoid file name collisions.
            string suffix = $"recolored_{inputData.GetUniqueFileSuffix()}";

            return(Utility.AddFileNameSuffix(Path.Combine("generated", outputPath), suffix));
        }
Exemple #3
0
        /// <summary>Get the current values.</summary>
        /// <param name="input">The input argument, if applicable.</param>
        public IEnumerable <string> GetValues(string input)
        {
            RecolorTokenArguments inputData = RecolorTokenArguments.Parse(input);

            if (!(inputData.Equals(previousInputData_)))
            {
                mustUpdateContext_ = true;
            }
            previousInputData_ = inputData;

            IContentPack contentPack = Utility.GetContentPackFromModInfo(helper_.ModRegistry.Get(inputData.ContentPackName));

            monitor_.Log($"Content pack {contentPack.Manifest.UniqueID} requests recoloring of {inputData.AssetName}.");
            monitor_.Log($"Recolor with {inputData.MaskPath} and {Utility.ColorToHtml(inputData.BlendColor)}");

            // "gamecontent" means loading from game folder.
            Texture2D source = inputData.SourcePath.ToLowerInvariant() == "gamecontent"
                             ? helper_.Content.Load <Texture2D>(inputData.AssetName, ContentSource.GameContent)
                             : contentPack.LoadAsset <Texture2D>(inputData.SourcePath);

            Texture2D extracted = inputData.MaskPath.ToLowerInvariant() != "none"
                                ? ExtractSubImage(source,
                                                  contentPack.LoadAsset <Texture2D>(inputData.MaskPath),
                                                  inputData.DesaturationMode)
                                : source;

            Texture2D target = ColorBlend(extracted, inputData.BlendColor);

            // ATTENTION: In order to load files we just generated we need at least ContentPatcher 1.18.3 .
            string generatedFilePath         = GenerateFilePath(inputData);
            string generatedFilePathAbsolute = Path.Combine(contentPack.DirectoryPath, generatedFilePath);

            Directory.CreateDirectory(Path.GetDirectoryName(generatedFilePathAbsolute));
            using (FileStream fs = new FileStream(generatedFilePathAbsolute, FileMode.Create)) {
                target.SaveAsPng(fs, target.Width, target.Height);
                fs.Close();
            }

            monitor_.Log($"Generated file {generatedFilePathAbsolute}, returning relative path {generatedFilePath}");

            helper_.Content.InvalidateCache(inputData.AssetName);

            yield return(generatedFilePath);
        }
Exemple #4
0
        /// <summary>Get the current values.</summary>
        /// <param name="input">The input argument, if applicable.</param>
        public IEnumerable <string> GetValues(string input)
        {
            RecolorTokenArguments inputData = RecolorTokenArguments.Parse(input);

            IContentPack contentPack = Utility.GetContentPackFromModInfo(helper_.ModRegistry.Get(inputData.ContentPackName));

            // ATTENTION: In order to load files we just generated we need at least ContentPatcher 1.18.3 .
            string generatedFilePath         = GenerateFilePath(inputData);
            string generatedFilePathAbsolute = Path.Combine(contentPack.DirectoryPath, generatedFilePath);

            if (!(inputData.Equals(previousInputData_)))
            {
                mustUpdateContext_ = true;

                previousInputData_ = inputData;

                monitor_.Log($"Content pack {contentPack.Manifest.UniqueID} requests recoloring of {inputData.AssetName}.");
                monitor_.Log($"Recolor with {inputData.MaskPath} and {Utility.ColorToHtml(inputData.BlendColor)}, flip mode {inputData.FlipMode}, brightness {inputData.Brightness}");

                // Check versions: If version of SDV or content pack changed we have to delete generated images.
                string contentPackVersion             = contentPack.Manifest.Version.ToString();
                string generatedDirectoryPathAbsolute = Path.Combine(contentPack.DirectoryPath, "generated");
                var    versions = contentPack.ReadJsonFile <Dictionary <string, string> >("generated/versions.json") ?? new Dictionary <string, string>();
                if (!versions.TryGetValue("StardewValley", out string stardewVersion) || stardewVersion != Game1.version)
                {
                    versions["StardewValley"] = Game1.version;

                    monitor_.Log($"Version of StardewValley changed from {stardewVersion ?? "(null)"} to {Game1.version}, deleting generated files.");
                    if (Directory.Exists(generatedDirectoryPathAbsolute))
                    {
                        Directory.Delete(generatedDirectoryPathAbsolute, true);
                    }

                    contentPack.WriteJsonFile("generated/versions.json", versions);
                }
                if (!versions.TryGetValue(contentPack.Manifest.UniqueID, out string modVersion) || modVersion != contentPackVersion)
                {
                    versions[contentPack.Manifest.UniqueID] = contentPackVersion;

                    monitor_.Log($"Version of content pack {contentPack.Manifest.UniqueID} changed from {modVersion ?? "(null)"} to {contentPackVersion}, deleting generated files.");
                    if (Directory.Exists(generatedDirectoryPathAbsolute))
                    {
                        Directory.Delete(generatedDirectoryPathAbsolute, true);
                    }

                    contentPack.WriteJsonFile("generated/versions.json", versions);
                }

                // Skip actions if file was found.
                if (File.Exists(generatedFilePathAbsolute))
                {
                    monitor_.Log($"Found existing file {generatedFilePathAbsolute}, returning relative path {generatedFilePath}");

                    helper_.Content.InvalidateCache(inputData.AssetName);
                }
                else
                {
                    try {
                        // "gamecontent" means loading from game folder. Don't dispose an asset source!
                        Texture2D source;
                        if (inputData.SourcePath.ToLowerInvariant() == "gamecontent")
                        {
                            // ATTENTION: Game content requires special attention because the loaded assets modify themselves over time:
                            // Game content contains vanilla assets only when game is loaded, otherwise the assets are already patched.
                            // Luma desaturation and recoloring don't change brightness so they can be applied multiple times
                            // without bad effects but changing brightness multiple times changes colors over time.
                            // A way to prevent that is caching them in files once and using the cached versions as a base for modifications.
                            string generatedBasePath         = Path.Combine("generated", $"{inputData.AssetName}_gamecontent.png");
                            string generatedBasePathAbsolute = Path.Combine(contentPack.DirectoryPath, generatedBasePath);
                            Directory.CreateDirectory(Path.GetDirectoryName(generatedBasePathAbsolute));

                            if (File.Exists(generatedBasePathAbsolute))
                            {
                                using (FileStream fs = new FileStream(generatedBasePathAbsolute, FileMode.Open)) {
                                    source = Texture2D.FromStream(Game1.graphics.GraphicsDevice, fs);
                                }
                                monitor_.Log($"Loading asset {inputData.AssetName} from existing cache file {generatedBasePathAbsolute}");
                            }
                            else
                            {
                                source = helper_.Content.Load <Texture2D>(inputData.AssetName, ContentSource.GameContent);
                                using (FileStream fs = new FileStream(generatedBasePathAbsolute, FileMode.Create)) {
                                    source.SaveAsPng(fs, source.Width, source.Height);
                                    fs.Close();
                                }
                                monitor_.Log($"Saving asset {inputData.AssetName} in cache file {generatedBasePathAbsolute}");
                            }
                        }
                        else
                        {
                            source = contentPack.LoadAsset <Texture2D>(inputData.SourcePath);
                        }

                        Texture2D mask = inputData.MaskPath.ToLowerInvariant() != "none"
                                       ? contentPack.LoadAsset <Texture2D>(inputData.MaskPath)
                                       : null;

                        using (Texture2D extracted = ExtractSubImage(source, mask, inputData.DesaturationMode, inputData.Brightness))
                            using (Texture2D blended = ColorBlend(extracted, inputData.BlendColor))
                                using (Texture2D flipped = FlipImage(source, blended, inputData.FlipMode)) {
                                    Directory.CreateDirectory(Path.GetDirectoryName(generatedFilePathAbsolute));
                                    using (FileStream fs = new FileStream(generatedFilePathAbsolute, FileMode.Create)) {
                                        flipped.SaveAsPng(fs, flipped.Width, flipped.Height);
                                        fs.Close();
                                    }
                                }

                        monitor_.Log($"Generated file {generatedFilePathAbsolute}, returning relative path {generatedFilePath}");

                        helper_.Content.InvalidateCache(inputData.AssetName);
                    }
                    catch (ContentLoadException) {
                        // Asset is not available, return its name to prevent game from crashing.
                        monitor_.Log($"Ignoring unavailable asset {inputData.AssetName}. If this was caused by patch reload you can ignore it, the next 10min update cycle should do a proper reload.", LogLevel.Info);

                        generatedFilePath = inputData.AssetName;
                    }
                }
            }

            yield return(generatedFilePath);
        }