diff --git a/OpenRA.Game/ExternalMods.cs b/OpenRA.Game/ExternalMods.cs index 98bd4b6ed7..f2f5e93971 100644 --- a/OpenRA.Game/ExternalMods.cs +++ b/OpenRA.Game/ExternalMods.cs @@ -16,6 +16,7 @@ using System.IO; using System.Linq; using OpenRA.FileFormats; using OpenRA.Graphics; +using OpenRA.Primitives; namespace OpenRA { @@ -30,6 +31,8 @@ namespace OpenRA public readonly string LaunchPath; public readonly string[] LaunchArgs; public Sprite Icon { get; internal set; } + public Sprite Icon2x { get; internal set; } + public Sprite Icon3x { get; internal set; } public static string MakeKey(Manifest mod) { return MakeKey(mod.Id, mod.Metadata.Version); } public static string MakeKey(ExternalMod mod) { return MakeKey(mod.Id, mod.Version); } @@ -41,9 +44,22 @@ namespace OpenRA readonly Dictionary mods = new Dictionary(); readonly SheetBuilder sheetBuilder; + Sheet CreateSheet() + { + var sheet = new Sheet(SheetType.BGRA, new Size(512, 512)); + + // We must manually force the buffer creation to avoid a crash + // that is indirectly triggered by rendering from a Sheet that + // has not yet been written to. + sheet.CreateBuffer(); + sheet.GetTexture().ScaleFilter = TextureScaleFilter.Linear; + + return sheet; + } + public ExternalMods() { - sheetBuilder = new SheetBuilder(SheetType.BGRA, 256); + sheetBuilder = new SheetBuilder(SheetType.BGRA, CreateSheet); // Several types of support directory types are available, depending on // how the player has installed and launched the game. @@ -80,10 +96,18 @@ namespace OpenRA var mod = FieldLoader.Load(yaml); var iconNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon"); if (iconNode != null && !string.IsNullOrEmpty(iconNode.Value.Value)) - { using (var stream = new MemoryStream(Convert.FromBase64String(iconNode.Value.Value))) mod.Icon = sheetBuilder.Add(new Png(stream)); - } + + var icon2xNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon2x"); + if (icon2xNode != null && !string.IsNullOrEmpty(icon2xNode.Value.Value)) + using (var stream = new MemoryStream(Convert.FromBase64String(icon2xNode.Value.Value))) + mod.Icon2x = sheetBuilder.Add(new Png(stream), 1f / 2); + + var icon3xNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon3x"); + if (icon3xNode != null && !string.IsNullOrEmpty(icon3xNode.Value.Value)) + using (var stream = new MemoryStream(Convert.FromBase64String(icon3xNode.Value.Value))) + mod.Icon3x = sheetBuilder.Add(new Png(stream), 1f / 3); // Avoid possibly overwriting a valid mod with an obviously bogus one var key = ExternalMod.MakeKey(mod); @@ -96,24 +120,27 @@ namespace OpenRA if (mod.Metadata.Hidden) return; - var iconData = ""; + var key = ExternalMod.MakeKey(mod); + var yaml = new MiniYamlNode("Registration", new MiniYaml("", new List() + { + new MiniYamlNode("Id", mod.Id), + new MiniYamlNode("Version", mod.Metadata.Version), + new MiniYamlNode("Title", mod.Metadata.Title), + new MiniYamlNode("LaunchPath", launchPath), + new MiniYamlNode("LaunchArgs", "Game.Mod=" + mod.Id) + })); + using (var stream = mod.Package.GetStream("icon.png")) if (stream != null) - iconData = Convert.ToBase64String(stream.ReadAllBytes()); + yaml.Value.Nodes.Add(new MiniYamlNode("Icon", Convert.ToBase64String(stream.ReadAllBytes()))); - var key = ExternalMod.MakeKey(mod); - var yaml = new List() - { - new MiniYamlNode("Registration", new MiniYaml("", new List() - { - new MiniYamlNode("Id", mod.Id), - new MiniYamlNode("Version", mod.Metadata.Version), - new MiniYamlNode("Title", mod.Metadata.Title), - new MiniYamlNode("Icon", iconData), - new MiniYamlNode("LaunchPath", launchPath), - new MiniYamlNode("LaunchArgs", "Game.Mod=" + mod.Id) - })) - }; + using (var stream = mod.Package.GetStream("icon-2x.png")) + if (stream != null) + yaml.Value.Nodes.Add(new MiniYamlNode("Icon2x", Convert.ToBase64String(stream.ReadAllBytes()))); + + using (var stream = mod.Package.GetStream("icon-3x.png")) + if (stream != null) + yaml.Value.Nodes.Add(new MiniYamlNode("Icon3x", Convert.ToBase64String(stream.ReadAllBytes()))); var sources = new List(); if (registration.HasFlag(ModRegistration.System)) @@ -131,8 +158,9 @@ namespace OpenRA } // Make sure the mod is available for this session, even if saving it fails - LoadMod(yaml.First().Value, forceRegistration: true); + LoadMod(yaml.Value, forceRegistration: true); + var lines = new List { yaml }.ToLines().ToArray(); foreach (var source in sources.Distinct()) { var metadataPath = Path.Combine(source, "ModMetadata"); @@ -140,7 +168,7 @@ namespace OpenRA try { Directory.CreateDirectory(metadataPath); - File.WriteAllLines(Path.Combine(metadataPath, key + ".yaml"), yaml.ToLines().ToArray()); + File.WriteAllLines(Path.Combine(metadataPath, key + ".yaml"), lines); } catch (Exception e) { diff --git a/OpenRA.Mods.Common/Widgets/Logic/ConnectionLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ConnectionLogic.cs index 8aa1072085..0638a04821 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/ConnectionLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/ConnectionLogic.cs @@ -159,9 +159,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic var abortButton = panel.Get("ABORT_BUTTON"); var switchButton = panel.Get("SWITCH_BUTTON"); - var modTitle = orderManager.ServerExternalMod.Title; - var modVersion = orderManager.ServerExternalMod.Version; - var modIcon = orderManager.ServerExternalMod.Icon; + var mod = orderManager.ServerExternalMod; + var modTitle = mod.Title; + var modVersion = mod.Version; switchButton.OnClick = () => { @@ -206,10 +206,22 @@ namespace OpenRA.Mods.Common.Widgets.Logic } var logo = panel.GetOrNull("MOD_ICON"); - if (logo != null && modIcon != null) - logo.GetSprite = () => modIcon; + if (logo != null) + { + logo.GetSprite = () => + { + var ws = Game.Renderer.WindowScale; + if (ws > 2 && mod.Icon3x != null) + return mod.Icon3x; - if (logo != null && modIcon == null) + if (ws > 1 && mod.Icon2x != null) + return mod.Icon2x; + + return mod.Icon; + }; + } + + if (logo != null && mod.Icon == null) { // Hide the logo and center just the text if (title != null) diff --git a/mods/cnc/icon-2x.png b/mods/cnc/icon-2x.png new file mode 100644 index 0000000000..400994d031 Binary files /dev/null and b/mods/cnc/icon-2x.png differ diff --git a/mods/cnc/icon-3x.png b/mods/cnc/icon-3x.png new file mode 100644 index 0000000000..6ec796f337 Binary files /dev/null and b/mods/cnc/icon-3x.png differ diff --git a/mods/d2k/icon-2x.png b/mods/d2k/icon-2x.png new file mode 100755 index 0000000000..5a12762d2b Binary files /dev/null and b/mods/d2k/icon-2x.png differ diff --git a/mods/d2k/icon-3x.png b/mods/d2k/icon-3x.png new file mode 100755 index 0000000000..1e9cf461f0 Binary files /dev/null and b/mods/d2k/icon-3x.png differ diff --git a/mods/ra/icon-2x.png b/mods/ra/icon-2x.png new file mode 100644 index 0000000000..61efe68419 Binary files /dev/null and b/mods/ra/icon-2x.png differ diff --git a/mods/ra/icon-3x.png b/mods/ra/icon-3x.png new file mode 100644 index 0000000000..cd36a71a28 Binary files /dev/null and b/mods/ra/icon-3x.png differ diff --git a/mods/ts/icon-2x.png b/mods/ts/icon-2x.png new file mode 100644 index 0000000000..494214e796 Binary files /dev/null and b/mods/ts/icon-2x.png differ diff --git a/mods/ts/icon-3x.png b/mods/ts/icon-3x.png new file mode 100644 index 0000000000..fa2555c9e6 Binary files /dev/null and b/mods/ts/icon-3x.png differ diff --git a/mods/ts/icon.png b/mods/ts/icon.png index a82496dac3..7a2a75c646 100644 Binary files a/mods/ts/icon.png and b/mods/ts/icon.png differ