Files renamed to RecrownedGTK.

This commit is contained in:
2020-02-16 21:44:21 -05:00
parent 2c62be1c6b
commit dee5f96ea7
69 changed files with 5891 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace RecrownedGTK.Tools.CommandProcessor
{
internal class CommandEngine
{
public bool running = true;
public readonly List<EngineCommand> commands;
public CommandEngine()
{
commands = new List<EngineCommand>();
}
internal void Run()
{
while (running)
{
ConsoleUtilities.WriteWrappedLine("\nAwaiting command.");
string command = Console.ReadLine();
try
{
Process(command);
}
catch (ArgumentException ae)
{
ConsoleUtilities.WriteWrappedLine("Error: " + ae.Message);
}
}
}
public void Process(string commandAndArguments)
{
string command = commandAndArguments;
string[] arguments = null;
if (commandAndArguments.Contains(' '))
{
command = command.Remove(command.IndexOf(' '));
if (!ContainsCommand(command)) throw new ArgumentException("Command not found.");
string[] argumentsSplit = commandAndArguments.Substring(command.Length + 1).Split(' ');
List<string> argumentsList = new List<string>();
for (int i = 0; i < argumentsSplit.Length; i++)
{
if (argumentsSplit[i].Contains('"') && !argumentsSplit[i].EndsWith('"'))
{
StringBuilder quoteBuilder = new StringBuilder();
do
{
quoteBuilder.Append(' ');
quoteBuilder.Append(argumentsSplit[i]);
i++;
if (i >= argumentsSplit.Length)
{
throw new ArgumentException("Argument is missing terminating \'\"\'");
}
} while (!argumentsSplit[i].Contains('"'));
quoteBuilder.Append(' ');
quoteBuilder.Append(argumentsSplit[i]);
argumentsList.Add(quoteBuilder.ToString().Replace("\"", "").Trim());
}
else
{
argumentsList.Add(argumentsSplit[i]);
}
}
arguments = argumentsList.ToArray();
}
if (!ContainsCommand(command)) throw new ArgumentException("Command not found. Type \"help\" for a list of commands.");
GetCommand(command).Run(arguments);
}
public bool ContainsCommand(string command)
{
for (int i = 0; i < commands.Count; i++)
{
if (commands[i].IsInvoked(command)) return true;
}
return false;
}
public EngineCommand GetCommand(string command)
{
for (int i = 0; i < commands.Count; i++)
{
if (commands[i].IsInvoked(command)) return commands[i];
}
return null;
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
namespace RecrownedGTK.Tools.CommandProcessor.Commands
{
internal class ClearConsoleCommand : EngineCommand
{
public ClearConsoleCommand() : base("clear")
{
help = "Clears the console.";
}
public override void Run(string[] arguments = null)
{
Console.Clear();
}
}
}

View File

@@ -0,0 +1,53 @@
using System;
namespace RecrownedGTK.Tools.CommandProcessor.Commands
{
internal class HelpCommand : EngineCommand
{
CommandEngine commandEngine;
public HelpCommand(CommandEngine commandEngine) : base("help")
{
this.commandEngine = commandEngine;
help = "help [command] [arg]";
}
public override void Run(string[] arguments)
{
if (arguments != null)
{
if (commandEngine.ContainsCommand(arguments[0]))
{
if (arguments.Length < 2) ConsoleUtilities.WriteWrappedLine(commandEngine.GetCommand(arguments[0]).Help(null));
for (int i = 1; i < arguments.Length; i++)
{
ConsoleUtilities.WriteWrappedLine(commandEngine.GetCommand(arguments[0]).Help(arguments[i]));
}
}
else
{
throw new ArgumentException(arguments[0] + " not a command. Type \"help\" for a list of commands.");
}
}
else
{
ConsoleUtilities.WriteWrappedLine("Tools for RecrownedGTK library. Possible commands are as follows:\n");
foreach (EngineCommand engineCommand in commandEngine.commands)
{
for (int i = 0; i < engineCommand.InvokeStrings.Length; i++)
{
ConsoleUtilities.WriteWrapped(engineCommand.InvokeStrings[i]);
if (i + 1 < engineCommand.InvokeStrings.Length)
{
ConsoleUtilities.WriteWrapped(", ");
}
}
ConsoleUtilities.WriteWrapped(" : ");
ConsoleUtilities.WriteWrapped(engineCommand.Help().Replace("\n", "\n\t"), true);
Console.WriteLine("--------");
}
}
}
}
}

View File

@@ -0,0 +1,18 @@
namespace RecrownedGTK.Tools.CommandProcessor.Commands
{
internal class StopCommand : EngineCommand
{
CommandEngine commandEngine;
public StopCommand(CommandEngine commandEngine) : base("quit", "stop", "q", "exit")
{
this.commandEngine = commandEngine;
help = "Exits the tool.";
}
public override void Run(string[] arguments = null)
{
commandEngine.running = false;
}
}
}

View File

@@ -0,0 +1,183 @@
using System;
using System.Text;
namespace RecrownedGTK.Tools.CommandProcessor
{
public abstract class EngineCommand
{
bool caseSensitiveName = false;
/// <summary>
/// the words a user can type that will invoke this command.
/// </summary>
protected string[] invokeStrings;
public string[] InvokeStrings { get { return invokeStrings; } }
/// <summary>
/// Arguments that this command should accept and take into account.
/// </summary>
protected EngineCommandArgument[] commandArguments;
protected string help;
public EngineCommand(params string[] invokeStrings)
{
this.invokeStrings = invokeStrings ?? throw new ArgumentNullException();
}
/// <summary>
/// Whether or not this command should be invoked given the string.
/// </summary>
/// <param name="command">The string that acts as the command.</param>
/// <returns>whether or not this command is invoked.</returns>
public bool IsInvoked(string command)
{
if (!caseSensitiveName) command = command.ToLower();
for (int i = 0; i < invokeStrings.Length; i++)
{
if (!caseSensitiveName)
{
if (invokeStrings[i].ToLower() == command) return true;
}
else
{
if (invokeStrings[i] == command) return true;
}
}
return false;
}
/// <summary>
/// Runs the command.
/// </summary>
/// <param name="arguments">arguments to be used. May be null.</param>
public abstract void Run(string[] arguments = null);
/// <summary>
/// Check if given argument is viable for command.
/// </summary>
/// <param name="argument">Argument to check.</param>
/// <returns>True if valid.</returns>
public bool Validate(string argument)
{
return EngineCommandArgumentOf(argument) == null ? false : true;
}
/// <summary>
/// Returns the <see cref="EngineCommandArgument"/> of the given argument or null if the string is an invalid argument.
/// </summary>
/// <param name="argument">The argument string.</param>
/// <returns>null if not viable or the <see cref="EngineCommandArgumentOf(argument)"/> if viable.</returns>
public EngineCommandArgument EngineCommandArgumentOf(string argument)
{
for (int i = 0; i < commandArguments.Length; i++)
{
if (commandArguments[i].invokeString == argument)
{
return commandArguments[i];
}
}
return null;
}
/// <summary>
/// Finds the index of the argument given in an array of arguments for the command.
/// </summary>
/// <param name="argument">The argument to find the index of.</param>
/// <param name="arguments">The array containing all arguments.</param>
/// <returns>The index or throws argument exception if it doesn't exist.</returns>
public int IndexOfArgument(string argument, string[] arguments)
{
if (arguments != null)
{
for (int i = 0; i < arguments.Length; i++)
{
if (arguments[i] == argument)
{
return i;
}
}
}
throw new ArgumentException("Expected argument " + argument + " is missing. Type \"help\" for more information.");
}
public bool HasArgument(string argument, string[] arguments)
{
try
{
IndexOfArgument(argument, arguments);
}
catch (ArgumentException)
{
return false;
}
return true;
}
public bool HasArgument(EngineCommandArgument argument, string[] arguments)
{
return HasArgument(argument.invokeString, arguments);
}
/// <summary>
/// Finds the index of the argument given in an array of arguments for the command.
/// </summary>
/// <param name="argument">The argument to find the index of.</param>
/// <param name="arguments">The array containing all arguments.</param>
/// <returns>The index or throws argument exception if it doesn't exist.</returns>
public int IndexOfArgumentIn(EngineCommandArgument argument, string[] arguments)
{
return IndexOfArgument(argument.invokeString, arguments);
}
/// <summary>
/// Called when help "command trigger" [argument] is invoked. Argument is optional and therefore, may be null. If the valid arguments are properly registered, this command will take care of it. If no arguments are provided, it will return the general help defined for this command.
/// </summary>
/// <param name="argument">Potential argument to request help for.</param>
/// <returns>The string for the help.</returns>
public string Help(string argument = null)
{
if (argument != null && commandArguments != null)
{
if (Validate(argument))
{
EngineCommandArgument eca = EngineCommandArgumentOf(argument);
string helpString = eca.help;
if (eca.required) helpString = helpString + " required.";
return EngineCommandArgumentOf(argument).help;
}
else
{
return "The argument " + argument + " does not exist. Type \"help " + invokeStrings[0] + "\" (or any of its aliases) for a list of arguments.";
}
}
else
{
StringBuilder helpBuilder = new StringBuilder();
helpBuilder.Append(help);
if (commandArguments != null)
{
helpBuilder.AppendLine();
helpBuilder.Append("Possible arguments are: ");
for (int i = 0; i < commandArguments.Length; i++)
{
helpBuilder.Append(commandArguments[i].invokeString);
if (commandArguments[i].required) helpBuilder.Append('*');
if (i == commandArguments.Length - 2)
{
helpBuilder.Append(", and ");
}
else if (i < commandArguments.Length - 2)
{
helpBuilder.Append(", ");
}
}
helpBuilder.Append('.');
helpBuilder.AppendLine(" [* are required arguments.]");
}
return helpBuilder.ToString();
}
}
}
}

View File

@@ -0,0 +1,15 @@
namespace RecrownedGTK.Tools.CommandProcessor
{
public class EngineCommandArgument
{
public string invokeString;
public string help;
public bool required;
public EngineCommandArgument(string invokeString, string help, bool required = false)
{
this.invokeString = invokeString;
this.help = help;
this.required = required;
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Text;
namespace RecrownedGTK.Tools
{
internal class ConsoleUtilities
{
public static void WriteWrapped(string message, bool line = false)
{
message = WrapMessageToConsoleWidth(message);
if (line) message = message + "\n";
Console.Write(message);
}
public static string WrapMessageToConsoleWidth(string message)
{
string[] words = message.Split(' ');
StringBuilder stringBuilder = new StringBuilder();
int currentLineSize = 0;
for (int i = 0; i < words.Length; i++)
{
if (currentLineSize + words[i].Length >= Console.BufferWidth - Console.CursorLeft -1)
{
currentLineSize = 0;
stringBuilder.AppendLine();
}
if (words[i].Contains("\n"))
{
currentLineSize = 0;
}
currentLineSize += words[i].Length + 1;
if (words[i].Contains("\n"))
{
currentLineSize -= 2;
}
stringBuilder.Append(words[i]);
if (i + 1 < words.Length)
{
stringBuilder.Append(' ');
}
}
return stringBuilder.ToString();
}
public static void WriteWrappedLine(string message)
{
WriteWrapped(message, true);
}
}
}

View File

@@ -0,0 +1,58 @@
using Newtonsoft.Json;
using RecrownedGTK.Data;
using RecrownedGTK.Tools.CommandProcessor;
using System;
using System.IO;
namespace RecrownedGTK.Tools.NinePatchTools
{
public class NinePatchCommand : EngineCommand
{
public NinePatchCommand() : base("9p", "ninepatch", "9patch")
{
help = "Generates a 9 patch file for a given image.";
commandArguments = new EngineCommandArgument[6];
commandArguments[0] = new EngineCommandArgument("-i", "defines the path to the texture to be used for the nine patch.", true);
commandArguments[1] = new EngineCommandArgument("-o", "defines path of output file.");
commandArguments[2] = new EngineCommandArgument("-l", "left patch.", true);
commandArguments[3] = new EngineCommandArgument("-r", "right patch.", true);
commandArguments[4] = new EngineCommandArgument("-t", "top patch.", true);
commandArguments[5] = new EngineCommandArgument("-b", "bottom patch.", true);
}
public override void Run(string[] arguments = null)
{
if (arguments == null) throw new ArgumentException("Missing arguments. Type \"help 9p\" for more information.");
int leftBound = 0, rightBound = 0, topBound = 0, bottomBound = 0;
string imagePath, outPath;
if (IndexOfArgument("-i", arguments) + 1 >= arguments.Length) throw new ArgumentException("Missing -i path after argument.");
imagePath = arguments[IndexOfArgument("-i", arguments) + 1];
if (!File.Exists(imagePath)) throw new ArgumentException("Input file does not exist at " + imagePath + ".");
if (HasArgument(commandArguments[1], arguments))
{
if (IndexOfArgument("-o", arguments) + 1 >= arguments.Length) throw new ArgumentException("Missing -o path after argument.");
outPath = arguments[IndexOfArgument("-o", arguments) + 1];
} else
{
outPath = imagePath.Substring(0, imagePath.Length - Path.GetExtension(imagePath).Length);
}
if (IndexOfArgument("-l", arguments) + 1 >= arguments.Length || !int.TryParse(arguments[IndexOfArgument("-l", arguments) + 1], out leftBound)) throw new ArgumentException("Missing -l argument bound.");
if (IndexOfArgument("-r", arguments) + 1 >= arguments.Length || !int.TryParse(arguments[IndexOfArgument("-r", arguments) + 1], out rightBound)) throw new ArgumentException("Missing -r argument bound.");
if (IndexOfArgument("-t", arguments) + 1 >= arguments.Length || !int.TryParse(arguments[IndexOfArgument("-t", arguments) + 1], out topBound)) throw new ArgumentException("Missing -u argument bound.");
if (IndexOfArgument("-b", arguments) + 1 >= arguments.Length || !int.TryParse(arguments[IndexOfArgument("-b", arguments) + 1], out bottomBound)) throw new ArgumentException("Missing -d argument bound.");
NinePatchData npData = new NinePatchData(Path.GetFileName(imagePath), leftBound, rightBound, bottomBound, topBound);
string serialized = JsonConvert.SerializeObject(npData, Formatting.Indented);
string modifiedPath = Directory.GetParent(imagePath) + Path.PathSeparator.ToString() + Path.GetFileNameWithoutExtension(imagePath) + "-texture" + Path.GetExtension(imagePath);
File.Move(imagePath, modifiedPath);
File.WriteAllText(outPath + ".9p", serialized);
ConsoleUtilities.WriteWrappedLine("Done. Written to \"" + outPath + "\" with values: left = " + leftBound + " right = " + rightBound + " top = " + topBound + " bottom = " + bottomBound);
ConsoleUtilities.WriteWrappedLine("Image renamed to: " + Path.GetFileName(modifiedPath));
}
}
}

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<ApplicationIcon />
<StartupObject />
<AssemblyName>RecrownedGTK.Tools</AssemblyName>
<RootNamespace>RecrownedGTK.Tools</RootNamespace>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" PrivateAssets="All"/>
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.0-beta0005" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta0005" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RecrownedGTK\RecrownedGTK.csproj" PrivateAssets="All"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,292 @@
using Newtonsoft.Json;
using RecrownedGTK.Data;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using SixLabors.Primitives;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security;
namespace RecrownedGTK.Tools.TextureAtlas
{
public class TexturePacker
{
private enum SupportedExtensions
{
jpeg, jpg, png
}
int powLimit;
Node masterNode;
List<ImageHandler> imageHandlers;
Dictionary<string, NinePatchData> ninePatchDictionary;
int tpl;
int TexturePowerLength { get { return tpl; } set { TextureLength = (int)Math.Pow(2, value); tpl = value; } }
public int TextureLength { get; private set; }
public int TexturesFound { get { return imageHandlers.Count; } }
/// <summary>
/// Machine to pack multiple textures into one large texture.
/// </summary>
/// <param name="rootDirectoryPath">Path to textures.</param>
/// <param name="powLimit">Power of two limit for auto expanding texture. Default is 12 which is a 4096x4096 texture.</param>
/// <param name="startingPower">What power to start at and build up from. Default is 8 which is a 256x256 texture.</param>
internal TexturePacker(string rootDirectoryPath, int powLimit = 12, int startingPower = 8)
{
this.powLimit = powLimit;
string[] paths;
try
{
paths = Directory.GetFiles(rootDirectoryPath);
}
catch (IOException)
{
throw new ArgumentException("Path " + rootDirectoryPath + " couldn't be resolved.");
}
TexturePowerLength = startingPower;
List<ImageHandler> imageHandlers = new List<ImageHandler>();
int minAreaRequired = 0;
for (int pathID = 0; pathID < paths.Length; pathID++)
{
SupportedExtensions extension;
if (Enum.TryParse(Path.GetExtension(paths[pathID]).ToLower().Substring(1), out extension))
{
ConsoleUtilities.WriteWrappedLine("Reading texture data for: " + paths[pathID]);
ImageHandler image = new ImageHandler(paths[pathID]);
imageHandlers.Add(image);
minAreaRequired += image.Area;
while (minAreaRequired > TextureLength * TextureLength)
{
TexturePowerLength++;
}
}
else if (Path.GetExtension(paths[pathID]).ToLower() == ".9p")
{
if (ninePatchDictionary == null) ninePatchDictionary = new Dictionary<string, NinePatchData>();
ConsoleUtilities.WriteWrappedLine("Reading ninepatch data for: " + paths[pathID]);
string serialized = File.ReadAllText(paths[pathID]);
NinePatchData npData = JsonConvert.DeserializeObject<NinePatchData>(serialized);
ninePatchDictionary.Add(npData.textureName, npData);
}
}
imageHandlers.Sort();
this.imageHandlers = imageHandlers;
}
/// <summary>
/// Builds a texture atlas.
/// </summary>
/// <param name="AutoCorrectAtlasSize">Whether or not to automatically upscale atlas' texture in the case it is too small. Goes up to 4096 by default.</param>
public void Build(bool AutoCorrectAtlasSize = true)
{
masterNode = new Node();
masterNode.region.Width = TextureLength;
masterNode.region.Height = TextureLength;
Queue<ImageHandler> imageHandlerQueue = new Queue<ImageHandler>(imageHandlers);
ImageHandler imageHandler;
while (imageHandlerQueue.TryDequeue(out imageHandler))
{
Node activeNode = null;
activeNode = masterNode.InsertImageHandler(imageHandler);
if (activeNode == null)
{
if (!AutoCorrectAtlasSize || TexturePowerLength + 1 > powLimit)
{
throw new InvalidOperationException("Texture not large enough. Current size: " + TextureLength + "x" + TextureLength + ".");
}
TexturePowerLength += 1;
imageHandlerQueue.Clear();
Build(AutoCorrectAtlasSize);
}
if (ninePatchDictionary != null && ninePatchDictionary.ContainsKey(imageHandler.name))
{
NinePatchData npd = ninePatchDictionary[imageHandler.name];
imageHandler.ninePatchData = npd;
if (npd.textureName.Contains("-texture"))
{
imageHandler.name = imageHandler.name.Remove(imageHandler.name.IndexOf("-texture"), 8);
}
}
}
}
/// <summary>
/// Renders the build into a PNG file and generates the respective <see cref="TextureAtlasData"/> meant for serialization and later to be loaded.
/// </summary>
/// <param name="output">directory to output to.</param>
/// <param name="atlasName">name of atlas.</param>
public void Save(string output, string atlasName)
{
GraphicsOptions gOptions = new GraphicsOptions();
TextureAtlasData.AtlasRegionData[] regions = new TextureAtlasData.AtlasRegionData[TexturesFound];
using (Image<Rgba32> atlasTexture = new Image<Rgba32>(TextureLength, TextureLength))
{
ImageHandler[] imageHandlers = this.imageHandlers.ToArray();
for (int i = 0; i < imageHandlers.Length; i++)
{
regions[i] = new TextureAtlasData.AtlasRegionData();
ImageHandler ih = imageHandlers[i];
regions[i].SetBounds(ih.x, ih.y, ih.Width, ih.Height);
regions[i].ninePatchData = ih.ninePatchData;
if (regions[i].ninePatchData != null) regions[i].ninePatchData.textureName = null;
regions[i].name = Path.GetFileNameWithoutExtension(ih.name);
using (Image<Rgba32> image = Image.Load<Rgba32>(ih.path))
{
atlasTexture.Mutate(img => img.DrawImage(gOptions, image, new Point(ih.x, ih.y)));
}
}
Directory.CreateDirectory(output);
using (FileStream stream = new FileStream(output + "/" + atlasName + "-texture" + ".png", FileMode.Create))
{
atlasTexture.SaveAsPng(stream);
}
}
string serialized = JsonConvert.SerializeObject(new TextureAtlasData(atlasName + "-texture" + ".png", regions), Formatting.Indented);
File.WriteAllText(output + "/" + atlasName + ".tatlas", serialized);
}
public void SetNinePatch(string fileName, int a, int b, int c, int d)
{
NinePatchData ninePatchData = new NinePatchData(fileName, a, b, c, d);
RetrieveImageHandler(fileName).ninePatchData = ninePatchData;
}
public void RemoveNinePatch(string name)
{
RetrieveImageHandler(name).ninePatchData = null;
}
private ImageHandler RetrieveImageHandler(string name)
{
for (int i = 0; i < TexturesFound; i++)
{
if (imageHandlers[i].name == name)
{
return imageHandlers[i];
}
}
throw new ArgumentException("Couldn't find texture with name: " + name);
}
private class Node
{
public Node parent;
private Node a, b;
public Node childA { get { if (a == null) a = new Node(this); return a; } set { value.parent = this; a = value; } }
public Node childB { get { if (b == null) { b = new Node(this); } return b; } set { value.parent = this; b = value; } }
public Rectangle region;
public bool ContainsImage = false;
public bool CanPlaceImage { get { return (a == null && b == null && !ContainsImage); } }
public Node(Node parent = null)
{
this.parent = parent;
if (parent != null) region = parent.region;
}
/// <summary>
/// Attempts to insert image within the node. This builds the node to have children if needed.
/// </summary>
/// <param name="imageHandler">the image to insert.</param>
/// <returns>The node the image is placed in.</returns>
public Node InsertImageHandler(ImageHandler imageHandler)
{
if (imageHandler.Width != region.Width)
{
if (imageHandler.Width < region.Width)
{
if (a == null)
{
childA.region.Width = imageHandler.Width;
}
Node attemptedNode = null;
if (!childA.ContainsImage && imageHandler.Width <= childA.region.Width)
{
attemptedNode = childA.InsertImageHandler(imageHandler);
}
if (attemptedNode == null && !childB.ContainsImage)
{
childB.region.Width = region.Width - childA.region.Width;
childB.region.X = childA.region.X + childA.region.Width;
attemptedNode = childB.InsertImageHandler(imageHandler);
}
return attemptedNode;
}
}
else if (imageHandler.Height != region.Height)
{
if (imageHandler.Height < region.Height)
{
if (a == null)
{
childA.region.Height = imageHandler.Height;
}
Node attemptedNode = null;
if (!childA.ContainsImage && imageHandler.Height <= childA.region.Height)
{
attemptedNode = childA.InsertImageHandler(imageHandler);
}
if (attemptedNode == null && !childB.ContainsImage)
{
childB.region.Height = region.Height - childA.region.Height;
childB.region.Y = childA.region.Y + childA.region.Height;
attemptedNode = childB.InsertImageHandler(imageHandler);
}
return attemptedNode;
}
}
else if (CanPlaceImage)
{
imageHandler.x = region.X;
imageHandler.y = region.Y;
ContainsImage = true;
return this;
}
return null;
}
}
private class ImageHandler : IComparable<ImageHandler>
{
public readonly string path;
public readonly IImageInfo image;
public int Area { get { return image.Width * image.Height; } }
public string name;
public int Width { get { return image.Width; } }
public int Height { get { return image.Height; } }
public int x, y;
public NinePatchData ninePatchData;
internal ImageHandler(string path)
{
this.path = path;
name = Path.GetFileName(path);
try
{
using (FileStream stream = new FileStream(path, FileMode.Open))
{
image = Image.Identify(stream);
}
} catch (SecurityException)
{
throw new ArgumentException("Security exception occurred for image: " + path);
}
}
public int CompareTo(ImageHandler tImage)
{
return Area - tImage.Area;
}
}
}
}

View File

@@ -0,0 +1,118 @@
using RecrownedGTK.Tools.CommandProcessor;
using RecrownedGTK.Tools.TextureAtlas;
using System;
using System.IO;
namespace RecrownedGTK.Tools.TextureAtlasTools
{
class TexturePackerCommand : EngineCommand
{
private const int DMP = 9;
private const int DSP = 6;
public TexturePackerCommand() : base("texturepacker")
{
help = "Packs a given directory composed of png and jpg files into an atlas. Can also add 9patch properties. Images with the associated \".9p\" files will automatically be defined in the resulting .tatlas file, but can be overwritten by use of the \"-9p\" argument.";
commandArguments = new[] {
new EngineCommandArgument("-interactive", "runs in interactive mode. Ninepatches must still be defined with arguments or previously defined with associated \".9p\" files. Other arguments will be ignored."),
new EngineCommandArgument("-i", "for input directory containing the textures.", true),
new EngineCommandArgument("-o", "Path for output files. Points to non-existent file. Will create texture and definitions file with name.", true),
new EngineCommandArgument("-9p", "Can be used multiple times for defining a 9patch. This parameter requires a name, left patch, right patch, top patch, and bottom patch in the format \"name,a,b,c,d\"."),
new EngineCommandArgument("-sp", "Starting power for one side of the texture. Default is " + DSP + "."),
new EngineCommandArgument("-mp", "Maximum power for one side of the texture. Default is " + DMP + "."),
new EngineCommandArgument("-dau", "Disables automatically upscaling the texture."),
};
}
public override void Run(string[] arguments)
{
TexturePacker texturePacker = null;
string path = null;
int mp = DMP;
int sp = DSP;
bool dau = false;
string output = null;
if (HasArgument("-interactive", arguments))
{
string input;
ConsoleUtilities.WriteWrappedLine("Texture packer interactive mode triggered. Type \"q\" at any time to exit.");
ConsoleUtilities.WriteWrappedLine("Please enter path of folder containing the textures to be packed.");
input = Console.ReadLine();
if (input == "q") return;
path = input.Replace("\"", "");
ConsoleUtilities.WriteWrappedLine("Please enter output path of file name.");
input = Console.ReadLine();
if (input == "q") return;
output = input.Replace("\"", "");
do
{
ConsoleUtilities.WriteWrappedLine("Please enter a valid starting power of two for the lengths of the resulting texture atlas. Leave blank for default of " + sp + ".");
input = Console.ReadLine();
if (input == "q") return;
if (input.Length == 0) break;
} while (!int.TryParse(input, out sp));
do
{
ConsoleUtilities.WriteWrappedLine("Please enter a valid maximum power by two of the lengths of the resulting texture atlas. Leave blank for default of " + mp + ".");
input = Console.ReadLine();
if (input == "q") return;
if (input.Length == 0) break;
} while (!int.TryParse(input, out mp));
}
else
{
int indexOfInputArg = IndexOfArgument("-i", arguments);
if (indexOfInputArg + 1 >= arguments.Length) throw new ArgumentException("-i is not followed by input path.");
path = arguments[1 + IndexOfArgument("-i", arguments)];
if (HasArgument("-mp", arguments))
{
int.TryParse(arguments[IndexOfArgument("-mp", arguments) + 1], out mp);
}
if (HasArgument("-sp", arguments))
{
int.TryParse(arguments[IndexOfArgument("-sp", arguments) + 1], out sp);
}
int indexOfOutputArg = IndexOfArgument("-o", arguments);
if (indexOfOutputArg + 1 >= arguments.Length) throw new ArgumentException("-o is not followed by input path.");
output = arguments[IndexOfArgument("-o", arguments) + 1];
if (HasArgument("-dau", arguments)) dau = true;
}
texturePacker = new TexturePacker(path, mp, sp);
ConsoleUtilities.WriteWrappedLine("Calculated minimum texture size: " + texturePacker.TextureLength + "x" + texturePacker.TextureLength + " with a total of " + texturePacker.TexturesFound + " textures.");
try
{
texturePacker.Build(!dau);
}
catch (InvalidOperationException e)
{
throw new ArgumentException(e.Message);
}
for (int i = 0; i < arguments.Length; i++)
{
if (arguments[i] == "-9p")
{
if (i + 1 >= arguments.Length) throw new ArgumentException("-9p is not followed by proper specifiers for a 9Patch (format: \"-9p textureName,a,b,c,d\" where a, b, c, and d are integers definining the border regions for the 9patch.)");
string[] nPatchArgs = arguments[i + 1].Split(',');
try
{
texturePacker.SetNinePatch(nPatchArgs[0], int.Parse(nPatchArgs[1]), int.Parse(nPatchArgs[2]), int.Parse(nPatchArgs[3]), int.Parse(nPatchArgs[4]));
}
catch (FormatException)
{
throw new ArgumentException("-9p argument parameters must be in the format \"-9p textureName,a,b,c,d\" where a, b, c, and d are integers definining the border regions for the 9patch.");
}
}
}
texturePacker.Save(Path.GetDirectoryName(output), Path.GetFileName(output));
Console.WriteLine("Complete. Final texture size: " + texturePacker.TextureLength + "x" + texturePacker.TextureLength + ".");
}
}
}

View File

@@ -0,0 +1,47 @@
using RecrownedGTK.Tools.CommandProcessor;
using RecrownedGTK.Tools.CommandProcessor.Commands;
using RecrownedGTK.Tools.NinePatchTools;
using RecrownedGTK.Tools.TextureAtlasTools;
using System;
using System.Reflection;
using System.Text;
namespace RecrownedGTK.Tools
{
internal class Tools
{
static void Main(string[] args)
{
CommandEngine ce = new CommandEngine();
ConsoleUtilities.WriteWrappedLine("Recrowned Athenaeum Console Tools version " + Assembly.GetExecutingAssembly().GetName().Version.ToString());
ConsoleUtilities.WriteWrappedLine("Type \"help\" for help.");
ce.commands.Add(new HelpCommand(ce));
ce.commands.Add(new TexturePackerCommand());
ce.commands.Add(new StopCommand(ce));
ce.commands.Add(new ClearConsoleCommand());
ce.commands.Add(new NinePatchCommand());
if (args.Length > 0)
{
ConsoleUtilities.WriteWrappedLine("Executing as one time use.");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < args.Length; i++) sb.Append(args[i] + ' ');
string commandAndArgs = sb.ToString().TrimEnd();
try
{
ConsoleUtilities.WriteWrappedLine("Command and argument received: " + commandAndArgs);
ce.Process(commandAndArgs);
}
catch (ArgumentException e)
{
ConsoleUtilities.WriteWrappedLine("An error has occurred: " + e.Message);
}
}
else
{
ce.Run();
}
}
}
}