123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Drawing.Imaging;
- using System.IO;
- using System.Linq;
- using System.Runtime.InteropServices;
- using ArcToolkitCLI.Commands.Options;
- using ArcToolkitCLI.Util;
- using CommandLine;
- using Squish;
- namespace ArcToolkitCLI.Commands.Converters
- {
- [Verb("tex", HelpText = "Convert TEX files into PNG and back")]
- internal class TexConverter : IConverterCommand, IInputOptions, IOutputOptions
- {
- private const string TEX_TAG = "CM3D2_TEX";
- private const int TEX_VERSION = 1010;
- private readonly byte[] PNG_HEADER = {137, 80, 78, 71, 13, 10, 26, 10};
- private readonly Dictionary<TextureFormat, Action<string, byte[], int, int, TextureFormat>> textureLoaders;
- public TexConverter()
- {
- textureLoaders = new Dictionary<TextureFormat, Action<string, byte[], int, int, TextureFormat>>
- {
- [TextureFormat.ARGB32] = ConvertFromPng,
- [TextureFormat.RGB24] = ConvertFromPng,
- [TextureFormat.DXT1] = ConvertFromDxt,
- [TextureFormat.DXT5] = ConvertFromDxt
- };
- }
- public int Run()
- {
- var files = Glob.EnumerateFiles(Input);
- Directory.CreateDirectory(Output);
- if (!files.Any())
- {
- Console.WriteLine("No files specified. Run `convert help tex` for help.");
- return 0;
- }
- foreach (var file in files)
- {
- var result = 0;
- switch (file.Extension.ToLowerInvariant())
- {
- case ".png":
- if ((result = PngToTex(file.FullName)) != 0)
- return result;
- break;
- case ".tex":
- if ((result = TexToPng(file.FullName)) != 0)
- return result;
- break;
- default:
- Console.WriteLine($"File {file.FullName} is neither .png nor .tex file. Skipping...");
- break;
- }
- }
- return 0;
- }
- [Value(0, HelpText = "Input PNG or TEX files")]
- public IEnumerable<string> Input { get; set; }
- public string Output { get; set; }
- private bool ByteEquals(byte[] b1, byte[] b2)
- {
- var len = Math.Min(b1.Length, b2.Length);
- for (var i = 0; i < len; i++)
- if (b1[i] != b2[i])
- return false;
- return true;
- }
- private int PngToTex(string file)
- {
- var img = File.ReadAllBytes(file);
- if (!ByteEquals(img, PNG_HEADER))
- {
- Console.WriteLine($"File {file} is not a PNG file!");
- return 1;
- }
- var w = BitConverter.ToInt32(img, PNG_HEADER.Length + 4 + 4);
- var h = BitConverter.ToInt32(img, PNG_HEADER.Length + 4 + 4 + 4);
- using (var bw =
- new BinaryWriter(File.Create(Path.Combine(Output, $"{Path.GetFileNameWithoutExtension(file)}.tex"))))
- {
- bw.Write(TEX_TAG);
- bw.Write(TEX_VERSION);
- bw.Write(string.Empty);
- bw.Write(w);
- bw.Write(h);
- bw.Write((int) TextureFormat.ARGB32);
- bw.Write(img.Length);
- bw.Write(img);
- }
- return 0;
- }
- private int TexToPng(string file)
- {
- using (var br = new BinaryReader(File.OpenRead(file)))
- {
- var tag = br.ReadString();
- if (tag != TEX_TAG)
- {
- Console.WriteLine($"File {file} is not a valid TEX file!");
- return 1;
- }
- var version = br.ReadInt32();
- br.ReadString();
- var width = 0;
- var height = 0;
- var format = TextureFormat.ARGB32;
- if (version >= 1010)
- {
- width = br.ReadInt32();
- height = br.ReadInt32();
- format = (TextureFormat) br.ReadInt32();
- }
- if (!Enum.IsDefined(typeof(TextureFormat), format))
- {
- Console.WriteLine($"File {file} has unsupported texture format: {format}");
- return 1;
- }
- var size = br.ReadInt32();
- var data = new byte[size];
- br.Read(data, 0, size);
- if (version == 1000)
- {
- width = BitConverter.ToInt32(data, PNG_HEADER.Length + 4 + 4);
- height = BitConverter.ToInt32(data, PNG_HEADER.Length + 4 + 4 + 4);
- }
- if (textureLoaders.TryGetValue(format, out var saveTex))
- {
- saveTex(file, data, width, height, format);
- }
- else
- {
- Console.WriteLine($"File {file} uses format {format} that is not supported!");
- return 1;
- }
- }
- return 0;
- }
- private void ConvertFromPng(string file, byte[] data, int width, int height, TextureFormat format)
- {
- var ms = new MemoryStream(data);
- var img = Image.FromStream(ms);
- img.Save(Path.Combine(Output, $"{Path.GetFileNameWithoutExtension(file)}.png"));
- img.Dispose();
- ms.Dispose();
- }
- private void ConvertFromDxt(string file, byte[] data, int width, int height, TextureFormat format)
- {
- var squishFlags = (SquishFlags) 0;
- switch (format)
- {
- case TextureFormat.DXT1:
- squishFlags |= SquishFlags.kDxt1;
- break;
- case TextureFormat.DXT5:
- squishFlags |= SquishFlags.kDxt5;
- break;
- }
- var outData = new byte[width * height * 4];
- Squish.Squish.DecompressImage(outData, width, height, ref data, squishFlags);
- for (var i = 0; i < width * height; i++)
- {
- var r = outData[i * 4];
- outData[i * 4] = outData[i * 4 + 2];
- outData[i * 4 + 2] = r;
- }
- var gch = GCHandle.Alloc(outData, GCHandleType.Pinned);
- var img = new Bitmap(width, height, width * 4, PixelFormat.Format32bppArgb, gch.AddrOfPinnedObject());
- img.RotateFlip(RotateFlipType.RotateNoneFlipY);
- img.Save(Path.Combine(Output, $"{Path.GetFileNameWithoutExtension(file)}.png"));
- img.Dispose();
- gch.Free();
- }
- private enum TextureFormat
- {
- ARGB32 = 5,
- RGB24 = 3,
- DXT1 = 10,
- DXT5 = 12
- }
- }
- }
|