From ec77e15e5480a5ad50e4067f51dfc74ba84d41cc Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Sun, 22 Mar 2015 11:48:47 +0000 Subject: [PATCH] Add a utility command to fix implicit tile definitions. --- OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 1 + .../UtilityCommands/FixClassicTilesets.cs | 104 ++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 OpenRA.Mods.Common/UtilityCommands/FixClassicTilesets.cs diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 0ee62bda50..56e8a65815 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -617,6 +617,7 @@ + diff --git a/OpenRA.Mods.Common/UtilityCommands/FixClassicTilesets.cs b/OpenRA.Mods.Common/UtilityCommands/FixClassicTilesets.cs new file mode 100644 index 0000000000..6b0f95ce8b --- /dev/null +++ b/OpenRA.Mods.Common/UtilityCommands/FixClassicTilesets.cs @@ -0,0 +1,104 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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.Reflection; +using OpenRA.FileSystem; +using OpenRA.Graphics; +using OpenRA.Traits; +using StyleCop; + +namespace OpenRA.Mods.Common.UtilityCommands +{ + class FixClassicTilesets : IUtilityCommand + { + public string Name { get { return "--fix-classic-tilesets"; } } + + [Desc("Fixes missing template tile definitions and adds filename extensions.")] + public void Run(ModData modData, string[] args) + { + // HACK: The engine code assumes that Game.modData is set. + Game.ModData = modData; + GlobalFileSystem.LoadFromManifest(Game.ModData.Manifest); + + var imageField = typeof(TerrainTemplateInfo).GetField("Image"); + var pickAnyField = typeof(TerrainTemplateInfo).GetField("PickAny"); + var tileInfoField = typeof(TerrainTemplateInfo).GetField("tileInfo", BindingFlags.NonPublic | BindingFlags.Instance); + var terrainTypeField = typeof(TerrainTileInfo).GetField("TerrainType"); + var terrainLeftColorField = typeof(TerrainTileInfo).GetField("LeftColor"); + var terrainRightColorField = typeof(TerrainTileInfo).GetField("RightColor"); + var empty = new Size(0, 0); + var single = new int2(1, 1); + + foreach (var t in Game.ModData.Manifest.TileSets) + { + var ts = new TileSet(Game.ModData, t); + var exts = new[] { "" }.Concat(ts.Extensions); + var frameCache = new FrameCache(Game.ModData.SpriteLoaders, ts.Extensions); + + Console.WriteLine("Tileset: " + ts.Name); + foreach (var template in ts.Templates.Values) + { + // Find the sprite associated with this template + foreach (var ext in exts) + { + Stream s; + if (!GlobalFileSystem.TryOpenWithExts(template.Image, new[] { ext }, out s)) + continue; + + // Rewrite the template image (normally readonly) using reflection + imageField.SetValue(template, template.Image + ext); + + // Fetch the private tileInfo array so that we can write new entries + var tileInfo = (TerrainTileInfo[])tileInfoField.GetValue(template); + + // Open the file and search for any implicit frames + var allFrames = frameCache[template.Image]; + var frames = template.Frames != null ? template.Frames.Select(f => allFrames[f]).ToArray() : allFrames; + + // Resize array for new entries + if (frames.Length > template.TilesCount) + { + var oldLength = template.TilesCount; + var ti = new TerrainTileInfo[frames.Length]; + Array.Copy(tileInfo, ti, template.TilesCount); + tileInfoField.SetValue(template, ti); + tileInfo = ti; + } + + for (var i = 0; i < template.TilesCount; i++) + { + if (template[i] == null && frames[i] != null && frames[i].Size != empty) + { + tileInfo[i] = new TerrainTileInfo(); + var ti = ts.GetTerrainIndex("Clear"); + terrainTypeField.SetValue(tileInfo[i], ti); + terrainLeftColorField.SetValue(tileInfo[i], ts[ti].Color); + terrainRightColorField.SetValue(tileInfo[i], ts[ti].Color); + Console.WriteLine("Fixing entry for {0}:{1}", template.Image, i); + } + } + + if (template.TilesCount > 1 && template.Size == single) + pickAnyField.SetValue(template, true); + + s.Dispose(); + } + } + + ts.Save(t); + } + } + } +}