From ff7b3541f47088aa91cfba81169b3e8475525411 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 21 Sep 2014 00:18:38 +1200 Subject: [PATCH 1/2] =?UTF-8?q?Remove=20obsolete=20=E2=80=94upgrade-map-v5?= =?UTF-8?q?=20and=20--setings-value=20commands.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- OpenRA.Utility/Command.cs | 25 ------------------------- OpenRA.Utility/Program.cs | 2 -- 2 files changed, 27 deletions(-) diff --git a/OpenRA.Utility/Command.cs b/OpenRA.Utility/Command.cs index 92fa94826e..c013c83123 100644 --- a/OpenRA.Utility/Command.cs +++ b/OpenRA.Utility/Command.cs @@ -34,22 +34,6 @@ namespace OpenRA.Utility yield return path; } - [Desc("KEY", "Get value of KEY from settings.yaml")] - public static void Settings(string[] args) - { - if (args.Length < 2) - { - Console.WriteLine("Error: Invalid syntax"); - return; - } - - var section = args[1].Split('.')[0]; - var field = args[1].Split('.')[1]; - var settings = new Settings(Platform.SupportDir + "settings.yaml", Arguments.Empty); - var result = settings.Sections[section].GetType().GetField(field).GetValue(settings.Sections[section]); - Console.WriteLine(result); - } - [Desc("PNGFILE [PNGFILE ...]", "Combine a list of PNG images into a SHP")] public static void ConvertPngToShp(string[] args) { @@ -550,15 +534,6 @@ namespace OpenRA.Utility Console.WriteLine(dest + " saved."); } - [Desc("MAPFILE", "MOD", "Upgrade a version 5 map to version 6.")] - public static void UpgradeV5Map(string[] args) - { - var map = args[1]; - var mod = args[2]; - Game.modData = new ModData(mod); - new Map(map, mod); - } - [Desc("MOD", "FILENAME", "Convert a legacy INI/MPR map to the OpenRA format.")] public static void ImportLegacyMap(string[] args) { diff --git a/OpenRA.Utility/Program.cs b/OpenRA.Utility/Program.cs index 1a32fe6b78..cf1a765061 100644 --- a/OpenRA.Utility/Program.cs +++ b/OpenRA.Utility/Program.cs @@ -20,7 +20,6 @@ namespace OpenRA.Utility { static readonly Dictionary> Actions = new Dictionary>() { - { "--settings-value", Command.Settings }, { "--shp", Command.ConvertPngToShp }, { "--png", Command.ConvertSpriteToPng }, { "--extract", Command.ExtractFiles }, @@ -30,7 +29,6 @@ namespace OpenRA.Utility { "--lua-docs", Command.ExtractLuaDocs }, { "--map-hash", Command.GetMapHash }, { "--map-preview", Command.GenerateMinimap }, - { "--map-upgrade-v5", Command.UpgradeV5Map }, { "--upgrade-map", UpgradeRules.UpgradeMap }, { "--upgrade-mod", UpgradeRules.UpgradeMod }, { "--map-import", Command.ImportLegacyMap }, From 08890b099469462d27b22c5761863d754738cd05 Mon Sep 17 00:00:00 2001 From: Alexander Fast Date: Wed, 20 Aug 2014 16:15:18 +0200 Subject: [PATCH 2/2] Added IUtilityCommand and move default commands into Mods.Common. --- Makefile | 4 +- OpenRA.Game/IUtilityCommand.cs | 22 + OpenRA.Game/OpenRA.Game.csproj | 1 + OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 16 + .../UtilityCommands/ConvertPngToShpCommand.cs | 66 +++ .../ConvertSpriteToPngCommand.cs | 95 +++ .../UtilityCommands/ExtractFilesCommand.cs | 41 ++ .../ExtractLanguageStringsCommand.cs | 19 +- .../UtilityCommands/ExtractLuaDocsCommand.cs | 175 ++++++ .../ExtractTraitDocsCommand.cs | 151 +++++ .../UtilityCommands/GenerateMinimapCommand.cs | 42 ++ .../UtilityCommands/GetMapHashCommand.cs | 30 + .../UtilityCommands}/Glob.cs | 4 +- .../UtilityCommands/ImportLegacyMapCommand.cs | 35 ++ .../UtilityCommands}/LegacyMapImporter.cs | 4 +- .../UtilityCommands/RemapShpCommand.cs | 85 +++ .../UtilityCommands/TransposeShpCommand.cs | 48 ++ .../UtilityCommands/UpgradeMapCommand.cs | 36 ++ .../UtilityCommands/UpgradeModCommand.cs | 79 +++ .../UtilityCommands}/UpgradeRules.cs | 89 +-- OpenRA.Mods.RA/OpenRA.Mods.RA.csproj | 1 + OpenRA.Utility/Command.cs | 550 ------------------ OpenRA.Utility/OpenRA.Utility.csproj | 5 - OpenRA.Utility/Program.cs | 53 +- packaging/update-wiki.sh | 5 +- 25 files changed, 982 insertions(+), 674 deletions(-) create mode 100644 OpenRA.Game/IUtilityCommand.cs create mode 100644 OpenRA.Mods.Common/UtilityCommands/ConvertPngToShpCommand.cs create mode 100644 OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs create mode 100644 OpenRA.Mods.Common/UtilityCommands/ExtractFilesCommand.cs rename OpenRA.Utility/ExtractLanguageStrings.cs => OpenRA.Mods.Common/UtilityCommands/ExtractLanguageStringsCommand.cs (75%) create mode 100644 OpenRA.Mods.Common/UtilityCommands/ExtractLuaDocsCommand.cs create mode 100644 OpenRA.Mods.Common/UtilityCommands/ExtractTraitDocsCommand.cs create mode 100644 OpenRA.Mods.Common/UtilityCommands/GenerateMinimapCommand.cs create mode 100644 OpenRA.Mods.Common/UtilityCommands/GetMapHashCommand.cs rename {OpenRA.Utility => OpenRA.Mods.Common/UtilityCommands}/Glob.cs (98%) create mode 100644 OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs rename {OpenRA.Utility => OpenRA.Mods.Common/UtilityCommands}/LegacyMapImporter.cs (99%) create mode 100644 OpenRA.Mods.Common/UtilityCommands/RemapShpCommand.cs create mode 100644 OpenRA.Mods.Common/UtilityCommands/TransposeShpCommand.cs create mode 100644 OpenRA.Mods.Common/UtilityCommands/UpgradeMapCommand.cs create mode 100644 OpenRA.Mods.Common/UtilityCommands/UpgradeModCommand.cs rename {OpenRA.Utility => OpenRA.Mods.Common/UtilityCommands}/UpgradeRules.cs (91%) delete mode 100644 OpenRA.Utility/Command.cs diff --git a/Makefile b/Makefile index 928766f564..4ddf7b1920 100644 --- a/Makefile +++ b/Makefile @@ -302,8 +302,8 @@ version: mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml mods/modchooser/mo # Documentation (d2k depends on all mod libraries) docs: utility - @mono --debug OpenRA.Utility.exe --docs d2k > DOCUMENTATION.md - @mono --debug OpenRA.Utility.exe --lua-docs ra > Lua-API.md + @mono --debug OpenRA.Utility.exe d2k --docs > DOCUMENTATION.md + @mono --debug OpenRA.Utility.exe ra --lua-docs > Lua-API.md install: install-core diff --git a/OpenRA.Game/IUtilityCommand.cs b/OpenRA.Game/IUtilityCommand.cs new file mode 100644 index 0000000000..0ed29f5ed3 --- /dev/null +++ b/OpenRA.Game/IUtilityCommand.cs @@ -0,0 +1,22 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +namespace OpenRA +{ + public interface IUtilityCommand + { + /// + /// The string used to invoke the command. + /// + string Name { get; } + + void Run(ModData modData, string[] args); + } +} diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index e34a8dba7e..e675c54864 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -109,6 +109,7 @@ + diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 84e3b2a0d3..b78d86ee5f 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -38,6 +38,22 @@ + + + + + + + + + + + + + + + + diff --git a/OpenRA.Mods.Common/UtilityCommands/ConvertPngToShpCommand.cs b/OpenRA.Mods.Common/UtilityCommands/ConvertPngToShpCommand.cs new file mode 100644 index 0000000000..4cef69cec1 --- /dev/null +++ b/OpenRA.Mods.Common/UtilityCommands/ConvertPngToShpCommand.cs @@ -0,0 +1,66 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using OpenRA.FileFormats; + +namespace OpenRA.Mods.Common.UtilityCommands +{ + class ConvertPngToShpCommand : IUtilityCommand + { + public string Name { get { return "--shp"; } } + + [Desc("PNGFILE [PNGFILE ...]", "Combine a list of PNG images into a SHP")] + public void Run(ModData modData, string[] args) + { + var inputFiles = GlobArgs(args).OrderBy(a => a).ToList(); + var dest = inputFiles[0].Split('-').First() + ".shp"; + var frames = inputFiles.Select(a => PngLoader.Load(a)); + + var size = frames.First().Size; + if (frames.Any(f => f.Size != size)) + throw new InvalidOperationException("All frames must be the same size"); + + using (var destStream = File.Create(dest)) + ShpReader.Write(destStream, size, frames.Select(f => ToBytes(f))); + + Console.WriteLine(dest + " saved."); + } + + static IEnumerable GlobArgs(string[] args, int startIndex = 1) + { + for (var i = startIndex; i < args.Length; i++) + foreach (var path in Glob.Expand(args[i])) + yield return path; + } + + static byte[] ToBytes(Bitmap bitmap) + { + var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, + PixelFormat.Format8bppIndexed); + + var bytes = new byte[bitmap.Width * bitmap.Height]; + for (var i = 0; i < bitmap.Height; i++) + Marshal.Copy(new IntPtr(data.Scan0.ToInt64() + i * data.Stride), + bytes, i * bitmap.Width, bitmap.Width); + + bitmap.UnlockBits(data); + + return bytes; + } + } +} diff --git a/OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs b/OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs new file mode 100644 index 0000000000..16fac878aa --- /dev/null +++ b/OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs @@ -0,0 +1,95 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using OpenRA.FileFormats; +using OpenRA.Graphics; + +namespace OpenRA.Mods.Common.UtilityCommands +{ + class ConvertSpriteToPngCommand : IUtilityCommand + { + public string Name { get { return "--png"; } } + + [Desc("SPRITEFILE PALETTE [--noshadow] [--nopadding]", + "Convert a shp/tmp/R8 to a series of PNGs, optionally removing shadow")] + public void Run(ModData modData, string[] args) + { + var src = args[1]; + var shadowIndex = new int[] { }; + if (args.Contains("--noshadow")) + { + Array.Resize(ref shadowIndex, shadowIndex.Length + 3); + shadowIndex[shadowIndex.Length - 1] = 1; + shadowIndex[shadowIndex.Length - 2] = 3; + shadowIndex[shadowIndex.Length - 3] = 4; + } + + var palette = new ImmutablePalette(args[2], shadowIndex); + + ISpriteSource source; + using (var stream = File.OpenRead(src)) + source = SpriteSource.LoadSpriteSource(stream, src); + + // The r8 padding requires external information that we can't access here. + var usePadding = !(args.Contains("--nopadding") || source is R8Reader); + var count = 0; + var prefix = Path.GetFileNameWithoutExtension(src); + + foreach (var frame in source.Frames) + { + var frameSize = usePadding ? frame.FrameSize : frame.Size; + var offset = usePadding ? (frame.Offset - 0.5f * new float2(frame.Size - frame.FrameSize)).ToInt2() : int2.Zero; + + // shp(ts) may define empty frames + if (frameSize.Width == 0 && frameSize.Height == 0) + { + count++; + continue; + } + + using (var bitmap = new Bitmap(frameSize.Width, frameSize.Height, PixelFormat.Format8bppIndexed)) + { + bitmap.Palette = palette.AsSystemPalette(); + var data = bitmap.LockBits(new Rectangle(0, 0, frameSize.Width, frameSize.Height), + ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); + + // Clear the frame + if (usePadding) + { + var clearRow = new byte[data.Stride]; + for (var i = 0; i < frameSize.Height; i++) + Marshal.Copy(clearRow, 0, new IntPtr(data.Scan0.ToInt64() + i * data.Stride), data.Stride); + } + + for (var i = 0; i < frame.Size.Height; i++) + { + var destIndex = new IntPtr(data.Scan0.ToInt64() + (i + offset.Y) * data.Stride + offset.X); + Marshal.Copy(frame.Data, i * frame.Size.Width, destIndex, frame.Size.Width); + } + + bitmap.UnlockBits(data); + + var filename = "{0}-{1:D4}.png".F(prefix, count++); + bitmap.Save(filename); + } + } + + Console.WriteLine("Saved {0}-[0..{1}].png", prefix, count - 1); + } + } +} diff --git a/OpenRA.Mods.Common/UtilityCommands/ExtractFilesCommand.cs b/OpenRA.Mods.Common/UtilityCommands/ExtractFilesCommand.cs new file mode 100644 index 0000000000..306fc24752 --- /dev/null +++ b/OpenRA.Mods.Common/UtilityCommands/ExtractFilesCommand.cs @@ -0,0 +1,41 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using OpenRA.FileSystem; + +namespace OpenRA.Mods.Common.UtilityCommands +{ + class ExtractFilesCommand : IUtilityCommand + { + public string Name { get { return "--extract"; } } + + [Desc("Extract files from mod packages to the current directory")] + public void Run(ModData modData, string[] args) + { + var files = args.Skip(1); + GlobalFileSystem.LoadFromManifest(modData.Manifest); + + foreach (var f in files) + { + var src = GlobalFileSystem.Open(f); + if (src == null) + throw new InvalidOperationException("File not found: {0}".F(f)); + var data = src.ReadAllBytes(); + File.WriteAllBytes(f, data); + Console.WriteLine(f + " saved."); + } + } + } +} diff --git a/OpenRA.Utility/ExtractLanguageStrings.cs b/OpenRA.Mods.Common/UtilityCommands/ExtractLanguageStringsCommand.cs similarity index 75% rename from OpenRA.Utility/ExtractLanguageStrings.cs rename to OpenRA.Mods.Common/UtilityCommands/ExtractLanguageStringsCommand.cs index 65e8618c01..daa2c6b5c4 100644 --- a/OpenRA.Utility/ExtractLanguageStrings.cs +++ b/OpenRA.Mods.Common/UtilityCommands/ExtractLanguageStringsCommand.cs @@ -12,16 +12,19 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; -namespace OpenRA.Utility +namespace OpenRA.Mods.Common.UtilityCommands { - public class ExtractLanguageStrings + class ExtractLanguageStringsCommand : IUtilityCommand { - [Desc("MOD", "Extract translatable strings that are not yet localized and update chrome layout.")] - public static void FromMod(string[] args) + public string Name { get { return "--extract-language-strings"; } } + + [Desc("Extract translatable strings that are not yet localized and update chrome layout.")] + public void Run(ModData modData, string[] args) { - var mod = args[1]; - Game.modData = new ModData(mod); + // HACK: The engine code assumes that Game.modData is set. + Game.modData = modData; Game.modData.RulesetCache.LoadDefaultRules(); var types = Game.modData.ObjectCreator.GetTypes(); @@ -32,7 +35,7 @@ namespace OpenRA.Utility { Console.WriteLine("# {0}:", filename); var yaml = MiniYaml.FromFile(filename); - ExtractLanguageStrings.FromChromeLayout(ref yaml, null, + ExtractLanguageStringsCommand.FromChromeLayout(ref yaml, null, translatableFields.Select(t => t.Name).Distinct(), null); using (var file = new StreamWriter(filename)) file.WriteLine(yaml.WriteToString()); @@ -41,7 +44,7 @@ namespace OpenRA.Utility // TODO: Properties can also be translated. } - public static void FromChromeLayout(ref List nodes, MiniYamlNode parent, IEnumerable translatables, string container) + internal static void FromChromeLayout(ref List nodes, MiniYamlNode parent, IEnumerable translatables, string container) { var parentNode = parent != null ? parent.Key.Split('@') : null; var parentType = parent != null ? parentNode.First() : null; diff --git a/OpenRA.Mods.Common/UtilityCommands/ExtractLuaDocsCommand.cs b/OpenRA.Mods.Common/UtilityCommands/ExtractLuaDocsCommand.cs new file mode 100644 index 0000000000..18bf9732eb --- /dev/null +++ b/OpenRA.Mods.Common/UtilityCommands/ExtractLuaDocsCommand.cs @@ -0,0 +1,175 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using OpenRA.Scripting; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.UtilityCommands +{ + class ExtractLuaDocsCommand : IUtilityCommand + { + public string Name { get { return "--lua-docs"; } } + + [Desc("Generate Lua API documentation in MarkDown format.")] + public void Run(ModData modData, string[] args) + { + // HACK: The engine code assumes that Game.modData is set. + Game.modData = modData; + + Console.WriteLine("This is an automatically generated lising of the new Lua map scripting API, generated for {0} of OpenRA.", Game.modData.Manifest.Mod.Version); + Console.WriteLine(); + Console.WriteLine("OpenRA allows custom maps and missions to be scripted using Lua 5.1.\n" + + "These scripts run in a sandbox that prevents access to unsafe functions (e.g. OS or file access), " + + "and limits the memory and CPU usage of the scripts."); + Console.WriteLine(); + Console.WriteLine("You can access this interface by adding the [LuaScript](Traits#luascript) trait to the world actor in your map rules (note, you must replace the spaces in the snippet below with a single tab for each level of indentation):"); + Console.WriteLine("```\nRules:\n\tWorld:\n\t\tLuaScript:\n\t\t\tScripts: myscript.lua\n```"); + Console.WriteLine(); + Console.WriteLine("Map scripts can interact with the game engine in three ways:\n" + + "* Global tables provide functions for interacting with the global world state, or performing general helper tasks.\n" + + "They exist in the global namespace, and can be called directly using ```.```.\n" + + "* Individual actors expose a collection of properties and commands that query information of modify their state.\n" + + " * Some commands, marked as queued activity, are asynchronous. Activities are queued on the actor, and will run in " + + "sequence until the queue is empty or the Stop command is called. Actors that are not performing an activity are Idle " + + "(actor.IsIdle will return true). The properties and commands available on each actor depends on the traits that the actor " + + "specifies in its rule definitions.\n" + + "* Individual players explose a collection of properties and commands that query information of modify their state.\n" + + "The properties and commands available on each actor depends on the traits that the actor specifies in its rule definitions.\n"); + Console.WriteLine(); + + var tables = Game.modData.ObjectCreator.GetTypesImplementing() + .OrderBy(t => t.Name); + + Console.WriteLine("

Global Tables

"); + + foreach (var t in tables) + { + var name = t.GetCustomAttributes(true).First().Name; + var members = ScriptMemberWrapper.WrappableMembers(t); + + Console.WriteLine("
", name); + foreach (var m in members.OrderBy(m => m.Name)) + { + var desc = m.HasAttribute() ? m.GetCustomAttributes(true).First().Lines.JoinWith("\n") : ""; + Console.WriteLine("".F(m.LuaDocString(), desc)); + } + Console.WriteLine("
{0}
{0}{1}
"); + } + + Console.WriteLine("

Actor Properties / Commands

"); + + var actorCategories = Game.modData.ObjectCreator.GetTypesImplementing().SelectMany(cg => + { + var catAttr = cg.GetCustomAttributes(false).FirstOrDefault(); + var category = catAttr != null ? catAttr.Category : "Unsorted"; + + var required = RequiredTraitNames(cg); + return ScriptMemberWrapper.WrappableMembers(cg).Select(mi => Tuple.Create(category, mi, required)); + }).GroupBy(g => g.Item1).OrderBy(g => g.Key); + + foreach (var kv in actorCategories) + { + Console.WriteLine("", kv.Key); + + foreach (var property in kv.OrderBy(p => p.Item2.Name)) + { + var mi = property.Item2; + var required = property.Item3; + var hasDesc = mi.HasAttribute(); + var hasRequires = required.Any(); + var isActivity = mi.HasAttribute(); + + Console.WriteLine(""); + } + Console.WriteLine("
{0}
{0}", mi.LuaDocString()); + + if (isActivity) + Console.WriteLine("
Queued Activity"); + + Console.WriteLine("
"); + + if (hasDesc) + Console.WriteLine(mi.GetCustomAttributes(false).First().Lines.JoinWith("\n")); + + if (hasDesc && hasRequires) + Console.WriteLine("
"); + + if (hasRequires) + Console.WriteLine("Requires {1}: {0}".F(required.JoinWith(", "), required.Length == 1 ? "Trait" : "Traits")); + + Console.WriteLine("
"); + } + + Console.WriteLine("

Player Properties / Commands

"); + + var playerCategories = Game.modData.ObjectCreator.GetTypesImplementing().SelectMany(cg => + { + var catAttr = cg.GetCustomAttributes(false).FirstOrDefault(); + var category = catAttr != null ? catAttr.Category : "Unsorted"; + + var required = RequiredTraitNames(cg); + return ScriptMemberWrapper.WrappableMembers(cg).Select(mi => Tuple.Create(category, mi, required)); + }).GroupBy(g => g.Item1).OrderBy(g => g.Key); + + foreach (var kv in playerCategories) + { + Console.WriteLine("", kv.Key); + + foreach (var property in kv.OrderBy(p => p.Item2.Name)) + { + var mi = property.Item2; + var required = property.Item3; + var hasDesc = mi.HasAttribute(); + var hasRequires = required.Any(); + var isActivity = mi.HasAttribute(); + + Console.WriteLine(""); + } + + Console.WriteLine("
{0}
{0}", mi.LuaDocString()); + + if (isActivity) + Console.WriteLine("
Queued Activity"); + + Console.WriteLine("
"); + + if (hasDesc) + Console.WriteLine(mi.GetCustomAttributes(false).First().Lines.JoinWith("\n")); + + if (hasDesc && hasRequires) + Console.WriteLine("
"); + + if (hasRequires) + Console.WriteLine("Requires {1}: {0}".F(required.JoinWith(", "), required.Length == 1 ? "Trait" : "Traits")); + + Console.WriteLine("
"); + } + } + + static string[] RequiredTraitNames(Type t) + { + // Returns the inner types of all the Requires interfaces on this type + var outer = t.GetInterfaces() + .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(Requires<>)); + + // Get the inner types + var inner = outer.SelectMany(i => i.GetGenericArguments()).ToArray(); + + // Remove the namespace and the trailing "Info" + return inner.Select(i => i.Name.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault()) + .Select(s => s.EndsWith("Info") ? s.Remove(s.Length - 4, 4) : s) + .ToArray(); + } + } +} diff --git a/OpenRA.Mods.Common/UtilityCommands/ExtractTraitDocsCommand.cs b/OpenRA.Mods.Common/UtilityCommands/ExtractTraitDocsCommand.cs new file mode 100644 index 0000000000..169836d385 --- /dev/null +++ b/OpenRA.Mods.Common/UtilityCommands/ExtractTraitDocsCommand.cs @@ -0,0 +1,151 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.UtilityCommands +{ + class ExtractTraitDocsCommand : IUtilityCommand + { + public string Name { get { return "--docs"; } } + + [Desc("Generate trait documentation in MarkDown format.")] + public void Run(ModData modData, string[] args) + { + // HACK: The engine code assumes that Game.modData is set. + Game.modData = modData; + + Console.WriteLine( + "This documentation is aimed at modders. It displays all traits with default values and developer commentary. " + + "Please do not edit it directly, but add new `[Desc(\"String\")]` tags to the source code. This file has been " + + "automatically generated for version {0} of OpenRA.", Game.modData.Manifest.Mod.Version); + Console.WriteLine(); + + var toc = new StringBuilder(); + var doc = new StringBuilder(); + + foreach (var t in Game.modData.ObjectCreator.GetTypesImplementing().OrderBy(t => t.Namespace)) + { + if (t.ContainsGenericParameters || t.IsAbstract) + continue; // skip helpers like TraitInfo + + var traitName = t.Name.EndsWith("Info") ? t.Name.Substring(0, t.Name.Length - 4) : t.Name; + toc.AppendLine("* [{0}](#{1})".F(traitName, traitName.ToLowerInvariant())); + var traitDescLines = t.GetCustomAttributes(false).SelectMany(d => d.Lines); + doc.AppendLine(); + doc.AppendLine("### {0}".F(traitName)); + foreach (var line in traitDescLines) + doc.AppendLine(line); + + var requires = RequiredTraitTypes(t); + var reqCount = requires.Length; + if (reqCount > 0) + { + if (t.HasAttribute()) + doc.AppendLine("\n"); + + doc.Append("Requires trait{0}: ".F(reqCount > 1 ? "s" : "")); + + var i = 0; + foreach (var require in requires) + { + var n = require.Name; + var name = n.EndsWith("Info") ? n.Remove(n.Length - 4, 4) : n; + doc.Append("`{0}`{1}".F(name, i + 1 == reqCount ? ".\n" : ", ")); + i++; + } + } + + var infos = FieldLoader.GetTypeLoadInfo(t); + if (!infos.Any()) + continue; + doc.AppendLine(""); + doc.AppendLine(""); + var liveTraitInfo = Game.modData.ObjectCreator.CreateBasic(t); + foreach (var info in infos) + { + var fieldDescLines = info.Field.GetCustomAttributes(true).SelectMany(d => d.Lines); + var fieldType = FriendlyTypeName(info.Field.FieldType); + var defaultValue = FieldSaver.SaveField(liveTraitInfo, info.Field.Name).Value.Value; + doc.Append("".F(info.YamlName, defaultValue, fieldType)); + doc.Append(""); + } + doc.AppendLine("
PropertyDefault ValueTypeDescription
{0}{1}{2}"); + foreach (var line in fieldDescLines) + doc.Append(line + " "); + doc.AppendLine("
"); + } + + Console.Write(toc.ToString()); + Console.Write(doc.ToString()); + } + + static Type[] RequiredTraitTypes(Type t) + { + return t.GetInterfaces() + .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(Requires<>)) + .SelectMany(i => i.GetGenericArguments()) + .Where(i => !i.IsInterface && !t.IsSubclassOf(i)) + .OrderBy(i => i.Name) + .ToArray(); + } + + static string FriendlyTypeName(Type t) + { + if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Dictionary<,>)) + return "Dictionary<{0},{1}>".F(t.GetGenericArguments().Select(FriendlyTypeName).ToArray()); + + if (t.IsSubclassOf(typeof(Array))) + return "Multiple {0}".F(FriendlyTypeName(t.GetElementType())); + + if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(OpenRA.Primitives.Cache<,>)) + return "Cached<{0},{1}>".F(t.GetGenericArguments().Select(FriendlyTypeName).ToArray()); + + if (t == typeof(int) || t == typeof(uint)) + return "Integer"; + + if (t == typeof(int2)) + return "2D Integer"; + + if (t == typeof(float) || t == typeof(decimal)) + return "Real Number"; + + if (t == typeof(float2)) + return "2D Real Number"; + + if (t == typeof(CPos)) + return "2D Cell Position"; + + if (t == typeof(CVec)) + return "2D Cell Vector"; + + if (t == typeof(WAngle)) + return "1D World Angle"; + + if (t == typeof(WRot)) + return "3D World Rotation"; + + if (t == typeof(WPos)) + return "3D World Position"; + + if (t == typeof(WRange)) + return "1D World Range"; + + if (t == typeof(WVec)) + return "3D World Vector"; + + return t.Name; + } + } +} diff --git a/OpenRA.Mods.Common/UtilityCommands/GenerateMinimapCommand.cs b/OpenRA.Mods.Common/UtilityCommands/GenerateMinimapCommand.cs new file mode 100644 index 0000000000..8b2142605f --- /dev/null +++ b/OpenRA.Mods.Common/UtilityCommands/GenerateMinimapCommand.cs @@ -0,0 +1,42 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using OpenRA.Graphics; +using OpenRA.FileSystem; + +namespace OpenRA.Mods.Common.UtilityCommands +{ + class GenerateMinimapCommand : IUtilityCommand + { + public string Name { get { return "--map-preview"; } } + + [Desc("MAPFILE", "Render PNG minimap of specified oramap file.")] + public void Run(ModData modData, string[] args) + { + Game.modData = modData; + var map = new Map(args[1]); + + GlobalFileSystem.UnmountAll(); + foreach (var dir in Game.modData.Manifest.Folders) + GlobalFileSystem.Mount(dir); + + var minimap = Minimap.RenderMapPreview(map.Rules.TileSets[map.Tileset], map, true); + + var dest = Path.GetFileNameWithoutExtension(args[1]) + ".png"; + minimap.Save(dest); + Console.WriteLine(dest + " saved."); + } + } +} diff --git a/OpenRA.Mods.Common/UtilityCommands/GetMapHashCommand.cs b/OpenRA.Mods.Common/UtilityCommands/GetMapHashCommand.cs new file mode 100644 index 0000000000..6bf1352b75 --- /dev/null +++ b/OpenRA.Mods.Common/UtilityCommands/GetMapHashCommand.cs @@ -0,0 +1,30 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenRA.Mods.Common.UtilityCommands +{ + public class GetMapHashCommand : IUtilityCommand + { + public string Name { get { return "--map-hash"; } } + + [Desc("MAPFILE", "Generate hash of specified oramap file.")] + public void Run(ModData modData, string[] args) + { + Game.modData = modData; + var result = new Map(args[1]).Uid; + Console.WriteLine(result); + } + } +} diff --git a/OpenRA.Utility/Glob.cs b/OpenRA.Mods.Common/UtilityCommands/Glob.cs similarity index 98% rename from OpenRA.Utility/Glob.cs rename to OpenRA.Mods.Common/UtilityCommands/Glob.cs index 46f32d57b8..382f8b8775 100644 --- a/OpenRA.Utility/Glob.cs +++ b/OpenRA.Mods.Common/UtilityCommands/Glob.cs @@ -12,9 +12,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; -namespace OpenRA.Utility +namespace OpenRA.Mods.Common.UtilityCommands { - public static class Glob + static class Glob { public static bool Enabled = true; diff --git a/OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs b/OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs new file mode 100644 index 0000000000..cc9a72e40f --- /dev/null +++ b/OpenRA.Mods.Common/UtilityCommands/ImportLegacyMapCommand.cs @@ -0,0 +1,35 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenRA.Mods.Common.UtilityCommands +{ + class ImportLegacyMapCommand : IUtilityCommand + { + public string Name { get { return "--map-import"; } } + + [Desc("FILENAME", "Convert a legacy INI/MPR map to the OpenRA format.")] + public void Run(ModData modData, string[] args) + { + // HACK: The engine code assumes that Game.modData is set. + Game.modData = modData; + + var rules = Game.modData.RulesetCache.LoadDefaultRules(); + var map = LegacyMapImporter.Import(args[1], modData.Manifest.Mod.Id, rules, e => Console.WriteLine(e)); + var dest = map.Title + ".oramap"; + map.Save(dest); + Console.WriteLine(dest + " saved."); + } + } +} diff --git a/OpenRA.Utility/LegacyMapImporter.cs b/OpenRA.Mods.Common/UtilityCommands/LegacyMapImporter.cs similarity index 99% rename from OpenRA.Utility/LegacyMapImporter.cs rename to OpenRA.Mods.Common/UtilityCommands/LegacyMapImporter.cs index 7fc213cdd2..cd06a77a2a 100644 --- a/OpenRA.Utility/LegacyMapImporter.cs +++ b/OpenRA.Mods.Common/UtilityCommands/LegacyMapImporter.cs @@ -21,9 +21,9 @@ using OpenRA.Graphics; using OpenRA.Primitives; using OpenRA.Traits; -namespace OpenRA.Utility +namespace OpenRA.Mods.Common.UtilityCommands { - public class LegacyMapImporter + class LegacyMapImporter { // Mapping from ra overlay index to type string static string[] redAlertOverlayNames = diff --git a/OpenRA.Mods.Common/UtilityCommands/RemapShpCommand.cs b/OpenRA.Mods.Common/UtilityCommands/RemapShpCommand.cs new file mode 100644 index 0000000000..59ee1885b6 --- /dev/null +++ b/OpenRA.Mods.Common/UtilityCommands/RemapShpCommand.cs @@ -0,0 +1,85 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using OpenRA.Traits; +using OpenRA.Graphics; +using OpenRA.FileFormats; +using OpenRA.FileSystem; + +namespace OpenRA.Mods.Common.UtilityCommands +{ + class RemapShpCommand : IUtilityCommand + { + public string Name { get { return "--remap"; } } + + [Desc("SRCMOD:PAL DESTMOD:PAL SRCSHP DESTSHP", "Remap SHPs to another palette")] + public void Run(ModData modData, string[] args) + { + var remap = new Dictionary(); + + /* the first 4 entries are fixed */ + for (var i = 0; i < 4; i++) + remap[i] = i; + + var srcMod = args[1].Split(':')[0]; + + Game.modData = new ModData(srcMod); + GlobalFileSystem.LoadFromManifest(Game.modData.Manifest); + var srcRules = Game.modData.RulesetCache.LoadDefaultRules(); + var srcPaletteInfo = srcRules.Actors["player"].Traits.Get(); + var srcRemapIndex = srcPaletteInfo.RemapIndex; + + var destMod = args[2].Split(':')[0]; + Game.modData = new ModData(destMod); + GlobalFileSystem.LoadFromManifest(Game.modData.Manifest); + var destRules = Game.modData.RulesetCache.LoadDefaultRules(); + var destPaletteInfo = destRules.Actors["player"].Traits.Get(); + var destRemapIndex = destPaletteInfo.RemapIndex; + var shadowIndex = new int[] { }; + + // the remap range is always 16 entries, but their location and order changes + for (var i = 0; i < 16; i++) + remap[PlayerColorRemap.GetRemapIndex(srcRemapIndex, i)] + = PlayerColorRemap.GetRemapIndex(destRemapIndex, i); + + // map everything else to the best match based on channel-wise distance + var srcPalette = new ImmutablePalette(args[1].Split(':')[1], shadowIndex); + var destPalette = new ImmutablePalette(args[2].Split(':')[1], shadowIndex); + + for (var i = 0; i < Palette.Size; i++) + if (!remap.ContainsKey(i)) + remap[i] = Enumerable.Range(0, Palette.Size) + .Where(a => !remap.ContainsValue(a)) + .MinBy(a => ColorDistance(destPalette[a], srcPalette[i])); + + var srcImage = ShpReader.Load(args[3]); + + using (var destStream = File.Create(args[4])) + ShpReader.Write(destStream, srcImage.Size, + srcImage.Frames.Select(im => im.Data.Select(px => (byte)remap[px]).ToArray())); + } + + static int ColorDistance(uint a, uint b) + { + var ca = Color.FromArgb((int)a); + var cb = Color.FromArgb((int)b); + + return Math.Abs((int)ca.R - (int)cb.R) + + Math.Abs((int)ca.G - (int)cb.G) + + Math.Abs((int)ca.B - (int)cb.B); + } + } +} diff --git a/OpenRA.Mods.Common/UtilityCommands/TransposeShpCommand.cs b/OpenRA.Mods.Common/UtilityCommands/TransposeShpCommand.cs new file mode 100644 index 0000000000..0f5f5be222 --- /dev/null +++ b/OpenRA.Mods.Common/UtilityCommands/TransposeShpCommand.cs @@ -0,0 +1,48 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using OpenRA.FileFormats; + +namespace OpenRA.Mods.Common.UtilityCommands +{ + class TransposeShpCommand : IUtilityCommand + { + public string Name { get { return "--transpose"; } } + + [Desc("SRCSHP DESTSHP START N M [START N M ...]", + "Transpose the N*M block of frames starting at START.")] + public void Run(ModData modData, string[] args) + { + var srcImage = ShpReader.Load(args[1]); + + var srcFrames = srcImage.Frames; + var destFrames = srcImage.Frames.ToArray(); + + for (var z = 3; z < args.Length - 2; z += 3) + { + var start = Exts.ParseIntegerInvariant(args[z]); + var m = Exts.ParseIntegerInvariant(args[z + 1]); + var n = Exts.ParseIntegerInvariant(args[z + 2]); + + for (var i = 0; i < m; i++) + for (var j = 0; j < n; j++) + destFrames[start + i * n + j] = srcFrames[start + j * m + i]; + } + + using (var destStream = File.Create(args[2])) + ShpReader.Write(destStream, srcImage.Size, destFrames.Select(f => f.Data)); + } + } +} diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeMapCommand.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeMapCommand.cs new file mode 100644 index 0000000000..fcfce75a6b --- /dev/null +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeMapCommand.cs @@ -0,0 +1,36 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenRA.Mods.Common.UtilityCommands +{ + class UpgradeMapCommand : IUtilityCommand + { + public string Name { get { return "--upgrade-map"; } } + + [Desc("MAP", "CURRENTENGINE", "Upgrade map rules to the latest engine version.")] + public void Run(ModData modData, string[] args) + { + // HACK: The engine code assumes that Game.modData is set. + Game.modData = modData; + + var map = new Map(args[1]); + var engineDate = Exts.ParseIntegerInvariant(args[2]); + + UpgradeRules.UpgradeWeaponRules(engineDate, ref map.WeaponDefinitions, null, 0); + UpgradeRules.UpgradeActorRules(engineDate, ref map.RuleDefinitions, null, 0); + map.Save(args[1]); + } + } +} diff --git a/OpenRA.Mods.Common/UtilityCommands/UpgradeModCommand.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeModCommand.cs new file mode 100644 index 0000000000..4f2633b5d6 --- /dev/null +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeModCommand.cs @@ -0,0 +1,79 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace OpenRA.Mods.Common.UtilityCommands +{ + class UpgradeModCommand : IUtilityCommand + { + public string Name { get { return "--upgrade-mod"; } } + + [Desc("CURRENTENGINE", "Upgrade mod rules to the latest engine version.")] + public void Run(ModData modData, string[] args) + { + // HACK: The engine code assumes that Game.modData is set. + Game.modData = modData; + Game.modData.MapCache.LoadMaps(); + + var engineDate = Exts.ParseIntegerInvariant(args[1]); + + Console.WriteLine("Processing Rules:"); + foreach (var filename in Game.modData.Manifest.Rules) + { + Console.WriteLine("\t" + filename); + var yaml = MiniYaml.FromFile(filename); + UpgradeRules.UpgradeActorRules(engineDate, ref yaml, null, 0); + + using (var file = new StreamWriter(filename)) + file.WriteLine(yaml.WriteToString()); + } + + Console.WriteLine("Processing Weapons:"); + foreach (var filename in Game.modData.Manifest.Weapons) + { + Console.WriteLine("\t" + filename); + var yaml = MiniYaml.FromFile(filename); + UpgradeRules.UpgradeWeaponRules(engineDate, ref yaml, null, 0); + + using (var file = new StreamWriter(filename)) + file.WriteLine(yaml.WriteToString()); + } + + Console.WriteLine("Processing Tilesets:"); + foreach (var filename in Game.modData.Manifest.TileSets) + { + Console.WriteLine("\t" + filename); + var yaml = MiniYaml.FromFile(filename); + UpgradeRules.UpgradeTileset(engineDate, ref yaml, null, 0); + + using (var file = new StreamWriter(filename)) + file.WriteLine(yaml.WriteToString()); + } + + Console.WriteLine("Processing Maps:"); + var maps = Game.modData.MapCache + .Where(m => m.Status == MapStatus.Available) + .Select(m => m.Map); + + foreach (var map in maps) + { + Console.WriteLine("\t" + map.Path); + UpgradeRules.UpgradeActorRules(engineDate, ref map.RuleDefinitions, null, 0); + UpgradeRules.UpgradeWeaponRules(engineDate, ref map.WeaponDefinitions, null, 0); + map.Save(map.Path); + } + } + } +} diff --git a/OpenRA.Utility/UpgradeRules.cs b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs similarity index 91% rename from OpenRA.Utility/UpgradeRules.cs rename to OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs index 6917c32c10..7113c037d3 100644 --- a/OpenRA.Utility/UpgradeRules.cs +++ b/OpenRA.Mods.Common/UtilityCommands/UpgradeRules.cs @@ -13,11 +13,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; -namespace OpenRA.Utility +namespace OpenRA.Mods.Common.UtilityCommands { - public static class UpgradeRules + static class UpgradeRules { - static void ConvertFloatToRange(ref string input) + internal static void ConvertFloatToRange(ref string input) { var value = float.Parse(input); var cells = (int)value; @@ -26,18 +26,18 @@ namespace OpenRA.Utility input = "{0}c{1}".F(cells, subcells); } - static void ConvertFloatArrayToPercentArray(ref string input) + internal static void ConvertFloatArrayToPercentArray(ref string input) { input = string.Join(", ", input.Split(',') .Select(s => ((int)Math.Round(FieldLoader.GetValue("(float value)", s) * 100)).ToString())); } - static void ConvertPxToRange(ref string input) + internal static void ConvertPxToRange(ref string input) { ConvertPxToRange(ref input, 1, 1); } - static void ConvertPxToRange(ref string input, int scaleMult, int scaleDiv) + internal static void ConvertPxToRange(ref string input, int scaleMult, int scaleDiv) { var value = Exts.ParseIntegerInvariant(input); var ts = Game.modData.Manifest.TileSize; @@ -48,13 +48,13 @@ namespace OpenRA.Utility input = cells != 0 ? "{0}c{1}".F(cells, subcells) : subcells.ToString(); } - static void ConvertAngle(ref string input) + internal static void ConvertAngle(ref string input) { var value = float.Parse(input); input = WAngle.ArcTan((int)(value * 4 * 1024), 1024).ToString(); } - static void ConvertInt2ToWVec(ref string input) + internal static void ConvertInt2ToWVec(ref string input) { var offset = FieldLoader.GetValue("(value)", input); var ts = Game.modData.Manifest.TileSize; @@ -62,7 +62,7 @@ namespace OpenRA.Utility input = world.ToString(); } - static void UpgradeActorRules(int engineVersion, ref List nodes, MiniYamlNode parent, int depth) + internal static void UpgradeActorRules(int engineVersion, ref List nodes, MiniYamlNode parent, int depth) { var parentKey = parent != null ? parent.Key.Split('@').First() : null; @@ -567,7 +567,7 @@ namespace OpenRA.Utility } } - static void UpgradeWeaponRules(int engineVersion, ref List nodes, MiniYamlNode parent, int depth) + internal static void UpgradeWeaponRules(int engineVersion, ref List nodes, MiniYamlNode parent, int depth) { var parentKey = parent != null ? parent.Key.Split('@').First() : null; @@ -888,7 +888,7 @@ namespace OpenRA.Utility } } - static void UpgradeTileset(int engineVersion, ref List nodes, MiniYamlNode parent, int depth) + internal static void UpgradeTileset(int engineVersion, ref List nodes, MiniYamlNode parent, int depth) { var parentKey = parent != null ? parent.Key.Split('@').First() : null; var addNodes = new List(); @@ -906,72 +906,5 @@ namespace OpenRA.Utility nodes.AddRange(addNodes); } - [Desc("MAP", "CURRENTENGINE", "MOD", "Upgrade map rules to the latest engine version.")] - public static void UpgradeMap(string[] args) - { - Game.modData = new ModData(args[3]); - var map = new Map(args[1]); - var engineDate = Exts.ParseIntegerInvariant(args[2]); - - UpgradeWeaponRules(engineDate, ref map.WeaponDefinitions, null, 0); - UpgradeActorRules(engineDate, ref map.RuleDefinitions, null, 0); - map.Save(args[1]); - } - - [Desc("MOD", "CURRENTENGINE", "Upgrade mod rules to the latest engine version.")] - public static void UpgradeMod(string[] args) - { - var mod = args[1]; - var engineDate = Exts.ParseIntegerInvariant(args[2]); - - Game.modData = new ModData(mod); - Game.modData.MapCache.LoadMaps(); - - Console.WriteLine("Processing Rules:"); - foreach (var filename in Game.modData.Manifest.Rules) - { - Console.WriteLine("\t" + filename); - var yaml = MiniYaml.FromFile(filename); - UpgradeActorRules(engineDate, ref yaml, null, 0); - - using (var file = new StreamWriter(filename)) - file.WriteLine(yaml.WriteToString()); - } - - Console.WriteLine("Processing Weapons:"); - foreach (var filename in Game.modData.Manifest.Weapons) - { - Console.WriteLine("\t" + filename); - var yaml = MiniYaml.FromFile(filename); - UpgradeWeaponRules(engineDate, ref yaml, null, 0); - - using (var file = new StreamWriter(filename)) - file.WriteLine(yaml.WriteToString()); - } - - Console.WriteLine("Processing Tilesets:"); - foreach (var filename in Game.modData.Manifest.TileSets) - { - Console.WriteLine("\t" + filename); - var yaml = MiniYaml.FromFile(filename); - UpgradeTileset(engineDate, ref yaml, null, 0); - - using (var file = new StreamWriter(filename)) - file.WriteLine(yaml.WriteToString()); - } - - Console.WriteLine("Processing Maps:"); - var maps = Game.modData.MapCache - .Where(m => m.Status == MapStatus.Available) - .Select(m => m.Map); - - foreach (var map in maps) - { - Console.WriteLine("\t" + map.Path); - UpgradeActorRules(engineDate, ref map.RuleDefinitions, null, 0); - UpgradeWeaponRules(engineDate, ref map.WeaponDefinitions, null, 0); - map.Save(map.Path); - } - } } } diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 6bf2ecb211..d5150ff46a 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -294,6 +294,7 @@ + diff --git a/OpenRA.Utility/Command.cs b/OpenRA.Utility/Command.cs deleted file mode 100644 index c013c83123..0000000000 --- a/OpenRA.Utility/Command.cs +++ /dev/null @@ -1,550 +0,0 @@ -#region Copyright & License Information -/* - * Copyright 2007-2014 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made - * available to you under the terms of the GNU General Public License - * as published by the Free Software Foundation. For more information, - * see COPYING. - */ -#endregion - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Text; -using OpenRA.FileFormats; -using OpenRA.FileSystem; -using OpenRA.Graphics; -using OpenRA.Scripting; -using OpenRA.Traits; - -namespace OpenRA.Utility -{ - public static class Command - { - static IEnumerable GlobArgs(string[] args, int startIndex = 1) - { - for (var i = startIndex; i < args.Length; i++) - foreach (var path in Glob.Expand(args[i])) - yield return path; - } - - [Desc("PNGFILE [PNGFILE ...]", "Combine a list of PNG images into a SHP")] - public static void ConvertPngToShp(string[] args) - { - var inputFiles = GlobArgs(args).OrderBy(a => a).ToList(); - var dest = inputFiles[0].Split('-').First() + ".shp"; - var frames = inputFiles.Select(a => PngLoader.Load(a)); - - var size = frames.First().Size; - if (frames.Any(f => f.Size != size)) - throw new InvalidOperationException("All frames must be the same size"); - - using (var destStream = File.Create(dest)) - ShpReader.Write(destStream, size, frames.Select(f => f.ToBytes())); - - Console.WriteLine(dest + " saved."); - } - - static byte[] ToBytes(this Bitmap bitmap) - { - var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, - PixelFormat.Format8bppIndexed); - - var bytes = new byte[bitmap.Width * bitmap.Height]; - for (var i = 0; i < bitmap.Height; i++) - Marshal.Copy(new IntPtr(data.Scan0.ToInt64() + i * data.Stride), - bytes, i * bitmap.Width, bitmap.Width); - - bitmap.UnlockBits(data); - - return bytes; - } - - [Desc("SPRITEFILE PALETTE [--noshadow] [--nopadding]", - "Convert a shp/tmp/R8 to a series of PNGs, optionally removing shadow")] - public static void ConvertSpriteToPng(string[] args) - { - var src = args[1]; - var shadowIndex = new int[] { }; - if (args.Contains("--noshadow")) - { - Array.Resize(ref shadowIndex, shadowIndex.Length + 3); - shadowIndex[shadowIndex.Length - 1] = 1; - shadowIndex[shadowIndex.Length - 2] = 3; - shadowIndex[shadowIndex.Length - 3] = 4; - } - - var palette = new ImmutablePalette(args[2], shadowIndex); - - ISpriteSource source; - using (var stream = File.OpenRead(src)) - source = SpriteSource.LoadSpriteSource(stream, src); - - // The r8 padding requires external information that we can't access here. - var usePadding = !(args.Contains("--nopadding") || source is R8Reader); - var count = 0; - var prefix = Path.GetFileNameWithoutExtension(src); - - foreach (var frame in source.Frames) - { - var frameSize = usePadding ? frame.FrameSize : frame.Size; - var offset = usePadding ? (frame.Offset - 0.5f * new float2(frame.Size - frame.FrameSize)).ToInt2() : int2.Zero; - - // shp(ts) may define empty frames - if (frameSize.Width == 0 && frameSize.Height == 0) - { - count++; - continue; - } - - using (var bitmap = new Bitmap(frameSize.Width, frameSize.Height, PixelFormat.Format8bppIndexed)) - { - bitmap.Palette = palette.AsSystemPalette(); - var data = bitmap.LockBits(new Rectangle(0, 0, frameSize.Width, frameSize.Height), - ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); - - // Clear the frame - if (usePadding) - { - var clearRow = new byte[data.Stride]; - for (var i = 0; i < frameSize.Height; i++) - Marshal.Copy(clearRow, 0, new IntPtr(data.Scan0.ToInt64() + i * data.Stride), data.Stride); - } - - for (var i = 0; i < frame.Size.Height; i++) - { - var destIndex = new IntPtr(data.Scan0.ToInt64() + (i + offset.Y) * data.Stride + offset.X); - Marshal.Copy(frame.Data, i * frame.Size.Width, destIndex, frame.Size.Width); - } - - bitmap.UnlockBits(data); - - var filename = "{0}-{1:D4}.png".F(prefix, count++); - bitmap.Save(filename); - } - } - - Console.WriteLine("Saved {0}-[0..{1}].png", prefix, count - 1); - } - - [Desc("MOD FILES", "Extract files from mod packages to the current directory")] - public static void ExtractFiles(string[] args) - { - var mod = args[1]; - var files = args.Skip(2); - - var manifest = new Manifest(mod); - GlobalFileSystem.LoadFromManifest(manifest); - - foreach (var f in files) - { - var src = GlobalFileSystem.Open(f); - if (src == null) - throw new InvalidOperationException("File not found: {0}".F(f)); - var data = src.ReadAllBytes(); - File.WriteAllBytes(f, data); - Console.WriteLine(f + " saved."); - } - } - - static int ColorDistance(uint a, uint b) - { - var ca = Color.FromArgb((int)a); - var cb = Color.FromArgb((int)b); - - return Math.Abs((int)ca.R - (int)cb.R) + - Math.Abs((int)ca.G - (int)cb.G) + - Math.Abs((int)ca.B - (int)cb.B); - } - - [Desc("SRCMOD:PAL DESTMOD:PAL SRCSHP DESTSHP", "Remap SHPs to another palette")] - public static void RemapShp(string[] args) - { - var remap = new Dictionary(); - - /* the first 4 entries are fixed */ - for (var i = 0; i < 4; i++) - remap[i] = i; - - var srcMod = args[1].Split(':')[0]; - Game.modData = new ModData(srcMod); - GlobalFileSystem.LoadFromManifest(Game.modData.Manifest); - var srcRules = Game.modData.RulesetCache.LoadDefaultRules(); - var srcPaletteInfo = srcRules.Actors["player"].Traits.Get(); - var srcRemapIndex = srcPaletteInfo.RemapIndex; - - var destMod = args[2].Split(':')[0]; - Game.modData = new ModData(destMod); - GlobalFileSystem.LoadFromManifest(Game.modData.Manifest); - var destRules = Game.modData.RulesetCache.LoadDefaultRules(); - var destPaletteInfo = destRules.Actors["player"].Traits.Get(); - var destRemapIndex = destPaletteInfo.RemapIndex; - var shadowIndex = new int[] { }; - - // the remap range is always 16 entries, but their location and order changes - for (var i = 0; i < 16; i++) - remap[PlayerColorRemap.GetRemapIndex(srcRemapIndex, i)] - = PlayerColorRemap.GetRemapIndex(destRemapIndex, i); - - // map everything else to the best match based on channel-wise distance - var srcPalette = new ImmutablePalette(args[1].Split(':')[1], shadowIndex); - var destPalette = new ImmutablePalette(args[2].Split(':')[1], shadowIndex); - - for (var i = 0; i < Palette.Size; i++) - if (!remap.ContainsKey(i)) - remap[i] = Enumerable.Range(0, Palette.Size) - .Where(a => !remap.ContainsValue(a)) - .MinBy(a => ColorDistance(destPalette[a], srcPalette[i])); - - var srcImage = ShpReader.Load(args[3]); - - using (var destStream = File.Create(args[4])) - ShpReader.Write(destStream, srcImage.Size, - srcImage.Frames.Select(im => im.Data.Select(px => (byte)remap[px]).ToArray())); - } - - [Desc("SRCSHP DESTSHP START N M [START N M ...]", - "Transpose the N*M block of frames starting at START.")] - public static void TransposeShp(string[] args) - { - var srcImage = ShpReader.Load(args[1]); - - var srcFrames = srcImage.Frames; - var destFrames = srcImage.Frames.ToArray(); - - for (var z = 3; z < args.Length - 2; z += 3) - { - var start = Exts.ParseIntegerInvariant(args[z]); - var m = Exts.ParseIntegerInvariant(args[z + 1]); - var n = Exts.ParseIntegerInvariant(args[z + 2]); - - for (var i = 0; i < m; i++) - for (var j = 0; j < n; j++) - destFrames[start + i * n + j] = srcFrames[start + j * m + i]; - } - - using (var destStream = File.Create(args[2])) - ShpReader.Write(destStream, srcImage.Size, destFrames.Select(f => f.Data)); - } - - static string FriendlyTypeName(Type t) - { - if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Dictionary<,>)) - return "Dictionary<{0},{1}>".F(t.GetGenericArguments().Select(FriendlyTypeName).ToArray()); - - if (t.IsSubclassOf(typeof(Array))) - return "Multiple {0}".F(FriendlyTypeName(t.GetElementType())); - - if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(OpenRA.Primitives.Cache<,>)) - return "Cached<{0},{1}>".F(t.GetGenericArguments().Select(FriendlyTypeName).ToArray()); - - if (t == typeof(int) || t == typeof(uint)) - return "Integer"; - - if (t == typeof(int2)) - return "2D Integer"; - - if (t == typeof(float) || t == typeof(decimal)) - return "Real Number"; - - if (t == typeof(float2)) - return "2D Real Number"; - - if (t == typeof(CPos)) - return "2D Cell Position"; - - if (t == typeof(CVec)) - return "2D Cell Vector"; - - if (t == typeof(WAngle)) - return "1D World Angle"; - - if (t == typeof(WRot)) - return "3D World Rotation"; - - if (t == typeof(WPos)) - return "3D World Position"; - - if (t == typeof(WRange)) - return "1D World Range"; - - if (t == typeof(WVec)) - return "3D World Vector"; - - return t.Name; - } - - [Desc("MOD", "Generate trait documentation in MarkDown format.")] - public static void ExtractTraitDocs(string[] args) - { - Game.modData = new ModData(args[1]); - - Console.WriteLine( - "This documentation is aimed at modders. It displays all traits with default values and developer commentary. " + - "Please do not edit it directly, but add new `[Desc(\"String\")]` tags to the source code. This file has been " + - "automatically generated for version {0} of OpenRA.", Game.modData.Manifest.Mod.Version); - Console.WriteLine(); - - var toc = new StringBuilder(); - var doc = new StringBuilder(); - - foreach (var t in Game.modData.ObjectCreator.GetTypesImplementing().OrderBy(t => t.Namespace)) - { - if (t.ContainsGenericParameters || t.IsAbstract) - continue; // skip helpers like TraitInfo - - var traitName = t.Name.EndsWith("Info") ? t.Name.Substring(0, t.Name.Length - 4) : t.Name; - toc.AppendLine("* [{0}](#{1})".F(traitName, traitName.ToLowerInvariant())); - var traitDescLines = t.GetCustomAttributes(false).SelectMany(d => d.Lines); - doc.AppendLine(); - doc.AppendLine("### {0}".F(traitName)); - foreach (var line in traitDescLines) - doc.AppendLine(line); - - var requires = RequiredTraitTypes(t); - var reqCount = requires.Length; - if (reqCount > 0) - { - if (t.HasAttribute()) - doc.AppendLine("\n"); - - doc.Append("Requires trait{0}: ".F(reqCount > 1 ? "s" : "")); - - var i = 0; - foreach (var require in requires) - { - var n = require.Name; - var name = n.EndsWith("Info") ? n.Remove(n.Length - 4, 4) : n; - doc.Append("`{0}`{1}".F(name, i + 1 == reqCount ? ".\n" : ", ")); - i++; - } - } - - var infos = FieldLoader.GetTypeLoadInfo(t); - if (!infos.Any()) - continue; - doc.AppendLine(""); - doc.AppendLine(""); - var liveTraitInfo = Game.modData.ObjectCreator.CreateBasic(t); - foreach (var info in infos) - { - var fieldDescLines = info.Field.GetCustomAttributes(true).SelectMany(d => d.Lines); - var fieldType = FriendlyTypeName(info.Field.FieldType); - var defaultValue = FieldSaver.SaveField(liveTraitInfo, info.Field.Name).Value.Value; - doc.Append("".F(info.YamlName, defaultValue, fieldType)); - doc.Append(""); - } - doc.AppendLine("
PropertyDefault ValueTypeDescription
{0}{1}{2}"); - foreach (var line in fieldDescLines) - doc.Append(line + " "); - doc.AppendLine("
"); - } - - Console.Write(toc.ToString()); - Console.Write(doc.ToString()); - } - - static string[] RequiredTraitNames(Type t) - { - // Returns the inner types of all the Requires interfaces on this type - var outer = t.GetInterfaces() - .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(Requires<>)); - - // Get the inner types - var inner = outer.SelectMany(i => i.GetGenericArguments()).ToArray(); - - // Remove the namespace and the trailing "Info" - return inner.Select(i => i.Name.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault()) - .Select(s => s.EndsWith("Info") ? s.Remove(s.Length - 4, 4) : s) - .ToArray(); - } - - static Type[] RequiredTraitTypes(Type t) - { - return t.GetInterfaces() - .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(Requires<>)) - .SelectMany(i => i.GetGenericArguments()) - .Where(i => !i.IsInterface && !t.IsSubclassOf(i)) - .OrderBy(i => i.Name) - .ToArray(); - } - - [Desc("MOD", "Generate Lua API documentation in MarkDown format.")] - public static void ExtractLuaDocs(string[] args) - { - Game.modData = new ModData(args[1]); - - Console.WriteLine("This is an automatically generated listing of the new Lua map scripting API, generated for {0} of OpenRA.", Game.modData.Manifest.Mod.Version); - Console.WriteLine(); - Console.WriteLine("OpenRA allows custom maps and missions to be scripted using Lua 5.1.\n" + - "These scripts run in a sandbox that prevents access to unsafe functions (e.g. OS or file access), " + - "and limits the memory and CPU usage of the scripts."); - Console.WriteLine(); - Console.WriteLine("You can access this interface by adding the [LuaScript](Traits#luascript) trait to the world actor in your map rules (note, you must replace the spaces in the snippet below with a single tab for each level of indentation):"); - Console.WriteLine("```\nRules:\n\tWorld:\n\t\tLuaScript:\n\t\t\tScripts: myscript.lua\n```"); - Console.WriteLine(); - Console.WriteLine("Map scripts can interact with the game engine in three ways:\n" + - "* Global tables provide functions for interacting with the global world state, or performing general helper tasks.\n" + - "They exist in the global namespace, and can be called directly using ```.```.\n" + - "* Individual actors expose a collection of properties and commands that query information or modify their state.\n" + - " * Some commands, marked as queued activity, are asynchronous. Activities are queued on the actor, and will run in " + - "sequence until the queue is empty or the Stop command is called. Actors that are not performing an activity are Idle " + - "(actor.IsIdle will return true). The properties and commands available on each actor depends on the traits that the actor " + - "specifies in its rule definitions.\n" + - "* Individual players expose a collection of properties and commands that query information or modify their state.\n" + - "The properties and commands available on each actor depends on the traits that the actor specifies in its rule definitions.\n"); - Console.WriteLine(); - - var tables = Game.modData.ObjectCreator.GetTypesImplementing() - .OrderBy(t => t.Name); - - Console.WriteLine("

Global Tables

"); - - foreach (var t in tables) - { - var name = t.GetCustomAttributes(true).First().Name; - var members = ScriptMemberWrapper.WrappableMembers(t); - - Console.WriteLine("
", name); - foreach (var m in members.OrderBy(m => m.Name)) - { - var desc = m.HasAttribute() ? m.GetCustomAttributes(true).First().Lines.JoinWith("\n") : ""; - Console.WriteLine("".F(m.LuaDocString(), desc)); - } - Console.WriteLine("
{0}
{0}{1}
"); - } - - Console.WriteLine("

Actor Properties / Commands

"); - - var actorCategories = Game.modData.ObjectCreator.GetTypesImplementing().SelectMany(cg => - { - var catAttr = cg.GetCustomAttributes(false).FirstOrDefault(); - var category = catAttr != null ? catAttr.Category : "Unsorted"; - - var required = RequiredTraitNames(cg); - return ScriptMemberWrapper.WrappableMembers(cg).Select(mi => Tuple.Create(category, mi, required)); - }).GroupBy(g => g.Item1).OrderBy(g => g.Key); - - foreach (var kv in actorCategories) - { - Console.WriteLine("", kv.Key); - - foreach (var property in kv.OrderBy(p => p.Item2.Name)) - { - var mi = property.Item2; - var required = property.Item3; - var hasDesc = mi.HasAttribute(); - var hasRequires = required.Any(); - var isActivity = mi.HasAttribute(); - - Console.WriteLine(""); - } - Console.WriteLine("
{0}
{0}", mi.LuaDocString()); - - if (isActivity) - Console.WriteLine("
Queued Activity"); - - Console.WriteLine("
"); - - if (hasDesc) - Console.WriteLine(mi.GetCustomAttributes(false).First().Lines.JoinWith("\n")); - - if (hasDesc && hasRequires) - Console.WriteLine("
"); - - if (hasRequires) - Console.WriteLine("Requires {1}: {0}".F(required.JoinWith(", "), required.Length == 1 ? "Trait" : "Traits")); - - Console.WriteLine("
"); - } - - Console.WriteLine("

Player Properties / Commands

"); - - var playerCategories = Game.modData.ObjectCreator.GetTypesImplementing().SelectMany(cg => - { - var catAttr = cg.GetCustomAttributes(false).FirstOrDefault(); - var category = catAttr != null ? catAttr.Category : "Unsorted"; - - var required = RequiredTraitNames(cg); - return ScriptMemberWrapper.WrappableMembers(cg).Select(mi => Tuple.Create(category, mi, required)); - }).GroupBy(g => g.Item1).OrderBy(g => g.Key); - - foreach (var kv in playerCategories) - { - Console.WriteLine("", kv.Key); - - foreach (var property in kv.OrderBy(p => p.Item2.Name)) - { - var mi = property.Item2; - var required = property.Item3; - var hasDesc = mi.HasAttribute(); - var hasRequires = required.Any(); - var isActivity = mi.HasAttribute(); - - Console.WriteLine(""); - } - - Console.WriteLine("
{0}
{0}", mi.LuaDocString()); - - if (isActivity) - Console.WriteLine("
Queued Activity"); - - Console.WriteLine("
"); - - if (hasDesc) - Console.WriteLine(mi.GetCustomAttributes(false).First().Lines.JoinWith("\n")); - - if (hasDesc && hasRequires) - Console.WriteLine("
"); - - if (hasRequires) - Console.WriteLine("Requires {1}: {0}".F(required.JoinWith(", "), required.Length == 1 ? "Trait" : "Traits")); - - Console.WriteLine("
"); - } - } - - [Desc("MAPFILE", "Generate hash of specified oramap file.")] - public static void GetMapHash(string[] args) - { - var result = new Map(args[1]).Uid; - Console.WriteLine(result); - } - - [Desc("MAPFILE", "Render PNG minimap of specified oramap file.")] - public static void GenerateMinimap(string[] args) - { - var map = new Map(args[1]); - Game.modData = new ModData(map.RequiresMod); - - GlobalFileSystem.UnmountAll(); - foreach (var dir in Game.modData.Manifest.Folders) - GlobalFileSystem.Mount(dir); - - var minimap = Minimap.RenderMapPreview(map.Rules.TileSets[map.Tileset], map, true); - - var dest = Path.GetFileNameWithoutExtension(args[1]) + ".png"; - minimap.Save(dest); - Console.WriteLine(dest + " saved."); - } - - [Desc("MOD", "FILENAME", "Convert a legacy INI/MPR map to the OpenRA format.")] - public static void ImportLegacyMap(string[] args) - { - var mod = args[1]; - var filename = args[2]; - Game.modData = new ModData(mod); - var rules = Game.modData.RulesetCache.LoadDefaultRules(); - var map = LegacyMapImporter.Import(filename, mod, rules, e => Console.WriteLine(e)); - var dest = map.Title + ".oramap"; - map.Save(dest); - Console.WriteLine(dest + " saved."); - } - } -} diff --git a/OpenRA.Utility/OpenRA.Utility.csproj b/OpenRA.Utility/OpenRA.Utility.csproj index e412f98ce0..e869dff510 100644 --- a/OpenRA.Utility/OpenRA.Utility.csproj +++ b/OpenRA.Utility/OpenRA.Utility.csproj @@ -65,12 +65,7 @@ - - - - - diff --git a/OpenRA.Utility/Program.cs b/OpenRA.Utility/Program.cs index cf1a765061..e06aa3402c 100644 --- a/OpenRA.Utility/Program.cs +++ b/OpenRA.Utility/Program.cs @@ -18,36 +18,39 @@ namespace OpenRA.Utility { class Program { - static readonly Dictionary> Actions = new Dictionary>() - { - { "--shp", Command.ConvertPngToShp }, - { "--png", Command.ConvertSpriteToPng }, - { "--extract", Command.ExtractFiles }, - { "--remap", Command.RemapShp }, - { "--transpose", Command.TransposeShp }, - { "--docs", Command.ExtractTraitDocs }, - { "--lua-docs", Command.ExtractLuaDocs }, - { "--map-hash", Command.GetMapHash }, - { "--map-preview", Command.GenerateMinimap }, - { "--upgrade-map", UpgradeRules.UpgradeMap }, - { "--upgrade-mod", UpgradeRules.UpgradeMod }, - { "--map-import", Command.ImportLegacyMap }, - { "--extract-language-strings", ExtractLanguageStrings.FromMod } - }; - static void Main(string[] args) { - if (args.Length == 0) { PrintUsage(); return; } + if (args.Length == 0) + { + PrintUsage(null); + return; + } AppDomain.CurrentDomain.AssemblyResolve += GlobalFileSystem.ResolveAssembly; Log.LogPath = Platform.SupportDir + "Logs" + Path.DirectorySeparatorChar; Log.AddChannel("perf", null); + var modName = args[0]; + if (!ModMetadata.AllMods.Keys.Contains(modName)) + { + PrintUsage(null); + return; + } + + var modData = new ModData(modName); + args = args.Skip(1).ToArray(); + var actions = new Dictionary>(); + foreach (var commandType in modData.ObjectCreator.GetTypesImplementing()) + { + var command = (IUtilityCommand)Activator.CreateInstance(commandType); + actions.Add(command.Name, command.Run); + } + try { - var action = Exts.WithDefault(_ => PrintUsage(), () => Actions[args[0]]); - action(args); + var action = Exts.WithDefault((a,b) => PrintUsage(actions), () => actions[args[0]]); + action(modData, args); } catch (Exception e) { @@ -60,11 +63,15 @@ namespace OpenRA.Utility } } - static void PrintUsage() + static void PrintUsage(IDictionary> actions) { - Console.WriteLine("Usage: OpenRA.Utility.exe [OPTION] [ARGS]"); + Console.WriteLine("Run `OpenRA.Utility.exe [MOD]` to see a list of available commands."); + Console.WriteLine("The available mods are: " + string.Join(", ", ModMetadata.AllMods.Keys)); Console.WriteLine(); - foreach (var a in Actions) + + if (actions == null) + return; + foreach (var a in actions) { var descParts = a.Value.Method.GetCustomAttributes(true) .SelectMany(d => d.Lines); diff --git a/packaging/update-wiki.sh b/packaging/update-wiki.sh index 82b5b9d25a..17c280ef84 100755 --- a/packaging/update-wiki.sh +++ b/packaging/update-wiki.sh @@ -7,10 +7,7 @@ echo "Updating https://github.com/OpenRA/OpenRA/wiki/Traits" rm -rf $HOME/openra-wiki git clone git@github.com:OpenRA/OpenRA.wiki.git $HOME/openra-wiki cp -fr ../DOCUMENTATION.md $HOME/openra-wiki/Traits.md - -pushd .. &> /dev/null -mono --debug OpenRA.Utility.exe --lua-docs d2k > $HOME/openra-wiki/New-Lua-API.md -popd &> /dev/null +cp -fr ../Lua-API.md $HOME/openra-wiki/New-Lua-API.md pushd $HOME/openra-wiki git add Traits.md