diff options
Diffstat (limited to 'src')
39 files changed, 315 insertions, 115 deletions
diff --git a/src/SMAPI.Installer/InteractiveInstaller.cs b/src/SMAPI.Installer/InteractiveInstaller.cs index fd1a6047..d00a5df4 100644 --- a/src/SMAPI.Installer/InteractiveInstaller.cs +++ b/src/SMAPI.Installer/InteractiveInstaller.cs @@ -206,7 +206,7 @@ namespace StardewModdingApi.Installer Console.WriteLine(); // handle choice - string choice = this.InteractivelyChoose("Type 1 or 2, then press enter.", new[] { "1", "2" }); + string choice = this.InteractivelyChoose("Type 1 or 2, then press enter.", new[] { "1", "2" }, printLine: Console.WriteLine); switch (choice) { case "1": @@ -629,22 +629,22 @@ namespace StardewModdingApi.Installer } /// <summary>Interactively ask the user to choose a value.</summary> - /// <param name="print">A callback which prints a message to the console.</param> + /// <param name="printLine">A callback which prints a message to the console.</param> /// <param name="message">The message to print.</param> /// <param name="options">The allowed options (not case sensitive).</param> /// <param name="indent">The indentation to prefix to output.</param> - private string InteractivelyChoose(string message, string[] options, string indent = "", Action<string>? print = null) + private string InteractivelyChoose(string message, string[] options, string indent = "", Action<string>? printLine = null) { - print ??= this.PrintInfo; + printLine ??= this.PrintInfo; while (true) { - print(indent + message); + printLine(indent + message); Console.Write(indent); string? input = Console.ReadLine()?.Trim().ToLowerInvariant(); if (input == null || !options.Contains(input)) { - print($"{indent}That's not a valid option."); + printLine($"{indent}That's not a valid option."); continue; } return input; diff --git a/src/SMAPI.Installer/assets/unix-launcher.sh b/src/SMAPI.Installer/assets/unix-launcher.sh index ae9624e7..778663d7 100644 --- a/src/SMAPI.Installer/assets/unix-launcher.sh +++ b/src/SMAPI.Installer/assets/unix-launcher.sh @@ -54,12 +54,12 @@ if [ "$(uname)" == "Darwin" ]; then # https://stackoverflow.com/a/29511052/262123 if [ "$USE_CURRENT_SHELL" == "false" ]; then echo "Reopening in the Terminal app..." - echo '#!/bin/sh' > /tmp/open-smapi-terminal.sh - echo "\"$0\" $@ --use-current-shell" >> /tmp/open-smapi-terminal.sh - chmod +x /tmp/open-smapi-terminal.sh - cat /tmp/open-smapi-terminal.sh - open -W -a Terminal /tmp/open-smapi-terminal.sh - rm /tmp/open-smapi-terminal.sh + echo '#!/bin/sh' > /tmp/open-smapi-terminal.command + echo "\"$0\" $@ --use-current-shell" >> /tmp/open-smapi-terminal.command + chmod +x /tmp/open-smapi-terminal.command + cat /tmp/open-smapi-terminal.command + open -W /tmp/open-smapi-terminal.command + rm /tmp/open-smapi-terminal.command exit 0 fi fi @@ -71,8 +71,8 @@ fi ########## # script must be run from the game folder if [ ! -f "Stardew Valley.dll" ]; then - echo "Oops! SMAPI must be placed in the Stardew Valley game folder.\nSee instructions: https://stardewvalleywiki.com/Modding:Player_Guide"; - read + printf "Oops! SMAPI must be placed in the Stardew Valley game folder.\nSee instructions: https://stardewvalleywiki.com/Modding:Player_Guide"; + read -r exit 1 fi @@ -102,37 +102,39 @@ else # find the true shell behind x-terminal-emulator if [ "$TERMINAL_NAME" = "x-terminal-emulator" ]; then - export TERMINAL_NAME="$(basename "$(readlink -f $(command -v x-terminal-emulator))")" + TERMINAL_NAME="$(basename "$(readlink -f "$(command -v x-terminal-emulator)")")" + export TERMINAL_NAME fi # run in selected terminal and account for quirks - export TERMINAL_PATH="$(command -v $TERMINAL_NAME)" - if [ -x $TERMINAL_PATH ]; then + TERMINAL_PATH="$(command -v "$TERMINAL_NAME")" + export TERMINAL_PATH + if [ -x "$TERMINAL_PATH" ]; then case $TERMINAL_NAME in terminal|termite) # consumes only one argument after -e # options containing space characters are unsupported - exec $TERMINAL_NAME -e "env TERM=xterm $LAUNCH_FILE $@" + exec "$TERMINAL_NAME" -e "env TERM=xterm $LAUNCH_FILE $@" ;; xterm|konsole|alacritty) # consumes all arguments after -e - exec $TERMINAL_NAME -e env TERM=xterm $LAUNCH_FILE "$@" + exec "$TERMINAL_NAME" -e env TERM=xterm $LAUNCH_FILE "$@" ;; terminator|xfce4-terminal|mate-terminal) # consumes all arguments after -x - exec $TERMINAL_NAME -x env TERM=xterm $LAUNCH_FILE "$@" + exec "$TERMINAL_NAME" -x env TERM=xterm $LAUNCH_FILE "$@" ;; gnome-terminal) # consumes all arguments after -- - exec $TERMINAL_NAME -- env TERM=xterm $LAUNCH_FILE "$@" + exec "$TERMINAL_NAME" -- env TERM=xterm $LAUNCH_FILE "$@" ;; kitty) # consumes all trailing arguments - exec $TERMINAL_NAME env TERM=xterm $LAUNCH_FILE "$@" + exec "$TERMINAL_NAME" env TERM=xterm $LAUNCH_FILE "$@" ;; *) diff --git a/src/SMAPI.ModBuildConfig/build/smapi.targets b/src/SMAPI.ModBuildConfig/build/smapi.targets index b66ec27b..12619439 100644 --- a/src/SMAPI.ModBuildConfig/build/smapi.targets +++ b/src/SMAPI.ModBuildConfig/build/smapi.targets @@ -8,8 +8,7 @@ ** Set build options **********************************************--> <PropertyGroup> - <!-- include PDB file by default to enable line numbers in stack traces --> - <DebugType>pdbonly</DebugType> + <!-- enable line numbers in stack traces --> <DebugSymbols>true</DebugSymbols> <!-- don't create the 'refs' folder (which isn't useful for mods) --> diff --git a/src/SMAPI.Mods.ConsoleCommands/manifest.json b/src/SMAPI.Mods.ConsoleCommands/manifest.json index 3c2dec19..adc45ec3 100644 --- a/src/SMAPI.Mods.ConsoleCommands/manifest.json +++ b/src/SMAPI.Mods.ConsoleCommands/manifest.json @@ -1,9 +1,9 @@ { "Name": "Console Commands", "Author": "SMAPI", - "Version": "3.15.1", + "Version": "3.16.0", "Description": "Adds SMAPI console commands that let you manipulate the game.", "UniqueID": "SMAPI.ConsoleCommands", "EntryDll": "ConsoleCommands.dll", - "MinimumApiVersion": "3.15.1" + "MinimumApiVersion": "3.16.0" } diff --git a/src/SMAPI.Mods.ErrorHandler/manifest.json b/src/SMAPI.Mods.ErrorHandler/manifest.json index 28b4b149..3a3c9283 100644 --- a/src/SMAPI.Mods.ErrorHandler/manifest.json +++ b/src/SMAPI.Mods.ErrorHandler/manifest.json @@ -1,9 +1,9 @@ { "Name": "Error Handler", "Author": "SMAPI", - "Version": "3.15.1", + "Version": "3.16.0", "Description": "Handles some common vanilla errors to log more useful info or avoid breaking the game.", "UniqueID": "SMAPI.ErrorHandler", "EntryDll": "ErrorHandler.dll", - "MinimumApiVersion": "3.15.1" + "MinimumApiVersion": "3.16.0" } diff --git a/src/SMAPI.Mods.SaveBackup/manifest.json b/src/SMAPI.Mods.SaveBackup/manifest.json index 1944575b..eb98aa32 100644 --- a/src/SMAPI.Mods.SaveBackup/manifest.json +++ b/src/SMAPI.Mods.SaveBackup/manifest.json @@ -1,9 +1,9 @@ { "Name": "Save Backup", "Author": "SMAPI", - "Version": "3.15.1", + "Version": "3.16.0", "Description": "Automatically backs up all your saves once per day into its folder.", "UniqueID": "SMAPI.SaveBackup", "EntryDll": "SaveBackup.dll", - "MinimumApiVersion": "3.15.1" + "MinimumApiVersion": "3.16.0" } diff --git a/src/SMAPI.Web/Controllers/LogParserController.cs b/src/SMAPI.Web/Controllers/LogParserController.cs index 33af5a81..a3bcf4c3 100644 --- a/src/SMAPI.Web/Controllers/LogParserController.cs +++ b/src/SMAPI.Web/Controllers/LogParserController.cs @@ -1,7 +1,9 @@ using System; -using System.Linq; +using System.Collections.Specialized; +using System.IO; using System.Text; using System.Threading.Tasks; +using System.Web; using Microsoft.AspNetCore.Mvc; using StardewModdingAPI.Toolkit.Utilities; using StardewModdingAPI.Web.Framework; @@ -87,9 +89,15 @@ namespace StardewModdingAPI.Web.Controllers public async Task<ActionResult> PostAsync() { // get raw log text - string? input = this.Request.Form["input"].FirstOrDefault(); - if (string.IsNullOrWhiteSpace(input)) - return this.View("Index", this.GetModel(null, uploadError: "The log file seems to be empty.")); + // note: avoid this.Request.Form, which fails if any mod logged a null character. + string? input; + { + using StreamReader reader = new StreamReader(this.Request.Body); + NameValueCollection parsed = HttpUtility.ParseQueryString(await reader.ReadToEndAsync()); + input = parsed["input"]; + if (string.IsNullOrWhiteSpace(input)) + return this.View("Index", this.GetModel(null, uploadError: "The log file seems to be empty.")); + } // upload log UploadResult uploadResult = await this.Storage.SaveAsync(input); diff --git a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs index 0efa62c5..18ba754c 100644 --- a/src/SMAPI.Web/Framework/LogParsing/LogParser.cs +++ b/src/SMAPI.Web/Framework/LogParsing/LogParser.cs @@ -36,13 +36,13 @@ namespace StardewModdingAPI.Web.Framework.LogParsing private readonly Regex ContentPackListStartPattern = new(@"^Loaded \d+ content packs:$", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// <summary>A regex pattern matching an entry in SMAPI's content pack list.</summary> - private readonly Regex ContentPackListEntryPattern = new(@"^ (?<name>.+?) (?<version>[^\s]+)(?: by (?<author>[^\|]+))? \| for (?<for>[^\|]+)(?: \| (?<description>.+))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private readonly Regex ContentPackListEntryPattern = new(@"^ (?<name>.+?) (?<version>[^\s]+)(?: by (?<author>[^\|]+))? \| for (?<for>[^\|]*)(?: \| (?<description>.+))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// <summary>A regex pattern matching the start of SMAPI's mod update list.</summary> private readonly Regex ModUpdateListStartPattern = new(@"^You can update \d+ mods?:$", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// <summary>A regex pattern matching an entry in SMAPI's mod update list.</summary> - private readonly Regex ModUpdateListEntryPattern = new(@"^ (?<name>.+) (?<version>[^\s]+): (?<link>.+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private readonly Regex ModUpdateListEntryPattern = new(@"^ (?<name>.+) (?<version>[^\s]+): (?<link>[^\s]+)(?: \(you have [^\)]+\))?$", RegexOptions.Compiled | RegexOptions.IgnoreCase); /// <summary>A regex pattern matching SMAPI's update line.</summary> private readonly Regex SmapiUpdatePattern = new(@"^You can update SMAPI to (?<version>[^\s]+): (?<link>.+)$", RegexOptions.Compiled | RegexOptions.IgnoreCase); @@ -77,8 +77,8 @@ namespace StardewModdingAPI.Web.Framework.LogParsing }; // parse log messages - LogModInfo smapiMod = new(name: "SMAPI", author: "Pathoschild", version: "", description: "", loaded: true, isMod: false); - LogModInfo gameMod = new(name: "game", author: "", version: "", description: "", loaded: true, isMod: false); + LogModInfo smapiMod = new(ModType.Special, name: "SMAPI", author: "Pathoschild", version: "", description: "", loaded: true); + LogModInfo gameMod = new(ModType.Special, name: "game", author: "", version: "", description: "", loaded: true); IDictionary<string, List<LogModInfo>> mods = new Dictionary<string, List<LogModInfo>>(); bool inModList = false; bool inContentPackList = false; @@ -133,7 +133,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing if (!mods.TryGetValue(name, out List<LogModInfo>? entries)) mods[name] = entries = new List<LogModInfo>(); - entries.Add(new LogModInfo(name: name, author: author, version: version, description: description, loaded: true)); + entries.Add(new LogModInfo(ModType.CodeMod, name: name, author: author, version: version, description: description, loaded: true)); message.Section = LogSection.ModsList; } @@ -156,7 +156,7 @@ namespace StardewModdingAPI.Web.Framework.LogParsing if (!mods.TryGetValue(name, out List<LogModInfo>? entries)) mods[name] = entries = new List<LogModInfo>(); - entries.Add(new LogModInfo(name: name, author: author, version: version, description: description, contentPackFor: forMod, loaded: true)); + entries.Add(new LogModInfo(ModType.ContentPack, name: name, author: author, version: version, description: description, contentPackFor: forMod, loaded: true)); message.Section = LogSection.ContentPackList; } diff --git a/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs b/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs index 557f08ff..c81942e4 100644 --- a/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs +++ b/src/SMAPI.Web/Framework/LogParsing/Models/LogModInfo.cs @@ -48,21 +48,24 @@ namespace StardewModdingAPI.Web.Framework.LogParsing.Models [MemberNotNullWhen(true, nameof(LogModInfo.UpdateVersion), nameof(LogModInfo.UpdateLink))] public bool HasUpdate => this.UpdateVersion != null && this.Version != this.UpdateVersion; + /// <summary>The mod type.</summary> + public ModType ModType { get; } + /// <summary>Whether this is an actual mod (rather than a special entry for SMAPI or the game itself).</summary> - public bool IsMod { get; } + public bool IsMod => this.ModType != ModType.Special; /// <summary>Whether this is a C# code mod.</summary> - public bool IsCodeMod { get; } + public bool IsCodeMod => this.ModType == ModType.CodeMod; /// <summary>Whether this is a content pack for another mod.</summary> - [MemberNotNullWhen(true, nameof(LogModInfo.ContentPackFor))] - public bool IsContentPack { get; } + public bool IsContentPack => this.ModType == ModType.ContentPack; /********* ** Public methods *********/ /// <summary>Construct an instance.</summary> + /// <param name="modType">The mod type.</param> /// <param name="name">The mod name.</param> /// <param name="author">The mod author.</param> /// <param name="version">The mod version.</param> @@ -72,9 +75,9 @@ namespace StardewModdingAPI.Web.Framework.LogParsing.Models /// <param name="contentPackFor">The name of the mod for which this is a content pack (if applicable).</param> /// <param name="errors">The number of errors logged by this mod.</param> /// <param name="loaded">Whether the mod was loaded into the game.</param> - /// <param name="isMod">Whether this is an actual mod (instead of a special entry for SMAPI or the game).</param> - public LogModInfo(string name, string author, string version, string description, string? updateVersion = null, string? updateLink = null, string? contentPackFor = null, int errors = 0, bool loaded = true, bool isMod = true) + public LogModInfo(ModType modType, string name, string author, string version, string description, string? updateVersion = null, string? updateLink = null, string? contentPackFor = null, int errors = 0, bool loaded = true) { + this.ModType = modType; this.Name = name; this.Author = author; this.Description = description; @@ -84,13 +87,6 @@ namespace StardewModdingAPI.Web.Framework.LogParsing.Models this.Errors = errors; this.Loaded = loaded; - if (isMod) - { - this.IsMod = true; - this.IsContentPack = !string.IsNullOrWhiteSpace(this.ContentPackFor); - this.IsCodeMod = !this.IsContentPack; - } - this.OverrideVersion(version); } diff --git a/src/SMAPI.Web/Framework/LogParsing/Models/ModType.cs b/src/SMAPI.Web/Framework/LogParsing/Models/ModType.cs new file mode 100644 index 00000000..363aaaec --- /dev/null +++ b/src/SMAPI.Web/Framework/LogParsing/Models/ModType.cs @@ -0,0 +1,15 @@ +namespace StardewModdingAPI.Web.Framework.LogParsing.Models +{ + /// <summary>The type for a <see cref="LogModInfo"/> instance.</summary> + public enum ModType + { + /// <summary>A special non-mod entry (e.g. for SMAPI or the game itself).</summary> + Special, + + /// <summary>A C# mod.</summary> + CodeMod, + + /// <summary>A content pack loaded by a C# mod.</summary> + ContentPack + } +} diff --git a/src/SMAPI.Web/SMAPI.Web.csproj b/src/SMAPI.Web/SMAPI.Web.csproj index 0a460e9e..d26cb6f8 100644 --- a/src/SMAPI.Web/SMAPI.Web.csproj +++ b/src/SMAPI.Web/SMAPI.Web.csproj @@ -15,7 +15,7 @@ </ItemGroup> <ItemGroup> - <PackageReference Include="Azure.Storage.Blobs" Version="12.12.0" /> + <PackageReference Include="Azure.Storage.Blobs" Version="12.13.0" /> <PackageReference Include="Hangfire.AspNetCore" Version="1.7.29" /> <PackageReference Include="Hangfire.MemoryStorage" Version="1.7.0" /> <PackageReference Include="HtmlAgilityPack" Version="1.11.43" /> diff --git a/src/SMAPI.Web/ViewModels/LogParserModel.cs b/src/SMAPI.Web/ViewModels/LogParserModel.cs index c39a9b0a..34d0b4df 100644 --- a/src/SMAPI.Web/ViewModels/LogParserModel.cs +++ b/src/SMAPI.Web/ViewModels/LogParserModel.cs @@ -107,7 +107,7 @@ namespace StardewModdingAPI.Web.ViewModels // group by mod return mods .Where(mod => mod.IsContentPack) - .GroupBy(mod => mod.ContentPackFor!) + .GroupBy(mod => mod.ContentPackFor ?? "") .ToDictionary(group => group.Key, group => group.ToArray()); } diff --git a/src/SMAPI.Web/Views/Index/Index.cshtml b/src/SMAPI.Web/Views/Index/Index.cshtml index acb8df78..d6472fcb 100644 --- a/src/SMAPI.Web/Views/Index/Index.cshtml +++ b/src/SMAPI.Web/Views/Index/Index.cshtml @@ -10,12 +10,12 @@ @section Head { <link rel="stylesheet" href="~/Content/css/index.css" /> <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1" crossorigin="anonymous"></script> - <script src="~/Content/js/index.js"></script> + <script src="~/Content/js/index.js?r=20220708"></script> } <h1> SMAPI - <img id="pufferchick" src="Content/images/pufferchick.png" /> + <img id="pufferchick" src="Content/images/pufferchick.svg" /> </h1> <div id="blurb"> <p>The mod loader for Stardew Valley.</p> diff --git a/src/SMAPI.Web/Views/LogParser/Index.cshtml b/src/SMAPI.Web/Views/LogParser/Index.cshtml index b824b768..f71c6ac1 100644 --- a/src/SMAPI.Web/Views/LogParser/Index.cshtml +++ b/src/SMAPI.Web/Views/LogParser/Index.cshtml @@ -17,6 +17,7 @@ LogModInfo[] outdatedMods = log?.Mods.Where(mod => mod.HasUpdate).ToArray() ?? Array.Empty<LogModInfo>(); LogModInfo? errorHandler = log?.Mods.FirstOrDefault(p => p.IsCodeMod && p.Name == "Error Handler"); bool hasOlderErrorHandler = errorHandler?.GetParsedVersion() is not null && log?.ApiVersionParsed is not null && log.ApiVersionParsed.IsNewerThan(errorHandler.GetParsedVersion()); + bool isPyTkCompatibilityMode = log?.ApiVersionParsed?.IsOlderThan("3.15.0") is false && log.Mods.Any(p => p.IsCodeMod && p.Name == "PyTK" && p.GetParsedVersion()?.IsOlderThan("1.23.1") is true); // get filters IDictionary<string, bool> defaultFilters = Enum @@ -242,7 +243,7 @@ else if (log?.IsValid == true) @if (log?.IsValid == true) { <div id="output"> - @if (outdatedMods.Any() || errorHandler is null || hasOlderErrorHandler) + @if (outdatedMods.Any() || errorHandler is null || hasOlderErrorHandler || isPyTkCompatibilityMode) { <h2>Suggested fixes</h2> <ul id="fix-list"> @@ -254,6 +255,10 @@ else if (log?.IsValid == true) { <li>Your <strong>Error Handler</strong> mod is older than SMAPI. You may be missing some game/mod error fixes. You can <a href="https://stardewvalleywiki.com/Modding:Player_Guide#Install_SMAPI">reinstall SMAPI</a> to update it.</li> } + @if (isPyTkCompatibilityMode) + { + <li>PyTK 1.23.0 or earlier isn't compatible with newer SMAPI performance optimizations. This may increase loading times or in-game lag.</li> + } @if (outdatedMods.Any()) { <li> @@ -347,16 +352,31 @@ else if (log?.IsValid == true) <span class="notice btn txt" v-on:click="toggleContentPacks">toggle content packs in list</span> } </caption> - @foreach (var mod in log.Mods.Where(p => p.Loaded && !p.IsContentPack)) - { - if (contentPacks == null || !contentPacks.TryGetValue(mod.Name, out LogModInfo[]? contentPackList)) - contentPackList = null; + @{ + var modsWithContentPacks = log.Mods + .Where(mod => mod.Loaded && !mod.IsContentPack) + .Select(mod => ( + Mod: mod, + ContentPacks: contentPacks?.TryGetValue(mod.Name, out LogModInfo[]? contentPackList) == true ? contentPackList : Array.Empty<LogModInfo>() + )) + .ToList(); + if (contentPacks?.TryGetValue("", out LogModInfo[] invalidPacks) == true) + { + modsWithContentPacks.Add(( + Mod: new LogModInfo(ModType.CodeMod, "<invalid content packs>", "", "", ""), + ContentPacks: invalidPacks + )); + } + } + + @foreach ((LogModInfo mod, LogModInfo[] contentPackList) in modsWithContentPacks) + { <tr v-on:click="toggleMod('@Model.GetSlug(mod.Name)')" class="mod-entry" v-bind:class="{ hidden: !showMods['@Model.GetSlug(mod.Name)'] }"> <td><input type="checkbox" v-bind:checked="showMods['@Model.GetSlug(mod.Name)']" v-bind:class="{ invisible: !anyModsHidden }" /></td> <td> <strong v-pre>@mod.Name</strong> @mod.Version - @if (contentPackList != null) + @if (contentPackList.Any()) { <div v-if="!hideContentPacks" class="content-packs"> @foreach (var contentPack in contentPackList) @@ -369,7 +389,7 @@ else if (log?.IsValid == true) </td> <td> @mod.Author - @if (contentPackList != null) + @if (contentPackList.Any()) { <div v-if="!hideContentPacks" class="content-packs"> @foreach (var contentPack in contentPackList) diff --git a/src/SMAPI.Web/wwwroot/Content/css/main.css b/src/SMAPI.Web/wwwroot/Content/css/main.css index a0a407d8..79de9c39 100644 --- a/src/SMAPI.Web/wwwroot/Content/css/main.css +++ b/src/SMAPI.Web/wwwroot/Content/css/main.css @@ -68,7 +68,7 @@ a { margin-top: 3em; min-height: 75%; width: 12em; - background: url("../images/sidebar-bg.gif") no-repeat top right; + background: url("../images/sidebar-bg.svg") no-repeat top right; color: #666; } diff --git a/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.png b/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.png Binary files differdeleted file mode 100644 index f359146c..00000000 --- a/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.png +++ /dev/null diff --git a/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.svg b/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.svg new file mode 100644 index 00000000..2734c289 --- /dev/null +++ b/src/SMAPI.Web/wwwroot/Content/images/pufferchick-cool.svg @@ -0,0 +1 @@ +<svg width="56" height="56" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M0 17.684h2.947v8.842H0v-8.842Zm29.474 32.421h2.947V56h-2.947v-5.895ZM18.667 29.474h2.947v5.894h-2.947v-5.894Zm16.701 20.631h2.948V56h-2.948v-5.895ZM2.948 14.737h5.894v2.947H2.947v-2.947Zm5.894 2.947h5.895v2.948H8.842v-2.948Zm0 23.58h5.895v2.947H8.842v-2.948ZM23.58 5.893h5.895v2.948H23.58V5.895Z" fill="#6B3407"/><path d="M14.737 41.263h5.895v2.948h-5.895v-2.948Zm29.474 0h5.894v2.948h-5.894v-2.948ZM32.42 23.58h5.895v2.947H32.42V23.58Z" fill="#8F4039"/><path d="M26.526 2.947h5.895v2.948h-5.895V2.947Z" fill="#FF4D1E"/><path d="M32.421 8.842h5.895v2.947H32.42V8.843Z" fill="#8F4039"/><path d="M23.579 47.158h14.737v2.947H23.579v-2.947Zm-1.965-11.79h11.79v2.948h-11.79v-2.948ZM20.632 0h11.79v2.947h-11.79V0Zm0 2.947h2.947v2.948h-2.947V2.947Zm11.79 0h2.946v2.948h-2.947V2.947Zm2.946 2.948h2.948v2.947h-2.948V5.895ZM2.948 26.526h2.947v2.948H2.947v-2.948Z" fill="#6B3407"/><path d="M2.947 23.579h2.948v2.947H2.947V23.58Zm2.948 2.947h2.947v2.948H5.895v-2.948Zm-2.948 5.895h2.948v2.947H2.947v-2.947Zm5.895 5.895h2.947v2.947H8.843v-2.947Zm9.825-2.948h2.947v2.948h-2.947v-2.948Zm14.736-2.947h2.948v2.947h-2.947v-2.947Z" fill="#9A4E0F"/><path d="M5.895 38.316h2.947v2.947H5.895v-2.947Zm15.719-8.842h2.947v2.947h-2.947v-2.947Z" fill="#6B3407"/><path d="M17.684 44.21h2.948v2.948h-2.948V44.21Zm5.895 0h2.947v2.948H23.58V44.21Zm20.632 0h2.947v2.948H44.21V44.21Zm5.894-5.894h2.948v2.947h-2.948v-2.947Zm-2.947-2.948h2.947v2.948h-2.947v-2.948Zm5.895-8.842H56v2.948h-2.947v-2.948Zm-2.948-2.947h2.948v2.947h-2.948V23.58ZM26.526 8.842h2.948v2.947h-2.948V8.843Z" fill="#8F4039"/><path d="M23.579 2.947h2.947v2.948H23.58V2.947Zm8.842 2.948h2.947v2.947h-2.947V5.895Z" fill="#C31E1E"/><path d="M29.474 5.895h2.947v2.947h-2.947V5.895Z" fill="#FF4D1E"/><path d="M23.579 11.79h2.947v2.947H23.58V11.79Z" fill="#8F4039"/><path d="M2.947 17.684h2.948v2.948H2.947v-2.948Z" fill="#FFE2AF"/><path d="M5.895 17.684h2.947v2.948H5.895v-2.948Zm-2.948 2.948h2.948v2.947H2.947v-2.947Zm8.843 0h2.947v2.947H11.79v-2.947Zm12.771 8.842h2.948v2.947H24.56v-2.947Zm8.843 0h2.947v2.947h-2.947v-2.947Zm0 5.894h2.947v2.948h-2.947v-2.948ZM8.841 26.526h2.947v5.895H8.843v-5.895Zm-5.895 2.948h2.948v2.947H2.947v-2.947Zm11.79 5.894h2.947v2.948h-2.947v-2.948Z" fill="#DEA159"/><path d="M14.737 38.316h2.947v2.947h-2.947v-2.947Zm5.895 2.947h2.947v2.948h-2.947v-2.948Zm23.579-8.842h2.947v2.947H44.21v-2.947Zm0-5.895h2.947v2.948H44.21v-2.948Zm-17.685 0h14.737v2.948H26.526v-2.948Zm0 11.79h5.895v2.947h-5.895v-2.947Zm5.895 2.947h8.842v2.948h-8.842v-2.948Zm2.947-2.947h8.843v2.947h-8.843v-2.947Zm2.948-2.948h8.842v2.948h-8.842v-2.948Zm0-11.79h8.842v2.948h-8.842V23.58Zm-2.948-8.841h8.843v2.947h-8.843v-2.947Zm-11.79 0h8.843v2.947H23.58v-2.947Zm0 8.842h8.843v2.947H23.58V23.58Zm5.896-5.895h11.79v5.895h-11.79v-5.895Zm6.877 14.737h4.912v2.947h-4.912v-2.947Zm0-2.947h13.754v2.947H36.351v-2.947ZM26.526 11.79h8.842v2.947h-8.842V11.79Z" fill="#FCD100"/><path d="M20.632 44.21h2.947v2.948h-2.947V44.21Z" fill="#DF8900"/><path d="M20.632 44.21h2.947v2.948h-2.947V44.21Zm20.631-2.947h2.948v2.948h-2.948v-2.948Zm2.948-2.947h2.947v2.947H44.21v-2.947Zm2.947-5.895h2.947v2.947h-2.947v-2.947Zm-5.895 0h2.948v2.947h-2.948v-2.947Zm2.948-14.737h2.947v2.948H44.21v-2.948Zm-8.843-5.895h2.948v2.948h-2.948V11.79Zm-14.736 2.948h2.947v2.947h-2.947v-2.947Zm-2.948 2.947h2.948v2.948h-2.948v- |
