Move Shp conversion/remap commands to Mods.Cnc
This commit is contained in:
58
OpenRA.Mods.Cnc/UtilityCommands/ConvertPngToShpCommand.cs
Normal file
58
OpenRA.Mods.Cnc/UtilityCommands/ConvertPngToShpCommand.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
using OpenRA.Mods.Common.SpriteLoaders;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.UtilityCommands
|
||||
{
|
||||
class ConvertPngToShpCommand : IUtilityCommand
|
||||
{
|
||||
string IUtilityCommand.Name { get { return "--shp"; } }
|
||||
|
||||
bool IUtilityCommand.ValidateArguments(string[] args)
|
||||
{
|
||||
return args.Length >= 2;
|
||||
}
|
||||
|
||||
[Desc("PNGFILE [PNGFILE ...]", "Combine a list of PNG images into a SHP")]
|
||||
void IUtilityCommand.Run(Utility utility, string[] args)
|
||||
{
|
||||
var inputFiles = GlobArgs(args).OrderBy(a => a).ToList();
|
||||
var dest = inputFiles[0].Split('-').First() + ".shp";
|
||||
|
||||
var frames = inputFiles.Select(a => new Png(File.OpenRead(a))).ToList();
|
||||
if (frames.Any(f => f.Palette == null))
|
||||
throw new InvalidOperationException("All frames must be paletted");
|
||||
|
||||
var size = new Size(frames[0].Width, frames[0].Height);
|
||||
if (frames.Any(f => f.Width != size.Width || f.Height != size.Height))
|
||||
throw new InvalidOperationException("All frames must be the same size");
|
||||
|
||||
using (var destStream = File.Create(dest))
|
||||
ShpTDSprite.Write(destStream, size, frames.Select(f => f.Data));
|
||||
|
||||
Console.WriteLine(dest + " saved.");
|
||||
}
|
||||
|
||||
static IEnumerable<string> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
127
OpenRA.Mods.Cnc/UtilityCommands/Glob.cs
Normal file
127
OpenRA.Mods.Cnc/UtilityCommands/Glob.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.UtilityCommands
|
||||
{
|
||||
public static class Glob
|
||||
{
|
||||
public static bool Enabled = true;
|
||||
|
||||
static readonly char[] GlobChars = new char[] { '*', '?' };
|
||||
static readonly char[] DirectorySeparators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
|
||||
|
||||
static bool NeedsExpansion(string filePath)
|
||||
{
|
||||
if (!Enabled)
|
||||
return false;
|
||||
|
||||
return filePath.IndexOfAny(GlobChars) >= 0;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> Expand(string filePath)
|
||||
{
|
||||
if (!NeedsExpansion(filePath))
|
||||
{
|
||||
yield return filePath;
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Split using DirectorySeparators but keep the separators
|
||||
var parts = new List<string>();
|
||||
|
||||
for (var startIndex = 0; startIndex < filePath.Length;)
|
||||
{
|
||||
var index = filePath.IndexOfAny(DirectorySeparators, startIndex);
|
||||
if (index == -1)
|
||||
{
|
||||
parts.Add(filePath.Substring(startIndex));
|
||||
break;
|
||||
}
|
||||
|
||||
parts.Add(filePath.Substring(startIndex, index - startIndex + 1));
|
||||
startIndex = index + 1;
|
||||
}
|
||||
|
||||
if (parts.Count > 0 && (parts[0] == "." || parts[0] == ".."))
|
||||
parts[0] += Path.DirectorySeparatorChar;
|
||||
|
||||
// If it's empty
|
||||
// or if
|
||||
// it's not rooted
|
||||
// and it doesn't start with "./" or "../"
|
||||
// prepend "./"
|
||||
if (parts.Count == 0
|
||||
|| (parts[0][0] != Path.DirectorySeparatorChar
|
||||
&& parts[0][0] != Path.AltDirectorySeparatorChar
|
||||
&& parts[0].Contains(':') == false
|
||||
&& parts[0] != "." + Path.DirectorySeparatorChar
|
||||
&& parts[0] != "." + Path.AltDirectorySeparatorChar
|
||||
&& parts[0] != ".." + Path.DirectorySeparatorChar
|
||||
&& parts[0] != ".." + Path.AltDirectorySeparatorChar))
|
||||
parts.Insert(0, "." + Path.DirectorySeparatorChar);
|
||||
|
||||
// If the last entry ends with a directory separator, append a '*'
|
||||
if (parts[parts.Count - 1][parts[parts.Count - 1].Length - 1] == Path.DirectorySeparatorChar
|
||||
|| parts[parts.Count - 1][parts[parts.Count - 1].Length - 1] == Path.AltDirectorySeparatorChar)
|
||||
parts.Add("*");
|
||||
|
||||
var root = parts[0];
|
||||
var dirs = parts.Skip(1).Take(parts.Count - 2).ToList();
|
||||
var file = parts[parts.Count - 1];
|
||||
|
||||
foreach (var path in Expand(root, dirs, 0, file))
|
||||
yield return path;
|
||||
}
|
||||
|
||||
static IEnumerable<string> Expand(string basePath, IList<string> dirs, int dirIndex, string file)
|
||||
{
|
||||
if (dirIndex < dirs.Count)
|
||||
{
|
||||
var dir = dirs[dirIndex];
|
||||
|
||||
if (!NeedsExpansion(dir))
|
||||
{
|
||||
var path = Path.Combine(basePath, dir);
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
yield break;
|
||||
else
|
||||
{
|
||||
foreach (var s in Expand(path, dirs, dirIndex + 1, file))
|
||||
yield return s;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (dir[dir.Length - 1] == Path.DirectorySeparatorChar || dir[dir.Length - 1] == Path.AltDirectorySeparatorChar)
|
||||
dir = dir.Substring(0, dir.Length - 1);
|
||||
foreach (var subDir in Directory.EnumerateDirectories(basePath, dir, SearchOption.TopDirectoryOnly))
|
||||
foreach (var s in Expand(subDir, dirs, dirIndex + 1, file))
|
||||
yield return s;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!NeedsExpansion(file))
|
||||
yield return Path.Combine(basePath, file);
|
||||
else
|
||||
{
|
||||
foreach (var s in Directory.GetFiles(basePath, file, SearchOption.TopDirectoryOnly))
|
||||
yield return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
89
OpenRA.Mods.Cnc/UtilityCommands/RemapShpCommand.cs
Normal file
89
OpenRA.Mods.Cnc/UtilityCommands/RemapShpCommand.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2019 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, either version 3 of
|
||||
* the License, or (at your option) any later version. For more
|
||||
* information, see COPYING.
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.SpriteLoaders;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Cnc.UtilityCommands
|
||||
{
|
||||
class RemapShpCommand : IUtilityCommand
|
||||
{
|
||||
string IUtilityCommand.Name { get { return "--remap"; } }
|
||||
|
||||
bool IUtilityCommand.ValidateArguments(string[] args)
|
||||
{
|
||||
return args.Length >= 5;
|
||||
}
|
||||
|
||||
[Desc("SRCMOD:PAL DESTMOD:PAL SRCSHP DESTSHP", "Remap SHPs to another palette")]
|
||||
void IUtilityCommand.Run(Utility utility, string[] args)
|
||||
{
|
||||
var remap = new Dictionary<int, int>();
|
||||
|
||||
/* the first 4 entries are fixed */
|
||||
for (var i = 0; i < 4; i++)
|
||||
remap[i] = i;
|
||||
|
||||
var srcMod = args[1].Split(':')[0];
|
||||
var srcModData = new ModData(utility.Mods[srcMod], utility.Mods);
|
||||
Game.ModData = srcModData;
|
||||
|
||||
var srcPaletteInfo = srcModData.DefaultRules.Actors["player"].TraitInfo<PlayerColorPaletteInfo>();
|
||||
var srcRemapIndex = srcPaletteInfo.RemapIndex;
|
||||
|
||||
var destMod = args[2].Split(':')[0];
|
||||
var destModData = new ModData(utility.Mods[destMod], utility.Mods);
|
||||
Game.ModData = destModData;
|
||||
var destPaletteInfo = destModData.DefaultRules.Actors["player"].TraitInfo<PlayerColorPaletteInfo>();
|
||||
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]));
|
||||
|
||||
using (var s = File.OpenRead(args[3]))
|
||||
using (var destStream = File.Create(args[4]))
|
||||
{
|
||||
var srcImage = new ShpTDSprite(s);
|
||||
ShpTDSprite.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(ca.R - cb.R) +
|
||||
Math.Abs(ca.G - cb.G) +
|
||||
Math.Abs(ca.B - cb.B);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user