/* This file is part of libWiiSharp * Copyright (C) 2009 Leathl * * libWiiSharp is free software: you can redistribute it and/or * modify it 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. * * libWiiSharp is distributed in the hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ //TPL conversion based on Wii.py by Xuzz, SquidMan, megazig, Matt_P, Omega and The Lemon Man. //Zetsubou by SquidMan was also a reference. //Thanks to the authors! using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Net; namespace libWiiSharp { #region Enums public enum TPL_TextureFormat : int { /// /// Intensity, 4bpp /// I4 = 0, /// /// Intensity, 8bpp /// I8 = 1, /// /// Intensity, Alpha, 8bpp /// IA4 = 2, /// /// Intensity, Alpha, 16bpp /// IA8 = 3, /// /// RGB, 16bpp /// RGB565 = 4, /// /// RGB, Alpha, 16bpp /// RGB5A3 = 5, /// /// RGB, Alpha, 32bpp /// RGBA8 = 6, /// /// Indexed, 4bpp /// CI4 = 8, /// /// Indexed, 8bpp /// CI8 = 9, /// /// Indexed, 14bpp /// CI14X2 = 10, /// /// S3TC Compressed /// CMP = 14, } public enum TPL_PaletteFormat : int { /// /// No Palette /// None = 255, /// /// Intensity, Alpha, 16bpp /// IA8 = 0, /// /// RGB, 16bpp /// RGB565 = 1, /// /// RGB, Alpha, 16bpp /// RGB5A3 = 2, } #endregion public class TPL : IDisposable { private TPL_Header tplHeader = new TPL_Header(); private List tplTextureEntries = new List(); private List tplTextureHeaders = new List(); private List tplPaletteHeaders = new List(); private List textureData = new List(); private List paletteData = new List(); /// /// The Number of Textures in the TPL. /// public int NumOfTextures { get { return (int)tplHeader.NumOfTextures; } } #region IDisposable Members private bool isDisposed = false; ~TPL() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing && !isDisposed) { tplHeader = null; tplTextureEntries.Clear(); tplTextureEntries = null; tplTextureHeaders.Clear(); tplTextureHeaders = null; tplPaletteHeaders.Clear(); tplPaletteHeaders = null; textureData.Clear(); textureData = null; paletteData.Clear(); paletteData = null; } isDisposed = true; } #endregion #region Public Functions /// /// Loads a TPL file. /// /// /// public static TPL Load(string pathToTpl) { TPL tmpTpl = new TPL(); MemoryStream ms = new MemoryStream(File.ReadAllBytes(pathToTpl)); try { tmpTpl.parseTpl(ms); } catch { ms.Dispose(); throw; } ms.Dispose(); return tmpTpl; } /// /// Loads a TPL file. /// /// /// public static TPL Load(byte[] tplFile) { TPL tmpTpl = new TPL(); MemoryStream ms = new MemoryStream(tplFile); try { tmpTpl.parseTpl(ms); } catch { ms.Dispose(); throw; } ms.Dispose(); return tmpTpl; } /// /// Loads a TPL file. /// /// /// public static TPL Load(Stream tpl) { TPL t = new TPL(); t.parseTpl(tpl); return t; } public static Bitmap ToImage(byte[] imageData, int width, int height, TPL_TextureFormat format, byte[] paletteData, int count, TPL_PaletteFormat paletteFormat) { TPL t = new TPL(); uint[] paletteRgba = new uint[0]; byte[] rgba; if (paletteData != null) { var tmpHeader = new TPL_PaletteHeader { NumberOfItems = (ushort)count, PaletteFormat = (uint)paletteFormat }; paletteRgba = t.paletteToRgba(tmpHeader, paletteData); } switch (format) { case TPL_TextureFormat.I4: rgba = t.fromI4(imageData, width, height); break; case TPL_TextureFormat.I8: rgba = t.fromI8(imageData, width, height); break; case TPL_TextureFormat.IA4: rgba = t.fromIA4(imageData, width, height); break; case TPL_TextureFormat.IA8: rgba = t.fromIA8(imageData, width, height); break; case TPL_TextureFormat.RGB565: rgba = t.fromRGB565(imageData, width, height); break; case TPL_TextureFormat.RGB5A3: rgba = t.fromRGB5A3(imageData, width, height); break; case TPL_TextureFormat.RGBA8: rgba = t.fromRGBA8(imageData, width, height); break; case TPL_TextureFormat.CI4: rgba = new byte[0]; rgba = t.fromCI4(imageData, paletteRgba, width, height); break; case TPL_TextureFormat.CI8: rgba = new byte[0]; rgba = t.fromCI8(imageData, paletteRgba, width, height); break; case TPL_TextureFormat.CI14X2: rgba = new byte[0]; rgba = t.fromCI14X2(imageData, paletteRgba, width, height); break; case TPL_TextureFormat.CMP: rgba = t.fromCMP(imageData, width, height); break; default: rgba = new byte[0]; break; } return t.rgbaToImage(rgba, width, height); } /// /// Creates a TPL from an image. /// Palette formats are only required for color indexed TPL formats (CI4, CI8 and CI14X2)! /// /// /// /// public static TPL FromImage(string pathToImage, TPL_TextureFormat tplFormat, TPL_PaletteFormat paletteFormat = TPL_PaletteFormat.RGB5A3) { return FromImages(new string[] { pathToImage }, new TPL_TextureFormat[] { tplFormat }, new TPL_PaletteFormat[] { paletteFormat }); } /// /// Creates a TPL from an image. /// Palette formats are only required for color indexed TPL formats (CI4, CI8 and CI14X2)! /// /// /// /// public static TPL FromImage(Image img, TPL_TextureFormat tplFormat, TPL_PaletteFormat paletteFormat = TPL_PaletteFormat.RGB5A3) { return FromImages(new Image[] { img }, new TPL_TextureFormat[] { tplFormat }, new TPL_PaletteFormat[] { paletteFormat }); } /// /// Creates a TPL from multiple images. /// Palette formats are only required for color indexed TPL formats (CI4, CI8 and CI14X2)! /// If you use a color indexed format, please provide at least one palette format. /// If you use multiple color indexed TPLs, the palette formats must have the same index as the image and tpl format! /// /// /// /// public static TPL FromImages(string[] imagePaths, TPL_TextureFormat[] tplFormats, TPL_PaletteFormat[] paletteFormats) { if (tplFormats.Length < imagePaths.Length) throw new Exception("You must specify a format for each image!"); List images = new List(); foreach (string imagePath in imagePaths) images.Add(Image.FromFile(imagePath)); TPL tmpTpl = new TPL(); tmpTpl.createFromImages(images.ToArray(), tplFormats, paletteFormats); return tmpTpl; } /// /// Creates a TPL from multiple images. /// Palette formats are only required for color indexed TPL formats (CI4, CI8 and CI14X2)! /// If you use a color indexed format, please provide at least one palette format. /// If you use multiple color indexed TPLs, the palette formats must have the same index as the image and tpl format! /// /// /// /// public static TPL FromImages(Image[] images, TPL_TextureFormat[] tplFormats, TPL_PaletteFormat[] paletteFormats) { if (tplFormats.Length < images.Length) throw new Exception("You must specify a format for each image!"); TPL tmpTpl = new TPL(); tmpTpl.createFromImages(images, tplFormats, paletteFormats); return tmpTpl; } /// /// Loads a TPL file. /// /// public void LoadFile(string pathToTpl) { MemoryStream ms = new MemoryStream(File.ReadAllBytes(pathToTpl)); try { parseTpl(ms); } catch { ms.Dispose(); throw; } ms.Dispose(); } /// /// Loads a TPL file. /// /// public void LoadFile(byte[] tplFile) { MemoryStream ms = new MemoryStream(tplFile); try { parseTpl(ms); } catch { ms.Dispose(); throw; } ms.Dispose(); } /// /// Loads a TPL file. /// /// public void LoadFile(Stream tpl) { parseTpl(tpl); } /// /// Creates a TPL from an image. /// Palette formats are only required for color indexed TPL formats (CI4, CI8 and CI14X2)! /// /// /// public void CreateFromImage(string pathToImage, TPL_TextureFormat tplFormat, TPL_PaletteFormat paletteFormat = TPL_PaletteFormat.RGB5A3) { CreateFromImages(new string[] { pathToImage }, new TPL_TextureFormat[] { tplFormat }, new TPL_PaletteFormat[] { paletteFormat }); } /// /// Creates a TPL from an image. /// Palette formats are only required for color indexed TPL formats (CI4, CI8 and CI14X2)! /// /// /// public void CreateFromImage(Image img, TPL_TextureFormat tplFormat, TPL_PaletteFormat paletteFormat = TPL_PaletteFormat.RGB5A3) { CreateFromImages(new Image[] { img }, new TPL_TextureFormat[] { tplFormat }, new TPL_PaletteFormat[] { paletteFormat }); } /// /// Creates a TPL from multiple images. /// Palette formats are only required for color indexed TPL formats (CI4, CI8 and CI14X2)! /// If you use a color indexed format, please provide at least one palette format. /// If you use multiple color indexed TPLs, the palette formats must have the same index as the image and tpl format! /// /// /// public void CreateFromImages(string[] imagePaths, TPL_TextureFormat[] tplFormats, TPL_PaletteFormat[] paletteFormats) { if (tplFormats.Length < imagePaths.Length) throw new Exception("You must specify a format for each image!"); List images = new List(); foreach (string imagePath in imagePaths) images.Add(Image.FromFile(imagePath)); createFromImages(images.ToArray(), tplFormats, paletteFormats); } /// /// Creates a TPL from multiple images. /// Palette formats are only required for color indexed TPL formats (CI4, CI8 and CI14X2)! /// If you use a color indexed format, please provide at least one palette format. /// If you use multiple color indexed TPLs, the palette formats must have the same index as the image and tpl format! /// /// /// public void CreateFromImages(Image[] images, TPL_TextureFormat[] tplFormats, TPL_PaletteFormat[] paletteFormats) { if (tplFormats.Length < images.Length) throw new Exception("You must specify a format for each image!"); createFromImages(images, tplFormats, paletteFormats); } /// /// Saves the TPL. /// /// public void Save(string savePath) { if (File.Exists(savePath)) File.Delete(savePath); FileStream fs = new FileStream(savePath, FileMode.Create); try { writeToStream(fs); } catch { fs.Dispose(); throw; } fs.Dispose(); } /// /// Returns the TPL as a memory stream. /// /// public MemoryStream ToMemoryStream() { MemoryStream ms = new MemoryStream(); try { writeToStream(ms); } catch { ms.Dispose(); throw; } return ms; } /// /// Returns the TPL as a byte array. /// /// public byte[] ToByteArray() { return ToMemoryStream().ToArray(); } /// /// Extracts the first Texture of the TPL. /// /// public Image ExtractTexture() { return ExtractTexture(0); } /// /// Extracts the Texture with the given index. /// /// /// public Image ExtractTexture(int index) { byte[] rgbaData; switch ((TPL_TextureFormat)tplTextureHeaders[index].TextureFormat) { case TPL_TextureFormat.I4: rgbaData = fromI4(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.I8: rgbaData = fromI8(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.IA4: rgbaData = fromIA4(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.IA8: rgbaData = fromIA8(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.RGB565: rgbaData = fromRGB565(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.RGB5A3: rgbaData = fromRGB5A3(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.RGBA8: rgbaData = fromRGBA8(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.CI4: rgbaData = fromCI4(textureData[index], paletteToRgba(index), tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.CI8: rgbaData = fromCI8(textureData[index], paletteToRgba(index), tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.CI14X2: rgbaData = fromCI14X2(textureData[index], paletteToRgba(index), tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.CMP: rgbaData = fromCMP(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; default: throw new FormatException("Unsupported Texture Format!"); } return rgbaToImage(rgbaData, tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); } public ushort GetImageWidth(int index) { return tplTextureHeaders[index].TextureWidth; } public ushort GetImageHeight(int index) { return tplTextureHeaders[index].TextureHeight; } public byte[] ExtractTextureByteArray(int index) { byte[] rgbaData; switch ((TPL_TextureFormat)tplTextureHeaders[index].TextureFormat) { case TPL_TextureFormat.I4: rgbaData = fromI4(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.I8: rgbaData = fromI8(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.IA4: rgbaData = fromIA4(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.IA8: rgbaData = fromIA8(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.RGB565: rgbaData = fromRGB565(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.RGB5A3: rgbaData = fromRGB5A3(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.RGBA8: rgbaData = fromRGBA8(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.CI4: rgbaData = fromCI4(textureData[index], paletteToRgba(index), tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.CI8: rgbaData = fromCI8(textureData[index], paletteToRgba(index), tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.CI14X2: rgbaData = fromCI14X2(textureData[index], paletteToRgba(index), tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; case TPL_TextureFormat.CMP: rgbaData = fromCMP(textureData[index], tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); break; default: throw new FormatException("Unsupported Texture Format!"); } return rgbaData; } /// /// Extracts the first Texture of the TPL. /// /// public void ExtractTexture(string savePath) { ExtractTexture(0, savePath); } /// /// Extracts the Texture with the given index. /// /// /// public void ExtractTexture(int index, string savePath) { if (File.Exists(savePath)) File.Delete(savePath); Image img = ExtractTexture(index); switch (Path.GetExtension(savePath).ToLower()) { case ".tif": case ".tiff": img.Save(savePath, System.Drawing.Imaging.ImageFormat.Tiff); break; case ".bmp": img.Save(savePath, System.Drawing.Imaging.ImageFormat.Bmp); break; case ".gif": img.Save(savePath, System.Drawing.Imaging.ImageFormat.Gif); break; case ".jpg": case ".jpeg": img.Save(savePath, System.Drawing.Imaging.ImageFormat.Jpeg); break; default: img.Save(savePath, System.Drawing.Imaging.ImageFormat.Png); break; } } /// /// Extracts all Textures of the TPL. /// /// public Image[] ExtractAllTextures() { List imgList = new List(); for (int i = 0; i < tplHeader.NumOfTextures; i++) imgList.Add(ExtractTexture(i)); return imgList.ToArray(); } /// /// Extracts all Textures of the TPL. /// /// public void ExtractAllTextures(string saveDir) { if (Directory.Exists(saveDir)) Directory.CreateDirectory(saveDir); for (int i = 0; i < tplHeader.NumOfTextures; i++) ExtractTexture(i, saveDir + Path.DirectorySeparatorChar + "Texture_" + i.ToString("x2") + ".png"); } /// /// Adds a Texture to the TPL. /// /// /// public void AddTexture(string imagePath, TPL_TextureFormat tplFormat, TPL_PaletteFormat paletteFormat = TPL_PaletteFormat.RGB5A3) { Image img = Image.FromFile(imagePath); AddTexture(img, tplFormat, paletteFormat); } /// /// Adds a Texture to the TPL. /// /// /// public void AddTexture(Image img, TPL_TextureFormat tplFormat, TPL_PaletteFormat paletteFormat = TPL_PaletteFormat.RGB5A3) { TPL_TextureEntry tempTexture = new TPL_TextureEntry(); TPL_TextureHeader tempTextureHeader = new TPL_TextureHeader(); TPL_PaletteHeader tempPaletteHeader = new TPL_PaletteHeader(); byte[] tempTextureData = imageToTpl(img, tplFormat); byte[] tempPaletteData = new byte[0]; tempTextureHeader.TextureHeight = (ushort)img.Height; tempTextureHeader.TextureWidth = (ushort)img.Width; tempTextureHeader.TextureFormat = (uint)tplFormat; if (tplFormat == TPL_TextureFormat.CI4 || tplFormat == TPL_TextureFormat.CI8 || tplFormat == TPL_TextureFormat.CI14X2) { ColorIndexConverter cic = new ColorIndexConverter(imageToRgba(img), img.Width, img.Height, tplFormat, paletteFormat); tempTextureData = cic.Data; tempPaletteData = cic.Palette; tempPaletteHeader.NumberOfItems = (ushort)(tempPaletteData.Length / 2); tempPaletteHeader.PaletteFormat = (uint)paletteFormat; } tplTextureEntries.Add(tempTexture); tplTextureHeaders.Add(tempTextureHeader); tplPaletteHeaders.Add(tempPaletteHeader); textureData.Add(tempTextureData); paletteData.Add(tempPaletteData); tplHeader.NumOfTextures++; } /// /// Removes a Texture from the TPL. /// /// public void RemoveTexture(int index) { if (tplHeader.NumOfTextures > index) { tplTextureEntries.RemoveAt(index); tplTextureHeaders.RemoveAt(index); tplPaletteHeaders.RemoveAt(index); textureData.RemoveAt(index); paletteData.RemoveAt(index); tplHeader.NumOfTextures--; } } /// /// Gets the format of the Texture with the given index. /// /// /// public TPL_TextureFormat GetTextureFormat(int index) { return (TPL_TextureFormat)tplTextureHeaders[index].TextureFormat; } /// /// Gets the palette format of the Texture with the given index. /// /// /// public TPL_PaletteFormat GetPaletteFormat(int index) { return (TPL_PaletteFormat)tplPaletteHeaders[index].PaletteFormat; } /// /// Gets the size of the Texture with the given index. /// /// /// public Size GetTextureSize(int index) { return new Size(tplTextureHeaders[index].TextureWidth, tplTextureHeaders[index].TextureHeight); } #endregion #region Private Functions private void writeToStream(Stream writeStream) { fireDebug("Writing TPL..."); writeStream.Seek(0, SeekOrigin.Begin); fireDebug(" Writing TPL Header... (Offset: 0x{0})", writeStream.Position); tplHeader.Write(writeStream); int textureEntriesPosition = (int)writeStream.Position; writeStream.Seek(tplHeader.NumOfTextures * 8, SeekOrigin.Current); //Get Number of Palettes int paletteCount = 0; for (int i = 0; i < tplHeader.NumOfTextures; i++) if ((TPL_TextureFormat)tplTextureHeaders[i].TextureFormat == TPL_TextureFormat.CI4 || (TPL_TextureFormat)tplTextureHeaders[i].TextureFormat == TPL_TextureFormat.CI8 || (TPL_TextureFormat)tplTextureHeaders[i].TextureFormat == TPL_TextureFormat.CI14X2) paletteCount++; int paletteHeaderPosition = (int)writeStream.Position; writeStream.Seek(paletteCount * 12, SeekOrigin.Current); //Write Palette Data for (int i = 0; i < tplHeader.NumOfTextures; i++) { if ((TPL_TextureFormat)tplTextureHeaders[i].TextureFormat == TPL_TextureFormat.CI4 || (TPL_TextureFormat)tplTextureHeaders[i].TextureFormat == TPL_TextureFormat.CI8 || (TPL_TextureFormat)tplTextureHeaders[i].TextureFormat == TPL_TextureFormat.CI14X2) { fireDebug(" Writing Palette of Texture #{1}... (Offset: 0x{0})", writeStream.Position, i + 1); writeStream.Seek(Shared.AddPadding(writeStream.Position, 32), SeekOrigin.Begin); tplPaletteHeaders[i].PaletteDataOffset = (uint)writeStream.Position; writeStream.Write(paletteData[i], 0, paletteData[i].Length); } } int textureHeaderPosition = (int)writeStream.Position; writeStream.Seek(tplHeader.NumOfTextures * 36, SeekOrigin.Current); //Write textureData for (int i = 0; i < tplHeader.NumOfTextures; i++) { fireDebug(" Writing Texture #{1} of {2}... (Offset: 0x{0})", writeStream.Position, i + 1, tplHeader.NumOfTextures); writeStream.Seek(Shared.AddPadding((int)writeStream.Position, 32), SeekOrigin.Begin); tplTextureHeaders[i].TextureDataOffset = (uint)writeStream.Position; writeStream.Write(textureData[i], 0, textureData[i].Length); } while (writeStream.Position % 32 != 0) writeStream.WriteByte(0x00); writeStream.Seek(paletteHeaderPosition, SeekOrigin.Begin); //Write Palette Headers for (int i = 0; i < tplHeader.NumOfTextures; i++) { if ((TPL_TextureFormat)tplTextureHeaders[i].TextureFormat == TPL_TextureFormat.CI4 || (TPL_TextureFormat)tplTextureHeaders[i].TextureFormat == TPL_TextureFormat.CI8 || (TPL_TextureFormat)tplTextureHeaders[i].TextureFormat == TPL_TextureFormat.CI14X2) { fireDebug(" Writing Palette Header of Texture #{1}... (Offset: 0x{0})", writeStream.Position, i + 1); tplTextureEntries[i].PaletteHeaderOffset = (uint)writeStream.Position; tplPaletteHeaders[i].Write(writeStream); } } writeStream.Seek(textureHeaderPosition, SeekOrigin.Begin); //Write Texture Headers for (int i = 0; i < tplHeader.NumOfTextures; i++) { fireDebug(" Writing Texture Header #{1} of {2}... (Offset: 0x{0})", writeStream.Position, i + 1, tplHeader.NumOfTextures); tplTextureEntries[i].TextureHeaderOffset = (uint)writeStream.Position; tplTextureHeaders[i].Write(writeStream); } writeStream.Seek(textureEntriesPosition, SeekOrigin.Begin); //Write Texture Entries for (int i = 0; i < tplHeader.NumOfTextures; i++) { fireDebug(" Writing Texture Entry #{1} of {2}... (Offset: 0x{0})", writeStream.Position, i + 1, tplHeader.NumOfTextures); tplTextureEntries[i].Write(writeStream); } fireDebug("Writing TPL Finished..."); } private void parseTpl(Stream tplFile) { fireDebug("Parsing TPL..."); tplHeader = new TPL_Header(); tplTextureEntries = new List(); tplTextureHeaders = new List(); tplPaletteHeaders = new List(); textureData = new List(); paletteData = new List(); tplFile.Seek(0, SeekOrigin.Begin); byte[] temp = new byte[4]; fireDebug(" Reading TPL Header: Magic... (Offset: 0x{0})", tplFile.Position); tplFile.Read(temp, 0, 4); if (Shared.Swap(BitConverter.ToUInt32(temp, 0)) != tplHeader.TplMagic) { fireDebug(" -> Invalid Magic: 0x{0}", Shared.Swap(BitConverter.ToUInt32(temp, 0))); throw new Exception("TPL Header: Invalid Magic!"); } fireDebug(" Reading TPL Header: NumOfTextures... (Offset: 0x{0})", tplFile.Position); tplFile.Read(temp, 0, 4); tplHeader.NumOfTextures = Shared.Swap(BitConverter.ToUInt32(temp, 0)); fireDebug(" Reading TPL Header: Headersize... (Offset: 0x{0})", tplFile.Position); tplFile.Read(temp, 0, 4); if (Shared.Swap(BitConverter.ToUInt32(temp, 0)) != tplHeader.HeaderSize) { fireDebug(" -> Invalid Headersize: 0x{0}", Shared.Swap(BitConverter.ToUInt32(temp, 0))); throw new Exception("TPL Header: Invalid Headersize!"); } for (int i = 0; i < tplHeader.NumOfTextures; i++) { fireDebug(" Reading Texture Entry #{1} of {2}... (Offset: 0x{0})", tplFile.Position, i + 1, tplHeader.NumOfTextures); TPL_TextureEntry tempTexture = new TPL_TextureEntry(); tplFile.Read(temp, 0, 4); tempTexture.TextureHeaderOffset = Shared.Swap(BitConverter.ToUInt32(temp, 0)); tplFile.Read(temp, 0, 4); tempTexture.PaletteHeaderOffset = Shared.Swap(BitConverter.ToUInt32(temp, 0)); tplTextureEntries.Add(tempTexture); } for (int i = 0; i < tplHeader.NumOfTextures; i++) { fireDebug(" Reading Texture Header #{1} of {2}... (Offset: 0x{0})", tplFile.Position, i + 1, tplHeader.NumOfTextures); TPL_TextureHeader tempTextureHeader = new TPL_TextureHeader(); TPL_PaletteHeader tempPaletteHeader = new TPL_PaletteHeader(); tplFile.Seek(tplTextureEntries[i].TextureHeaderOffset, SeekOrigin.Begin); tplFile.Read(temp, 0, 4); tempTextureHeader.TextureHeight = Shared.Swap(BitConverter.ToUInt16(temp, 0)); tempTextureHeader.TextureWidth = Shared.Swap(BitConverter.ToUInt16(temp, 2)); tplFile.Read(temp, 0, 4); tempTextureHeader.TextureFormat = Shared.Swap(BitConverter.ToUInt32(temp, 0)); tplFile.Read(temp, 0, 4); tempTextureHeader.TextureDataOffset = Shared.Swap(BitConverter.ToUInt32(temp, 0)); tplFile.Read(temp, 0, 4); tempTextureHeader.WrapS = Shared.Swap(BitConverter.ToUInt32(temp, 0)); tplFile.Read(temp, 0, 4); tempTextureHeader.WrapT = Shared.Swap(BitConverter.ToUInt32(temp, 0)); tplFile.Read(temp, 0, 4); tempTextureHeader.MinFilter = Shared.Swap(BitConverter.ToUInt32(temp, 0)); tplFile.Read(temp, 0, 4); tempTextureHeader.MagFilter = Shared.Swap(BitConverter.ToUInt32(temp, 0)); tplFile.Read(temp, 0, 4); tempTextureHeader.LodBias = Shared.Swap(BitConverter.ToUInt32(temp, 0)); tplFile.Read(temp, 0, 4); tempTextureHeader.EdgeLod = temp[0]; tempTextureHeader.MinLod = temp[1]; tempTextureHeader.MaxLod = temp[2]; tempTextureHeader.Unpacked = temp[3]; if (tplTextureEntries[i].PaletteHeaderOffset != 0) { fireDebug(" Reading Palette Header #{1} of {2}... (Offset: 0x{0})", tplFile.Position, i + 1, tplHeader.NumOfTextures); tplFile.Seek(tplTextureEntries[i].PaletteHeaderOffset, SeekOrigin.Begin); tplFile.Read(temp, 0, 4); tempPaletteHeader.NumberOfItems = Shared.Swap(BitConverter.ToUInt16(temp, 0)); tempPaletteHeader.Unpacked = temp[2]; tempPaletteHeader.Pad = temp[3]; tplFile.Read(temp, 0, 4); tempPaletteHeader.PaletteFormat = Shared.Swap(BitConverter.ToUInt32(temp, 0)); tplFile.Read(temp, 0, 4); tempPaletteHeader.PaletteDataOffset = Shared.Swap(BitConverter.ToUInt32(temp, 0)); } tplFile.Seek(tempTextureHeader.TextureDataOffset, SeekOrigin.Begin); byte[] tempTextureData = new byte[textureByteSize((TPL_TextureFormat)tempTextureHeader.TextureFormat, tempTextureHeader.TextureWidth, tempTextureHeader.TextureHeight)]; byte[] tempPaletteData = new byte[tempPaletteHeader.NumberOfItems * 2]; fireDebug(" Reading Texture #{1} of {2}... (Offset: 0x{0})", tplFile.Position, i + 1, tplHeader.NumOfTextures); tplFile.Read(tempTextureData, 0, tempTextureData.Length); if (tplTextureEntries[i].PaletteHeaderOffset > 0) { fireDebug(" Reading Palette #{1} of {2}... (Offset: 0x{0})", tplFile.Position, i + 1, tplHeader.NumOfTextures); tplFile.Seek(tempPaletteHeader.PaletteDataOffset, SeekOrigin.Begin); tplFile.Read(tempPaletteData, 0, tempPaletteData.Length); } else tempPaletteData = new byte[0]; tplTextureHeaders.Add(tempTextureHeader); tplPaletteHeaders.Add(tempPaletteHeader); textureData.Add(tempTextureData); paletteData.Add(tempPaletteData); } } private int textureByteSize(TPL_TextureFormat tplFormat, int width, int height) { switch (tplFormat) { case TPL_TextureFormat.I4: return Shared.AddPadding(width, 8) * Shared.AddPadding(height, 8) / 2; case TPL_TextureFormat.I8: case TPL_TextureFormat.IA4: return Shared.AddPadding(width, 8) * Shared.AddPadding(height, 4); case TPL_TextureFormat.IA8: case TPL_TextureFormat.RGB565: case TPL_TextureFormat.RGB5A3: return Shared.AddPadding(width, 4) * Shared.AddPadding(height, 4) * 2; case TPL_TextureFormat.RGBA8: return Shared.AddPadding(width, 4) * Shared.AddPadding(height, 4) * 4; case TPL_TextureFormat.CI4: return Shared.AddPadding(width, 8) * Shared.AddPadding(height, 8) / 2; case TPL_TextureFormat.CI8: return Shared.AddPadding(width, 8) * Shared.AddPadding(height, 4); case TPL_TextureFormat.CI14X2: return Shared.AddPadding(width, 4) * Shared.AddPadding(height, 4) * 2; case TPL_TextureFormat.CMP: return width * height; default: throw new FormatException("Unsupported Texture Format!"); } } private void createFromImages(Image[] images, TPL_TextureFormat[] tplFormats, TPL_PaletteFormat[] paletteFormats) { tplHeader = new TPL_Header(); tplTextureEntries = new List(); tplTextureHeaders = new List(); tplPaletteHeaders = new List(); textureData = new List(); paletteData = new List(); tplHeader.NumOfTextures = (uint)images.Length; for (int i = 0; i < images.Length; i++) { Image img = images[i]; TPL_TextureEntry tempTexture = new TPL_TextureEntry(); TPL_TextureHeader tempTextureHeader = new TPL_TextureHeader(); TPL_PaletteHeader tempPaletteHeader = new TPL_PaletteHeader(); byte[] tempTextureData = imageToTpl(img, tplFormats[i]); byte[] tempPaletteData = new byte[0]; tempTextureHeader.TextureHeight = (ushort)img.Height; tempTextureHeader.TextureWidth = (ushort)img.Width; tempTextureHeader.TextureFormat = (uint)tplFormats[i]; if (tplFormats[i] == TPL_TextureFormat.CI4 || tplFormats[i] == TPL_TextureFormat.CI8 || tplFormats[i] == TPL_TextureFormat.CI14X2) { ColorIndexConverter cic = new ColorIndexConverter(imageToRgba(img), img.Width, img.Height, tplFormats[i], paletteFormats[i]); tempTextureData = cic.Data; tempPaletteData = cic.Palette; tempPaletteHeader.NumberOfItems = (ushort)(tempPaletteData.Length / 2); tempPaletteHeader.PaletteFormat = (uint)paletteFormats[i]; } tplTextureEntries.Add(tempTexture); tplTextureHeaders.Add(tempTextureHeader); tplPaletteHeaders.Add(tempPaletteHeader); textureData.Add(tempTextureData); paletteData.Add(tempPaletteData); } } private byte[] imageToTpl(Image img, TPL_TextureFormat tplFormat) { switch (tplFormat) { case TPL_TextureFormat.I4: return toI4((Bitmap)img); case TPL_TextureFormat.I8: return toI8((Bitmap)img); case TPL_TextureFormat.IA4: return toIA4((Bitmap)img); case TPL_TextureFormat.IA8: return toIA8((Bitmap)img); case TPL_TextureFormat.RGB565: return toRGB565((Bitmap)img); case TPL_TextureFormat.RGB5A3: return toRGB5A3((Bitmap)img); case TPL_TextureFormat.RGBA8: return toRGBA8((Bitmap)img); case TPL_TextureFormat.CI4: case TPL_TextureFormat.CI8: case TPL_TextureFormat.CI14X2: return new byte[0]; case TPL_TextureFormat.CMP: default: throw new FormatException("Format not supported!\nCurrently, images can only be converted to the following formats:\nI4, I8, IA4, IA8, RGB565, RGB5A3, RGBA8, CI4, CI8 , CI14X2."); } } private uint[] imageToRgba(Image img) { Bitmap bmp = (Bitmap)img; System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); byte[] pixelData = new byte[bmpData.Height * (int)Math.Abs(bmpData.Stride)]; System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, pixelData, 0, pixelData.Length); bmp.UnlockBits(bmpData); return Shared.ByteArrayToUIntArray(pixelData); } private Bitmap rgbaToImage(byte[] data, int width, int height) { if (width == 0) width = 1; if (height == 0) height = 1; Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); try { System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits( new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, bmp.PixelFormat); System.Runtime.InteropServices.Marshal.Copy(data, 0, bmpData.Scan0, data.Length); bmp.UnlockBits(bmpData); } catch { bmp.Dispose(); throw; } return bmp; } private uint[] paletteToRgba(int index) { TPL_PaletteFormat paletteformat = (TPL_PaletteFormat)tplPaletteHeaders[index].PaletteFormat; int itemcount = tplPaletteHeaders[index].NumberOfItems; int r, g, b, a; uint[] output = new uint[itemcount]; for (int i = 0; i < itemcount; i++) { if (i >= itemcount) continue; ushort pixel = BitConverter.ToUInt16(new byte[] { paletteData[index][i * 2 + 1], paletteData[index][i * 2] }, 0); if (paletteformat == TPL_PaletteFormat.IA8) //IA8 { r = pixel & 0xff; b = r; g = r; a = pixel >> 8; } else if (paletteformat == TPL_PaletteFormat.RGB565) //RGB565 { b = (((pixel >> 11) & 0x1F) << 3) & 0xff; g = (((pixel >> 5) & 0x3F) << 2) & 0xff; r = (((pixel >> 0) & 0x1F) << 3) & 0xff; a = 255; } else //RGB5A3 { if ((pixel & (1 << 15)) != 0) //RGB555 { a = 255; b = (((pixel >> 10) & 0x1F) * 255) / 31; g = (((pixel >> 5) & 0x1F) * 255) / 31; r = (((pixel >> 0) & 0x1F) * 255) / 31; } else //RGB4A3 { a = (((pixel >> 12) & 0x07) * 255) / 7; b = (((pixel >> 8) & 0x0F) * 255) / 15; g = (((pixel >> 4) & 0x0F) * 255) / 15; r = (((pixel >> 0) & 0x0F) * 255) / 15; } } output[i] = (uint)((r << 0) | (g << 8) | (b << 16) | (a << 24)); } return output; } private uint[] paletteToRgba(TPL_PaletteHeader header, byte[] data) { TPL_PaletteFormat paletteformat = (TPL_PaletteFormat)header.PaletteFormat; int itemcount = header.NumberOfItems; int r, g, b, a; uint[] output = new uint[itemcount]; for (int i = 0; i < itemcount; i++) { if (i >= itemcount) continue; ushort pixel = BitConverter.ToUInt16(new byte[] { data[i * 2 + 1], data[i * 2] }, 0); if (paletteformat == TPL_PaletteFormat.IA8) //IA8 { r = pixel & 0xff; b = r; g = r; a = pixel >> 8; } else if (paletteformat == TPL_PaletteFormat.RGB565) //RGB565 { b = (((pixel >> 11) & 0x1F) << 3) & 0xff; g = (((pixel >> 5) & 0x3F) << 2) & 0xff; r = (((pixel >> 0) & 0x1F) << 3) & 0xff; a = 255; } else //RGB5A3 { if ((pixel & (1 << 15)) != 0) //RGB555 { a = 255; b = (((pixel >> 10) & 0x1F) * 255) / 31; g = (((pixel >> 5) & 0x1F) * 255) / 31; r = (((pixel >> 0) & 0x1F) * 255) / 31; } else //RGB4A3 { a = (((pixel >> 12) & 0x07) * 255) / 7; b = (((pixel >> 8) & 0x0F) * 255) / 15; g = (((pixel >> 4) & 0x0F) * 255) / 15; r = (((pixel >> 0) & 0x0F) * 255) / 15; } } output[i] = (uint)((r << 0) | (g << 8) | (b << 16) | (a << 24)); } return output; } private int avg(int w0, int w1, int c0, int c1) { int a0 = c0 >> 11; int a1 = c1 >> 11; int a = (w0 * a0 + w1 * a1) / (w0 + w1); int c = (a << 11) & 0xffff; a0 = (c0 >> 5) & 63; a1 = (c1 >> 5) & 63; a = (w0 * a0 + w1 * a1) / (w0 + w1); c = c | ((a << 5) & 0xffff); a0 = c0 & 31; a1 = c1 & 31; a = (w0 * a0 + w1 * a1) / (w0 + w1); c = c | a; return c; } #endregion #region Conversions #region RGBA8 private byte[] fromRGBA8(byte[] tpl, int width, int height) { uint[] output = new uint[width * height]; int inp = 0; for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { for (int k = 0; k < 2; k++) { for (int y1 = y; y1 < y + 4; y1++) { for (int x1 = x; x1 < x + 4; x1++) { ushort pixel = Shared.Swap(BitConverter.ToUInt16(tpl, inp++ * 2)); if ((x1 >= width) || (y1 >= height)) continue; if (k == 0) { int a = (pixel >> 8) & 0xff; int r = (pixel >> 0) & 0xff; output[x1 + (y1 * width)] |= (uint)((r << 16) | (a << 24)); } else { int g = (pixel >> 8) & 0xff; int b = (pixel >> 0) & 0xff; output[x1 + (y1 * width)] |= (uint)((g << 8) | (b << 0)); } } } } } } return Shared.UIntArrayToByteArray(output); } private byte[] toRGBA8(Bitmap img) { uint[] pixeldata = imageToRgba(img); int w = img.Width; int h = img.Height; int z = 0, iv = 0; byte[] output = new byte[Shared.AddPadding(w, 4) * Shared.AddPadding(h, 4) * 4]; uint[] lr = new uint[32], lg = new uint[32], lb = new uint[32], la = new uint[32]; for (int y1 = 0; y1 < h; y1 += 4) { for (int x1 = 0; x1 < w; x1 += 4) { for (int y = y1; y < (y1 + 4); y++) { for (int x = x1; x < (x1 + 4); x++) { uint rgba; if (y >= h || x >= w) rgba = 0; else rgba = pixeldata[x + (y * w)]; lr[z] = (uint)(rgba >> 16) & 0xff; lg[z] = (uint)(rgba >> 8) & 0xff; lb[z] = (uint)(rgba >> 0) & 0xff; la[z] = (uint)(rgba >> 24) & 0xff; z++; } } if (z == 16) { for (int i = 0; i < 16; i++) { output[iv++] = (byte)(la[i]); output[iv++] = (byte)(lr[i]); } for (int i = 0; i < 16; i++) { output[iv++] = (byte)(lg[i]); output[iv++] = (byte)(lb[i]); } z = 0; } } } return output; } #endregion #region RGB5A3 private byte[] fromRGB5A3(byte[] tpl, int width, int height) { uint[] output = new uint[width * height]; int inp = 0; int r, g, b; int a = 0; for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { for (int y1 = y; y1 < y + 4; y1++) { for (int x1 = x; x1 < x + 4; x1++) { ushort pixel = Shared.Swap(BitConverter.ToUInt16(tpl, inp++ * 2)); if (y1 >= height || x1 >= width) continue; if ((pixel & (1 << 15)) != 0) { b = (((pixel >> 10) & 0x1F) * 255) / 31; g = (((pixel >> 5) & 0x1F) * 255) / 31; r = (((pixel >> 0) & 0x1F) * 255) / 31; a = 255; } else { a = (((pixel >> 12) & 0x07) * 255) / 7; b = (((pixel >> 8) & 0x0F) * 255) / 15; g = (((pixel >> 4) & 0x0F) * 255) / 15; r = (((pixel >> 0) & 0x0F) * 255) / 15; } output[(y1 * width) + x1] = (uint)((r << 0) | (g << 8) | (b << 16) | (a << 24)); } } } } return Shared.UIntArrayToByteArray(output); } private byte[] toRGB5A3(Bitmap img) { uint[] pixeldata = imageToRgba(img); int w = img.Width; int h = img.Height; int z = -1; byte[] output = new byte[Shared.AddPadding(w, 4) * Shared.AddPadding(h, 4) * 2]; for (int y1 = 0; y1 < h; y1 += 4) { for (int x1 = 0; x1 < w; x1 += 4) { for (int y = y1; y < y1 + 4; y++) { for (int x = x1; x < x1 + 4; x++) { int newpixel; if (y >= h || x >= w) newpixel = 0; else { int rgba = (int)pixeldata[x + (y * w)]; newpixel = 0; int r = (rgba >> 16) & 0xff; int g = (rgba >> 8) & 0xff; int b = (rgba >> 0) & 0xff; int a = (rgba >> 24) & 0xff; if (a <= 0xda) //RGB4A3 { newpixel &= ~(1 << 15); r = ((r * 15) / 255) & 0xf; g = ((g * 15) / 255) & 0xf; b = ((b * 15) / 255) & 0xf; a = ((a * 7) / 255) & 0x7; newpixel |= (a << 12) | (r << 8) | (g << 4) | b; } else //RGB5 { newpixel |= (1 << 15); r = ((r * 31) / 255) & 0x1f; g = ((g * 31) / 255) & 0x1f; b = ((b * 31) / 255) & 0x1f; newpixel |= (r << 10) | (g << 5) | b; } } output[++z] = (byte)(newpixel >> 8); output[++z] = (byte)(newpixel & 0xff); } } } } return output; } #endregion #region RGB565 private byte[] fromRGB565(byte[] tpl, int width, int height) { uint[] output = new uint[width * height]; int inp = 0; for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { for (int y1 = y; y1 < y + 4; y1++) { for (int x1 = x; x1 < x + 4; x1++) { ushort pixel = Shared.Swap(BitConverter.ToUInt16(tpl, inp++ * 2)); if (y1 >= height || x1 >= width) continue; int b = (((pixel >> 11) & 0x1F) << 3) & 0xff; int g = (((pixel >> 5) & 0x3F) << 2) & 0xff; int r = (((pixel >> 0) & 0x1F) << 3) & 0xff; output[y1 * width + x1] = (uint)((r << 0) | (g << 8) | (b << 16) | (255 << 24)); } } } } return Shared.UIntArrayToByteArray(output); } private byte[] toRGB565(Bitmap img) { uint[] pixeldata = imageToRgba(img); int w = img.Width; int h = img.Height; int z = -1; byte[] output = new byte[Shared.AddPadding(w, 4) * Shared.AddPadding(h, 4) * 2]; for (int y1 = 0; y1 < h; y1 += 4) { for (int x1 = 0; x1 < w; x1 += 4) { for (int y = y1; y < y1 + 4; y++) { for (int x = x1; x < x1 + 4; x++) { ushort newpixel; if (y >= h || x >= w) newpixel = 0; else { uint rgba = pixeldata[x + (y * w)]; uint b = (rgba >> 16) & 0xff; uint g = (rgba >> 8) & 0xff; uint r = (rgba >> 0) & 0xff; newpixel = (ushort)(((b >> 3) << 11) | ((g >> 2) << 5) | ((r >> 3) << 0)); } output[++z] = (byte)(newpixel >> 8); output[++z] = (byte)(newpixel & 0xff); } } } } return output; } #endregion #region I4 private byte[] fromI4(byte[] tpl, int width, int height) { uint[] output = new uint[width * height]; int inp = 0; for (int y = 0; y < height; y += 8) { for (int x = 0; x < width; x += 8) { for (int y1 = y; y1 < y + 8; y1++) { for (int x1 = x; x1 < x + 8; x1 += 2) { int pixel = tpl[inp++]; if (y1 >= height || x1 >= width) continue; int i = (pixel >> 4) * 255 / 15; output[y1 * width + x1] = (uint)((i << 0) | (i << 8) | (i << 16) | (255 << 24)); i = (pixel & 0x0F) * 255 / 15; if (y1 * width + x1 + 1 < output.Length) output[y1 * width + x1 + 1] = (uint)((i << 0) | (i << 8) | (i << 16) | (255 << 24)); } } } } return Shared.UIntArrayToByteArray(output); } private byte[] toI4(Bitmap img) { uint[] pixeldata = imageToRgba(img); int w = img.Width; int h = img.Height; int inp = 0; byte[] output = new byte[Shared.AddPadding(w, 8) * Shared.AddPadding(h, 8) / 2]; for (int y1 = 0; y1 < h; y1 += 8) { for (int x1 = 0; x1 < w; x1 += 8) { for (int y = y1; y < y1 + 8; y++) { for (int x = x1; x < x1 + 8; x += 2) { byte newpixel; if (x >= w || y >= h) newpixel = 0; else { uint rgba = pixeldata[x + (y * w)]; uint r = (rgba >> 0) & 0xff; uint g = (rgba >> 8) & 0xff; uint b = (rgba >> 16) & 0xff; uint i1 = ((r + g + b) / 3) & 0xff; if ((x + (y * w) + 1) >= pixeldata.Length) rgba = 0; else rgba = pixeldata[x + (y * w) + 1]; r = (rgba >> 0) & 0xff; g = (rgba >> 8) & 0xff; b = (rgba >> 16) & 0xff; uint i2 = ((r + g + b) / 3) & 0xff; newpixel = (byte)((((i1 * 15) / 255) << 4) | (((i2 * 15) / 255) & 0xf)); } output[inp++] = newpixel; } } } } return output; } #endregion #region I8 private byte[] fromI8(byte[] tpl, int width, int height) { uint[] output = new uint[width * height]; int inp = 0; for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 8) { for (int y1 = y; y1 < y + 4; y1++) { for (int x1 = x; x1 < x + 8; x1++) { int pixel = tpl[inp++]; if (y1 >= height || x1 >= width) continue; output[y1 * width + x1] = (uint)((pixel << 0) | (pixel << 8) | (pixel << 16) | (255 << 24)); } } } } return Shared.UIntArrayToByteArray(output); } private byte[] toI8(Bitmap img) { uint[] pixeldata = imageToRgba(img); int w = img.Width; int h = img.Height; int inp = 0; byte[] output = new byte[Shared.AddPadding(w, 8) * Shared.AddPadding(h, 4)]; for (int y1 = 0; y1 < h; y1 += 4) { for (int x1 = 0; x1 < w; x1 += 8) { for (int y = y1; y < y1 + 4; y++) { for (int x = x1; x < x1 + 8; x++) { byte newpixel; if (x >= w || y >= h) newpixel = 0; else { uint rgba = pixeldata[x + (y * w)]; uint r = (rgba >> 0) & 0xff; uint g = (rgba >> 8) & 0xff; uint b = (rgba >> 16) & 0xff; newpixel = (byte)(((r + g + b) / 3) & 0xff); } output[inp++] = newpixel; } } } } return output; } #endregion #region IA4 private byte[] fromIA4(byte[] tpl, int width, int height) { uint[] output = new uint[width * height]; int inp = 0; for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 8) { for (int y1 = y; y1 < y + 4; y1++) { for (int x1 = x; x1 < x + 8; x1++) { int pixel = tpl[inp++]; if (y1 >= height || x1 >= width) continue; int i = ((pixel & 0x0F) * 255 / 15) & 0xff; int a = (((pixel >> 4) * 255) / 15) & 0xff; output[y1 * width + x1] = (uint)((i << 0) | (i << 8) | (i << 16) | (a << 24)); } } } } return Shared.UIntArrayToByteArray(output); } private byte[] toIA4(Bitmap img) { uint[] pixeldata = imageToRgba(img); int w = img.Width; int h = img.Height; int inp = 0; byte[] output = new byte[Shared.AddPadding(w, 8) * Shared.AddPadding(h, 4)]; for (int y1 = 0; y1 < h; y1 += 4) { for (int x1 = 0; x1 < w; x1 += 8) { for (int y = y1; y < y1 + 4; y++) { for (int x = x1; x < x1 + 8; x++) { byte newpixel; if (x >= w || y >= h) newpixel = 0; else { uint rgba = pixeldata[x + (y * w)]; uint r = (rgba >> 0) & 0xff; uint g = (rgba >> 8) & 0xff; uint b = (rgba >> 16) & 0xff; uint i = ((r + g + b) / 3) & 0xff; uint a = (rgba >> 24) & 0xff; newpixel = (byte)((((i * 15) / 255) & 0xf) | (((a * 15) / 255) << 4)); } output[inp++] = newpixel; } } } } return output; } #endregion #region IA8 private byte[] fromIA8(byte[] tpl, int width, int height) { uint[] output = new uint[width * height]; int inp = 0; for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { for (int y1 = y; y1 < y + 4; y1++) { for (int x1 = x; x1 < x + 4; x1++) { int pixel = Shared.Swap(BitConverter.ToUInt16(tpl, inp++ * 2)); if (y1 >= height || x1 >= width) continue; uint a = (uint)(pixel >> 8); uint i = (uint)(pixel & 0xff); output[y1 * width + x1] = (i << 0) | (i << 8) | (i << 16) | (a << 24); } } } } return Shared.UIntArrayToByteArray(output); } private byte[] toIA8(Bitmap img) { uint[] pixeldata = imageToRgba(img); int w = img.Width; int h = img.Height; int inp = 0; byte[] output = new byte[Shared.AddPadding(w, 4) * Shared.AddPadding(h, 4) * 2]; for (int y1 = 0; y1 < h; y1 += 4) { for (int x1 = 0; x1 < w; x1 += 4) { for (int y = y1; y < y1 + 4; y++) { for (int x = x1; x < x1 + 4; x++) { ushort newpixel; if (x >= w || y >= h) newpixel = 0; else { uint rgba = pixeldata[x + (y * w)]; uint r = (rgba >> 0) & 0xff; uint g = (rgba >> 8) & 0xff; uint b = (rgba >> 16) & 0xff; uint i = ((r + g + b) / 3) & 0xff; uint a = (rgba >> 24) & 0xff; newpixel = (ushort)((a << 8) | i); } byte[] temp = BitConverter.GetBytes(newpixel); Array.Reverse(temp); output[inp++] = (byte)(newpixel >> 8); output[inp++] = (byte)(newpixel & 0xff); } } } } return output; } #endregion #region CI4 private byte[] fromCI4(byte[] tpl, uint[] paletteData, int width, int height) { uint[] output = new uint[width * height]; int i = 0; for (int y = 0; y < height; y += 8) { for (int x = 0; x < width; x += 8) { for (int y1 = y; y1 < y + 8; y1++) { for (int x1 = x; x1 < x + 8; x1 += 2) { byte pixel = tpl[i++]; if (y1 >= height || x1 >= width) continue; output[y1 * width + x1] = paletteData[pixel >> 4]; ; if (y1 * width + x1 + 1 < output.Length) output[y1 * width + x1 + 1] = paletteData[pixel & 0x0F]; } } } } return Shared.UIntArrayToByteArray(output); } //toCI4 done in class ColorIndexConverter #endregion #region CI8 private byte[] fromCI8(byte[] tpl, uint[] paletteData, int width, int height) { uint[] output = new uint[width * height]; int i = 0; for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 8) { for (int y1 = y; y1 < y + 4; y1++) { for (int x1 = x; x1 < x + 8; x1++) { ushort pixel = tpl[i++]; if (y1 >= height || x1 >= width) continue; output[y1 * width + x1] = paletteData[pixel]; } } } } return Shared.UIntArrayToByteArray(output); } //toCI8 done in class ColorIndexConverter #endregion #region CI14X2 private byte[] fromCI14X2(byte[] tpl, uint[] paletteData, int width, int height) { uint[] output = new uint[width * height]; int i = 0; for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { for (int y1 = y; y1 < y + 4; y1++) { for (int x1 = x; x1 < x + 4; x1++) { ushort pixel = Shared.Swap(BitConverter.ToUInt16(tpl, i++ * 2)); if (y1 >= height || x1 >= width) continue; output[y1 * width + x1] = paletteData[pixel & 0x3FFF]; } } } } return Shared.UIntArrayToByteArray(output); } //toCI14X2 done in class ColorIndexConverter #endregion #region CMP private byte[] fromCMP(byte[] tpl, int width, int height) { uint[] output = new uint[width * height]; ushort[] c = new ushort[4]; int[] pix = new int[4]; int inp = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int ww = Shared.AddPadding(width, 8); int x0 = x & 0x03; int x1 = (x >> 2) & 0x01; int x2 = x >> 3; int y0 = y & 0x03; int y1 = (y >> 2) & 0x01; int y2 = y >> 3; int off = (8 * x1) + (16 * y1) + (32 * x2) + (4 * ww * y2); c[0] = Shared.Swap(BitConverter.ToUInt16(tpl, off)); c[1] = Shared.Swap(BitConverter.ToUInt16(tpl, off + 2)); if (c[0] > c[1]) { c[2] = (ushort)avg(2, 1, c[0], c[1]); c[3] = (ushort)avg(1, 2, c[0], c[1]); } else { c[2] = (ushort)avg(1, 1, c[0], c[1]); c[3] = 0; } uint pixel = Shared.Swap(BitConverter.ToUInt32(tpl, off + 4)); int ix = x0 + (4 * y0); int raw = c[(pixel >> (30 - (2 * ix))) & 0x03]; pix[0] = (raw >> 8) & 0xf8; pix[1] = (raw >> 3) & 0xf8; pix[2] = (raw << 3) & 0xf8; pix[3] = 0xff; if (((pixel >> (30 - (2 * ix))) & 0x03) == 3 && c[0] <= c[1]) pix[3] = 0x00; output[inp] = (uint)((pix[0] << 16) | (pix[1] << 8) | (pix[2] << 0) | (pix[3] << 24)); inp++; } } return Shared.UIntArrayToByteArray(output); } //There's currently no conversion to CMP #endregion #endregion #region Events /// /// Fires debugging messages. You may write them into a log file or log textbox. /// private void fireDebug(string debugMessage, params object[] args) { System.Diagnostics.Debug.WriteLine($"{debugMessage}"); } #endregion } public class TPL_Header { private uint tplMagic = 0x0020AF30; private uint numOfTextures = 0; private uint headerSize = 0x0C; public uint TplMagic { get { return tplMagic; } } public uint NumOfTextures { get { return numOfTextures; } set { numOfTextures = value; } } public uint HeaderSize { get { return headerSize; } } public void Write(Stream writeStream) { writeStream.Write(BitConverter.GetBytes(Shared.Swap(tplMagic)), 0, 4); writeStream.Write(BitConverter.GetBytes(Shared.Swap(numOfTextures)), 0, 4); writeStream.Write(BitConverter.GetBytes(Shared.Swap(headerSize)), 0, 4); } } public class TPL_TextureEntry { private uint textureHeaderOffset = 0x00000000; private uint paletteHeaderOffset = 0x00000000; public uint TextureHeaderOffset { get { return textureHeaderOffset; } set { textureHeaderOffset = value; } } public uint PaletteHeaderOffset { get { return paletteHeaderOffset; } set { paletteHeaderOffset = value; } } public void Write(Stream writeStream) { writeStream.Write(BitConverter.GetBytes(Shared.Swap(textureHeaderOffset)), 0, 4); writeStream.Write(BitConverter.GetBytes(Shared.Swap(paletteHeaderOffset)), 0, 4); } } public class TPL_TextureHeader { private ushort textureHeight; private ushort textureWidth; private uint textureFormat; private uint textureDataOffset; private uint wrapS = 0x00000000; private uint wrapT = 0x00000000; private uint minFilter = 0x00000001; private uint magFilter = 0x00000001; private uint lodBias = 0x00000000; private byte edgeLod = 0x00; private byte minLod = 0x00; private byte maxLod = 0x00; private byte unpacked = 0x00; public ushort TextureHeight { get { return textureHeight; } set { textureHeight = value; } } public ushort TextureWidth { get { return textureWidth; } set { textureWidth = value; } } public uint TextureFormat { get { return textureFormat; } set { textureFormat = value; } } public uint TextureDataOffset { get { return textureDataOffset; } set { textureDataOffset = value; } } public uint WrapS { get { return wrapS; } set { wrapS = value; } } public uint WrapT { get { return wrapT; } set { wrapT = value; } } public uint MinFilter { get { return minFilter; } set { minFilter = value; } } public uint MagFilter { get { return magFilter; } set { magFilter = value; } } public uint LodBias { get { return lodBias; } set { lodBias = value; } } public byte EdgeLod { get { return edgeLod; } set { edgeLod = value; } } public byte MinLod { get { return minLod; } set { minLod = value; } } public byte MaxLod { get { return maxLod; } set { maxLod = value; } } public byte Unpacked { get { return unpacked; } set { unpacked = value; } } public void Write(Stream writeStream) { writeStream.Write(BitConverter.GetBytes(Shared.Swap(textureHeight)), 0, 2); writeStream.Write(BitConverter.GetBytes(Shared.Swap(textureWidth)), 0, 2); writeStream.Write(BitConverter.GetBytes(Shared.Swap(textureFormat)), 0, 4); writeStream.Write(BitConverter.GetBytes(Shared.Swap(textureDataOffset)), 0, 4); writeStream.Write(BitConverter.GetBytes(Shared.Swap(wrapS)), 0, 4); writeStream.Write(BitConverter.GetBytes(Shared.Swap(wrapT)), 0, 4); writeStream.Write(BitConverter.GetBytes(Shared.Swap(minFilter)), 0, 4); writeStream.Write(BitConverter.GetBytes(Shared.Swap(magFilter)), 0, 4); writeStream.Write(BitConverter.GetBytes(Shared.Swap(lodBias)), 0, 4); writeStream.WriteByte(edgeLod); writeStream.WriteByte(minLod); writeStream.WriteByte(maxLod); writeStream.WriteByte(unpacked); } } public class TPL_PaletteHeader { private ushort numberOfItems = 0x0000; private byte unpacked = 0x00; private byte pad = 0x00; private uint paletteFormat = 255; private uint paletteDataOffset; public ushort NumberOfItems { get { return numberOfItems; } set { numberOfItems = value; } } public byte Unpacked { get { return unpacked; } set { unpacked = value; } } public byte Pad { get { return pad; } set { pad = value; } } public uint PaletteFormat { get { return paletteFormat; } set { paletteFormat = value; } } public uint PaletteDataOffset { get { return paletteDataOffset; } set { paletteDataOffset = value; } } public void Write(Stream writeStream) { writeStream.Write(BitConverter.GetBytes(Shared.Swap(numberOfItems)), 0, 2); writeStream.WriteByte(unpacked); writeStream.WriteByte(pad); writeStream.Write(BitConverter.GetBytes(Shared.Swap(paletteFormat)), 0, 4); writeStream.Write(BitConverter.GetBytes(Shared.Swap(paletteDataOffset)), 0, 4); } } internal class ColorIndexConverter { private uint[] rgbaPalette; private byte[] tplPalette; private uint[] rgbaData; private byte[] tplData; private TPL_TextureFormat tplFormat; private TPL_PaletteFormat paletteFormat; private int width; private int height; public byte[] Palette { get { return tplPalette; } } public byte[] Data { get { return tplData; } } public ColorIndexConverter(uint[] rgbaData, int width, int height, TPL_TextureFormat tplFormat, TPL_PaletteFormat paletteFormat) { if (tplFormat != TPL_TextureFormat.CI4 && tplFormat != TPL_TextureFormat.CI8) // && tplFormat != TPL_TextureFormat.CI14X2) throw new Exception("Texture format must be either CI4 or CI8"); // or CI14X2!"); if (paletteFormat != TPL_PaletteFormat.IA8 && paletteFormat != TPL_PaletteFormat.RGB565 && paletteFormat != TPL_PaletteFormat.RGB5A3) throw new Exception("Palette format must be either IA8, RGB565 or RGB5A3!"); this.rgbaData = rgbaData; this.width = width; this.height = height; this.tplFormat = tplFormat; this.paletteFormat = paletteFormat; buildPalette(); if (tplFormat == TPL_TextureFormat.CI4) toCI4(); else if (tplFormat == TPL_TextureFormat.CI8) toCI8(); else toCI14X2(); } #region Private Functions private void toCI4() { byte[] indexData = new byte[libWiiSharp.Shared.AddPadding(width, 8) * libWiiSharp.Shared.AddPadding(height, 8) / 2]; int i = 0; for (int y = 0; y < height; y += 8) { for (int x = 0; x < width; x += 8) { for (int y1 = y; y1 < y + 8; y1++) { for (int x1 = x; x1 < x + 8; x1 += 2) { uint pixel; if (y1 >= height || x1 >= width) pixel = 0; else pixel = rgbaData[y1 * width + x1]; uint index1 = getColorIndex(pixel); if (y1 >= height || x1 >= width) pixel = 0; else if (y1 * width + x1 + 1 >= rgbaData.Length) pixel = 0; else pixel = rgbaData[y1 * width + x1 + 1]; uint index2 = getColorIndex(pixel); indexData[i++] = (byte)(((byte)index1 << 4) | (byte)index2); } } } } this.tplData = indexData; } private void toCI8() { byte[] indexData = new byte[libWiiSharp.Shared.AddPadding(width, 8) * libWiiSharp.Shared.AddPadding(height, 4)]; int i = 0; for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 8) { for (int y1 = y; y1 < y + 4; y1++) { for (int x1 = x; x1 < x + 8; x1++) { uint pixel; if (y1 >= height || x1 >= width) pixel = 0; else pixel = rgbaData[y1 * width + x1]; indexData[i++] = (byte)getColorIndex(pixel); } } } } this.tplData = indexData; } private void toCI14X2() { byte[] indexData = new byte[libWiiSharp.Shared.AddPadding(width, 4) * libWiiSharp.Shared.AddPadding(height, 4) * 2]; int i = 0; for (int y = 0; y < height; y += 4) { for (int x = 0; x < width; x += 4) { for (int y1 = y; y1 < y + 4; y1++) { for (int x1 = x; x1 < x + 4; x1++) { uint pixel; if (y1 >= height || x1 >= width) pixel = 0; else pixel = rgbaData[y1 * width + x1]; byte[] temp = BitConverter.GetBytes((ushort)getColorIndex(pixel)); indexData[i++] = temp[1]; indexData[i++] = temp[0]; } } } } this.tplData = indexData; } private void buildPalette() { int palLength = 256; if (tplFormat == TPL_TextureFormat.CI4) palLength = 16; else if (tplFormat == TPL_TextureFormat.CI14X2) palLength = 16384; List palette = new List(); List tPalette = new List(); palette.Add(0); tPalette.Add(0); for (int i = 1; i < rgbaData.Length; i++) { if (palette.Count == palLength) break; if (((rgbaData[i] >> 24) & 0xff) < ((tplFormat == TPL_TextureFormat.CI14X2) ? 1 : 25)) continue; ushort tplValue = libWiiSharp.Shared.Swap(convertToPaletteValue((int)rgbaData[i])); if (!palette.Contains(rgbaData[i]) && !tPalette.Contains(tplValue)) { palette.Add(rgbaData[i]); tPalette.Add(tplValue); } } while (palette.Count % 16 != 0) { palette.Add(0xffffffff); tPalette.Add(0xffff); } tplPalette = libWiiSharp.Shared.UShortArrayToByteArray(tPalette.ToArray()); rgbaPalette = palette.ToArray(); } private ushort convertToPaletteValue(int rgba) { int newpixel = 0, r, g, b, a; if (paletteFormat == TPL_PaletteFormat.IA8) { int intensity = ((((rgba >> 0) & 0xff) + ((rgba >> 8) & 0xff) + ((rgba >> 16) & 0xff)) / 3) & 0xff; int alpha = (rgba >> 24) & 0xff; newpixel = (ushort)((alpha << 8) | intensity); } else if (paletteFormat == TPL_PaletteFormat.RGB565) { newpixel = (ushort)(((((rgba >> 16) & 0xff) >> 3) << 11) | ((((rgba >> 8) & 0xff) >> 2) << 5) | ((((rgba >> 0) & 0xff) >> 3) << 0)); } else { r = (rgba >> 16) & 0xff; g = (rgba >> 8) & 0xff; b = (rgba >> 0) & 0xff; a = (rgba >> 24) & 0xff; if (a <= 0xda) //RGB4A3 { newpixel &= ~(1 << 15); r = ((r * 15) / 255) & 0xf; g = ((g * 15) / 255) & 0xf; b = ((b * 15) / 255) & 0xf; a = ((a * 7) / 255) & 0x7; newpixel |= a << 12; newpixel |= b << 0; newpixel |= g << 4; newpixel |= r << 8; } else //RGB5 { newpixel |= (1 << 15); r = ((r * 31) / 255) & 0x1f; g = ((g * 31) / 255) & 0x1f; b = ((b * 31) / 255) & 0x1f; newpixel |= b << 0; newpixel |= g << 5; newpixel |= r << 10; } } return (ushort)newpixel; } private uint getColorIndex(uint value) { uint minDistance = 0x7FFFFFFF; uint colorIndex = 0; if (((value >> 24) & 0xFF) < ((tplFormat == TPL_TextureFormat.CI14X2) ? 1 : 25)) return 0; ushort color = convertToPaletteValue((int)value); for (int i = 0; i < rgbaPalette.Length; i++) { ushort curPal = convertToPaletteValue((int)rgbaPalette[i]); if (color == curPal) return (uint)i; uint curDistance = getDistance(color, curPal); //(uint)Math.Abs(Math.Abs(color) - Math.Abs(curVal)); if (curDistance < minDistance) { minDistance = curDistance; colorIndex = (uint)i; } } return colorIndex; } private uint getDistance(ushort color, ushort paletteColor) { uint curCol = convertToRgbaValue(color); uint palCol = convertToRgbaValue(paletteColor); uint curA = (curCol >> 24) & 0xFF; uint curR = (curCol >> 16) & 0xFF; uint curG = (curCol >> 8) & 0xFF; uint curB = (curCol >> 0) & 0xFF; uint palA = (palCol >> 24) & 0xFF; uint palR = (palCol >> 16) & 0xFF; uint palG = (palCol >> 8) & 0xFF; uint palB = (palCol >> 0) & 0xFF; uint distA = Math.Max(curA, palA) - Math.Min(curA, palA); uint distR = Math.Max(curR, palR) - Math.Min(curR, palR); uint distG = Math.Max(curG, palG) - Math.Min(curG, palG); uint distB = Math.Max(curB, palB) - Math.Min(curB, palB); return distA + distR + distG + distB; } private uint convertToRgbaValue(ushort pixel) { int rgba = 0, r, g, b, a; if (paletteFormat == TPL_PaletteFormat.IA8) { int i = (pixel >> 8); a = pixel & 0xff; rgba = (i << 0) | (i << 8) | (i << 16) | (a << 24); } else if (paletteFormat == TPL_PaletteFormat.RGB565) { b = (((pixel >> 11) & 0x1F) << 3) & 0xff; g = (((pixel >> 5) & 0x3F) << 2) & 0xff; r = (((pixel >> 0) & 0x1F) << 3) & 0xff; a = 255; rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24); } else { if ((pixel & (1 << 15)) != 0) { b = (((pixel >> 10) & 0x1F) * 255) / 31; g = (((pixel >> 5) & 0x1F) * 255) / 31; r = (((pixel >> 0) & 0x1F) * 255) / 31; a = 255; } else { a = (((pixel >> 12) & 0x07) * 255) / 7; b = (((pixel >> 8) & 0x0F) * 255) / 15; g = (((pixel >> 4) & 0x0F) * 255) / 15; r = (((pixel >> 0) & 0x0F) * 255) / 15; } rgba = (r << 0) | (g << 8) | (b << 16) | (a << 24); } return (uint)rgba; } #endregion } public static class Shared { /// /// Merges two string arrays into one without double entries. /// /// /// /// public static string[] MergeStringArrays(string[] a, string[] b) { List sList = new List(a); foreach (string currentString in b) if (!sList.Contains(currentString)) sList.Add(currentString); sList.Sort(); return sList.ToArray(); } /// /// Compares two byte arrays. /// /// /// /// /// /// /// public static bool CompareByteArrays(byte[] first, int firstIndex, byte[] second, int secondIndex, int length) { if (first.Length < length || second.Length < length) return false; for (int i = 0; i < length; i++) if (first[firstIndex + i] != second[secondIndex + i]) return false; return true; } /// /// Compares two byte arrays. /// /// /// /// public static bool CompareByteArrays(byte[] first, byte[] second) { if (first.Length != second.Length) return false; else for (int i = 0; i < first.Length; i++) if (first[i] != second[i]) return false; return true; } /// /// Turns a byte array into a string, default separator is a space. /// /// /// /// public static string ByteArrayToString(byte[] byteArray, char separator = ' ') { string res = string.Empty; foreach (byte b in byteArray) res += b.ToString("x2").ToUpper() + separator; return res.Remove(res.Length - 1); } /// /// Turns a hex string into a byte array. /// /// /// public static byte[] HexStringToByteArray(string hexString) { byte[] ba = new byte[hexString.Length / 2]; for (int i = 0; i < hexString.Length / 2; i++) ba[i] = byte.Parse(hexString.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber); return ba; } /// /// Counts how often the given char exists in the given string. /// /// /// /// public static int CountCharsInString(string theString, char theChar) { int count = 0; foreach (char thisChar in theString) if (thisChar == theChar) count++; return count; } /// /// Pads the given value to a multiple of the given padding value, default padding value is 64. /// /// /// public static long AddPadding(long value) { return AddPadding(value, 64); } /// /// Pads the given value to a multiple of the given padding value, default padding value is 64. /// /// /// /// public static long AddPadding(long value, int padding) { if (value % padding != 0) { value = value + (padding - (value % padding)); } return value; } /// /// Pads the given value to a multiple of the given padding value, default padding value is 64. /// /// /// public static int AddPadding(int value) { return AddPadding(value, 64); } /// /// Pads the given value to a multiple of the given padding value, default padding value is 64. /// /// /// /// public static int AddPadding(int value, int padding) { if (value % padding != 0) { value = value + (padding - (value % padding)); } return value; } /// /// Swaps endianness. /// /// /// public static ushort Swap(ushort value) { return (ushort)IPAddress.HostToNetworkOrder((short)value); } /// /// Swaps endianness. /// /// /// public static uint Swap(uint value) { return (uint)IPAddress.HostToNetworkOrder((int)value); } /// /// Swaps endianness /// /// /// public static ulong Swap(ulong value) { return (ulong)IPAddress.HostToNetworkOrder((long)value); } /// /// Turns a ushort array into a byte array. /// /// /// public static byte[] UShortArrayToByteArray(ushort[] array) { List results = new List(); foreach (ushort value in array) { byte[] converted = BitConverter.GetBytes(value); results.AddRange(converted); } return results.ToArray(); } /// /// Turns a uint array into a byte array. /// /// /// public static byte[] UIntArrayToByteArray(uint[] array) { List results = new List(); foreach (uint value in array) { byte[] converted = BitConverter.GetBytes(value); results.AddRange(converted); } return results.ToArray(); } /// /// Turns a byte array into a uint array. /// /// /// public static uint[] ByteArrayToUIntArray(byte[] array) { UInt32[] converted = new UInt32[array.Length / 4]; int j = 0; for (int i = 0; i < array.Length; i += 4) converted[j++] = BitConverter.ToUInt32(array, i); return converted; } /// /// Turns a byte array into a ushort array. /// /// /// public static ushort[] ByteArrayToUShortArray(byte[] array) { ushort[] converted = new ushort[array.Length / 2]; int j = 0; for (int i = 0; i < array.Length; i += 2) converted[j++] = BitConverter.ToUInt16(array, i); return converted; } } }