public static WTPFile Create(string filepath) { string baseFileName = filepath.Substring(filepath.LastIndexOf(Path.DirectorySeparatorChar) + 1); string baseFileNameLower = baseFileName.ToLower(); string directory = ""; if (filepath.IndexOf(Path.DirectorySeparatorChar) != -1) { directory = filepath.Substring(0, filepath.LastIndexOf(Path.DirectorySeparatorChar)) + Path.DirectorySeparatorChar; } if (baseFileNameLower.EndsWith(".wtp")) { baseFileName = baseFileName.Substring(0, baseFileName.Length - 4); } else if (baseFileNameLower.EndsWith(".tga")) { if (baseFileNameLower.EndsWith("_primary.tga")) { baseFileName = baseFileName.Substring(0, baseFileName.Length - 12); } else if (baseFileNameLower.EndsWith("_secondary.tga")) { baseFileName = baseFileName.Substring(0, baseFileName.Length - 14); } else if (baseFileNameLower.EndsWith("_weapon.tga") || baseFileNameLower.EndsWith("_banner.tga")) { baseFileName = baseFileName.Substring(0, baseFileName.Length - 11); } else if (baseFileNameLower.EndsWith("_badge.tga")) { baseFileName = baseFileName.Substring(0, baseFileName.Length - 10); } else if (baseFileNameLower.EndsWith("_eyes.tga") || baseFileNameLower.EndsWith("_trim.tga") || baseFileNameLower.EndsWith("_dirt.tga")) { baseFileName = baseFileName.Substring(0, baseFileName.Length - 9); } else { baseFileName = baseFileName.Substring(0, baseFileName.Length - 4); } } else { throw new InvalidFileException("File path specified is not valid for a WTP file or one of its components"); } string skinName = baseFileName.Substring(baseFileName.LastIndexOf('_') + 1); //make it forwards compatible on the off-chance Relic introduce multiple textures ChunkyDataDATA defaultData = null; ChunkyData attr = null; FileStream fs = null; BinaryReader br = null; FileInfo file = null; byte [] data; int width = 0; int height = 0; if (File.Exists(directory + baseFileName + ".tga")) { CompilationEvent("Reading " + baseFileName + ".tga"); file = new FileInfo(directory + baseFileName + ".tga"); fs = file.OpenRead(); br = new BinaryReader(fs); br.BaseStream.Seek(12, SeekOrigin.Begin); width = br.ReadInt16(); height = br.ReadInt16(); br.BaseStream.Seek(0, SeekOrigin.Begin); data = br.ReadBytes((int)br.BaseStream.Length); defaultData = ChunkyDataDATAIMAG.CreateFromTGA(2, "", data); br.Close(); data = new byte[] { 0x0, 0x0, 0x0, 0x0, (byte)width, (byte)(width >> 8), (byte)(width >> 16), (byte)(width >> 24), (byte)(height), (byte)(height >> 8), (byte)(height >> 16), (byte)(height >> 24), 0x1, 0x0, 0x0, 0x0 }; attr = new ChunkyDataUnknown("ATTR", 2, "", data); } else { throw new RelicTools.Exceptions.FileNotFoundException("WTP files must have a 32bit layer e.g. _default.tga layer"); } string dirt_name = directory + baseFileName + "_Dirt.tga"; ChunkyFolder defaultFolder = new ChunkyFolder("IMAG", 1, "TOOL:" + dirt_name); defaultFolder.Children.Add(attr); defaultFolder.Children.Add(defaultData); ChunkyDataPTLD primary = null; ChunkyDataPTLD secondary = null; ChunkyDataPTLD trim = null; ChunkyDataPTLD weapon = null; ChunkyDataPTLD eyes = null; ChunkyDataPTLD dirt = null; ChunkyDataPTBD badge = null; ChunkyDataPTBN banner = null; string primaryname = directory + baseFileName + "_Primary.tga"; string secondaryname = directory + baseFileName + "_Secondary.tga"; string trimname = directory + baseFileName + "_Trim.tga"; string weaponname = directory + baseFileName + "_Weapon.tga"; string eyesname = directory + baseFileName + "_Eyes.tga"; string badgename = directory + baseFileName + "_Badge.tga"; string bannername = directory + baseFileName + "_Banner.tga"; //int byteRead = width*height+18;//length of TGA data plus header if (File.Exists(dirt_name)) { CompilationEvent("Reading " + baseFileName + "_Dirt.tga"); file = new FileInfo(dirt_name); fs = file.OpenRead(); br = new BinaryReader(fs); br.BaseStream.Seek(0, SeekOrigin.Begin); data = br.ReadBytes((int)br.BaseStream.Length); br.Close(); dirt = ChunkyDataPTLD.CreateFromTGA(PTLD_Layers.Dirt, 1, "", data); } else { throw new RelicTools.Exceptions.FileNotFoundException("WTP Files must contain a dirt layer"); } data = new byte[8]; data[0] = (byte)(width); data[1] = (byte)(width >> 8); data[2] = (byte)(width >> 16); data[3] = (byte)(width >> 24); data[4] = (byte)(height); data[5] = (byte)(height >> 8); data[6] = (byte)(height >> 16); data[7] = (byte)(height >> 24); ChunkyData info = new ChunkyDataUnknown("INFO", 1, "", data); if (File.Exists(primaryname)) { CompilationEvent("Reading " + baseFileName + "_Primary.tga"); file = new FileInfo(primaryname); fs = file.OpenRead(); br = new BinaryReader(fs); br.BaseStream.Seek(0, SeekOrigin.Begin); data = br.ReadBytes((int)br.BaseStream.Length); br.Close(); primary = ChunkyDataPTLD.CreateFromTGA(PTLD_Layers.Primary, 1, "", data); if (primary == null) { CompilationEvent("Skipped " + baseFileName + "_Primary.tga - no team colour data"); } } if (File.Exists(secondaryname)) { CompilationEvent("Reading " + baseFileName + "_Secondary.tga"); file = new FileInfo(secondaryname); fs = file.OpenRead(); br = new BinaryReader(fs); br.BaseStream.Seek(0, SeekOrigin.Begin); data = br.ReadBytes((int)br.BaseStream.Length); br.Close(); secondary = ChunkyDataPTLD.CreateFromTGA(PTLD_Layers.Secondary, 1, "", data); if (secondary == null) { CompilationEvent("Skipped " + baseFileName + "_Secondary.tga - no team colour data"); } } if (File.Exists(trimname)) { CompilationEvent("Reading " + baseFileName + "_Trim.tga"); file = new FileInfo(trimname); fs = file.OpenRead(); br = new BinaryReader(fs); br.BaseStream.Seek(0, SeekOrigin.Begin); data = br.ReadBytes((int)br.BaseStream.Length); br.Close(); trim = ChunkyDataPTLD.CreateFromTGA(PTLD_Layers.Trim, 1, "", data); if (trim == null) { CompilationEvent("Skipped " + baseFileName + "_Trim.tga - no team colour data"); } } if (File.Exists(weaponname)) { CompilationEvent("Reading " + baseFileName + "_Weapon.tga"); file = new FileInfo(weaponname); fs = file.OpenRead(); br = new BinaryReader(fs); br.BaseStream.Seek(0, SeekOrigin.Begin); data = br.ReadBytes((int)br.BaseStream.Length); br.Close(); weapon = ChunkyDataPTLD.CreateFromTGA(PTLD_Layers.Weapon, 1, "", data); if (weapon == null) { CompilationEvent("Skipped " + baseFileName + "_Weapon.tga - no team colour data"); } } if (File.Exists(eyesname)) { CompilationEvent("Reading " + baseFileName + "_Eyes.tga"); file = new FileInfo(eyesname); fs = file.OpenRead(); br = new BinaryReader(fs); br.BaseStream.Seek(0, SeekOrigin.Begin); data = br.ReadBytes((int)br.BaseStream.Length); br.Close(); eyes = ChunkyDataPTLD.CreateFromTGA(PTLD_Layers.Eyes, 1, "", data); if (eyes == null) { CompilationEvent("Skipped " + baseFileName + "_Eyes.tga - no team colour data"); } } if (File.Exists(badgename)) { CompilationEvent("Reading " + baseFileName + "_Badge.tga"); file = new FileInfo(badgename); fs = file.OpenRead(); br = new BinaryReader(fs); br.BaseStream.Seek(0, SeekOrigin.Begin); data = br.ReadBytes((int)br.BaseStream.Length); br.Close(); badge = ChunkyDataPTBD.CreateFromTGA(1, "", data); } if (File.Exists(bannername)) { CompilationEvent("Reading " + baseFileName + "_Banner.tga"); file = new FileInfo(bannername); fs = file.OpenRead(); br = new BinaryReader(fs); br.BaseStream.Seek(0, SeekOrigin.Begin); data = br.ReadBytes((int)br.BaseStream.Length); br.Close(); banner = ChunkyDataPTBN.CreateFromTGA(1, "", data); } if (badge != null && banner != null) { if (((badge.Pos_x <= banner.Pos_x && (badge.Pos_x + badge.Width) >= banner.Pos_x) || (badge.Pos_x <= (banner.Pos_x + banner.Width) && (badge.Pos_x + badge.Width) >= (banner.Pos_x + banner.Width))) && ((badge.Pos_y <= banner.Pos_y && (badge.Pos_y + badge.Height) >= banner.Pos_y) || (badge.Pos_y <= (banner.Pos_y + banner.Height) && (badge.Pos_y + badge.Height) >= (banner.Pos_y + banner.Height)))) { throw new InvalidFileException("Badge and banner position overlap"); } } CompilationEvent("Compiling WTP File"); ChunkyFolder tpat = new ChunkyFolder("TPAT", 3, skinName); tpat.Children.Add(info); tpat.Children.Add(primary); tpat.Children.Add(secondary); tpat.Children.Add(trim); tpat.Children.Add(weapon); tpat.Children.Add(eyes); tpat.Children.Add(dirt); tpat.Children.Add(defaultFolder); tpat.Children.Add(banner); tpat.Children.Add(badge); return(new WTPFile(baseFileName + ".wtp", tpat)); //RelicChunkyFile.SaveChunky(directory+baseFileName+".wtp", tpat.GetBytes()); }
public byte[] CreateCompositeTGABytes(LayerCollection colours, string badgePath, string bannerPath) { if (colours[PTLD_Layers.Dirt].Length != 3) { throw new InvalidOperationException("LayerCollection 'colours' must contain RGB colour data in a 3-byte array"); } FileInfo file = null; BinaryReader br = null; byte[] badge = new byte[64 * 64 * 4]; byte[] banner = new byte[64 * 96 * 4]; byte[] temp; CompilationEvent("Loading badge"); if (File.Exists(badgePath)) { file = new FileInfo(badgePath); br = new BinaryReader(file.OpenRead()); temp = br.ReadBytes((int)file.Length); br.Close(); if ((temp[12] + (temp[13] << 8)) != 64 || (temp[14] + (temp[15] << 8)) != 64) { CompilationEvent("Invalid badge image - badge must be a 64x64 pixel 24/32-bit TGA image", true); } else if (!((temp[16] == 32 || temp[16] == 24) && temp[2] == 2)) { CompilationEvent("Invalid badge image - badge must be a 64x64 pixel 24/32-bit TGA image", true); } else { Buffer.BlockCopy(ImageConverter.TGAto32bitTGA(temp), 18, badge, 0, badge.Length); } } else { CompilationEvent("Invalid badge path - file must exist. Using blank badge.", true); } CompilationEvent("Loading banner"); if (File.Exists(bannerPath)) { file = new FileInfo(bannerPath); br = new BinaryReader(file.OpenRead()); temp = br.ReadBytes((int)file.Length); br.Close(); if ((temp[12] + (temp[13] << 8)) != 64 || (temp[14] + (temp[15] << 8)) != 96) { CompilationEvent("Invalid banner image - banner must be a 64x96 pixel 24/32-bit TGA image", true); } else if (!((temp[16] == 32 || temp[16] == 24) && temp[2] == 2)) { CompilationEvent("Invalid banner image - banner must be a 64x64 pixel 24/32-bit TGA image", true); } else { Buffer.BlockCopy(ImageConverter.TGAto32bitTGA(temp), 18, banner, 0, banner.Length); } } else { CompilationEvent("Invalid banner path - file must exist. Using blank badge.", true); } ChunkyFolder root = (ChunkyFolder)this.ChunkyStructures[0].RootChunks[0]; ChunkyChunk chunk = null; ChunkyDataPTLD ptld = null; ChunkyDataPTBN bannerLayer = null; ChunkyDataPTBD badgeLayer = null; ChunkyDataINFOTPAT info = (ChunkyDataINFOTPAT)root.Children[0]; LayerCollection layers = new LayerCollection(info.Width * info.Height); byte[] mainImage = new byte[info.Width * info.Height]; int badge_bottom = int.MaxValue; int badge_top = int.MaxValue; int badge_left = int.MaxValue; int badge_right = int.MaxValue; int banner_bottom = int.MaxValue; int banner_top = int.MaxValue; int banner_left = int.MaxValue; int banner_right = int.MaxValue; CompilationEvent("Loading colour layers"); for (int i = 0; i < root.Children.Count; i++) { chunk = root.Children[i]; if (chunk is ChunkyDataPTLD) { ptld = (ChunkyDataPTLD)root.Children[i]; layers[(int)ptld.Layer] = ptld.GetColourBytes(); } else if (chunk is ChunkyDataPTBD) { badgeLayer = (ChunkyDataPTBD)chunk; badge_bottom = (int)badgeLayer.Pos_y; badge_top = badge_bottom + (int)badgeLayer.Height; badge_left = (int)badgeLayer.Pos_x; badge_right = badge_left + (int)badgeLayer.Width; } else if (chunk is ChunkyDataPTBN) { bannerLayer = (ChunkyDataPTBN)chunk; banner_bottom = (int)bannerLayer.Pos_y; banner_top = banner_bottom + (int)bannerLayer.Height; banner_left = (int)bannerLayer.Pos_x; banner_right = banner_left + (int)bannerLayer.Width; } else if (chunk is ChunkyFolder) { mainImage = ((ChunkyData)((ChunkyFolder)chunk).Children[1]).GetDataBytes(); } } int bytePos = 0; int bytePos32bit = 0; int bytePosBadge = 0; int bytePosBanner = 0; byte[] badgeByte; byte[] bannerByte; double ratio; double ratio2; double ratio3; double ratioTC; double tempByte = 0; double extra = 0; int badge_width = 0; if (badgeLayer != null) { badge_width = (int)badgeLayer.Width; } int banner_width = 0; if (bannerLayer != null) { banner_width = (int)bannerLayer.Width; } CompilationEvent("Compiling TGA"); for (int i = 0; i < info.Height; i++) { for (int j = 0; j < info.Width; j++) { bytePos = (i * info.Width) + j; bytePos32bit = bytePos * 4; ratio = layers[PTLD_Layers.Dirt][bytePos] / 255.0; if (badge_left <= j && badge_right > j && badge_bottom <= i && badge_top > i) { bytePosBadge = ((i - badge_bottom) * badge_width + (j - badge_left)) * 4; ratio2 = (badge[bytePosBadge + 3] / 255.0); badgeByte = new byte[] { badge[bytePosBadge], badge[bytePosBadge + 1], badge[bytePosBadge + 2] }; } else { ratio2 = 0; badgeByte = new byte[3]; } if (banner_left <= j && banner_right > j && banner_bottom <= i && banner_top > i) { bytePosBanner = ((i - banner_bottom) * banner_width + (j - banner_left)) * 4; ratio3 = (banner[bytePosBanner + 3] / 255.0); bannerByte = new byte[] { banner[bytePosBanner], banner[bytePosBanner + 1], banner[bytePosBanner + 2] }; } else { ratio3 = 0; bannerByte = new byte[3]; } ratioTC = 1 - ratio; ratio2 = ratioTC * ratio2; ratio3 = ratioTC * ratio3; ratioTC = (ratioTC - ratio2 - ratio3) * 2; extra = 0; //TGA files run in reverse - little-endian ARGB, which ends up as BGRA in the file //red tempByte = (ratio * mainImage[bytePos32bit + 2]) + (ratioTC) * ((colours[PTLD_Layers.Primary][0] * layers[PTLD_Layers.Primary][bytePos] / 255.0) + (colours[PTLD_Layers.Secondary][0] * layers[PTLD_Layers.Secondary][bytePos] / 255.0) + (colours[PTLD_Layers.Weapon][0] * layers[PTLD_Layers.Weapon][bytePos] / 255.0) + (colours[PTLD_Layers.Trim][0] * layers[PTLD_Layers.Trim][bytePos] / 255.0) + (colours[PTLD_Layers.Eyes][0] * layers[PTLD_Layers.Eyes][bytePos] / 255.0)) + ratio2 * badgeByte[2] + ratio3 * bannerByte[2]; if (tempByte > byte.MaxValue) { //CompilationEvent("Colour overflow at X:"+j.ToString()+", Y:"+i.ToString()+". Colour will show as Magenta or other bright colour with some colour schemes.", true); mainImage[bytePos32bit + 2] = byte.MaxValue; extra = (tempByte - byte.MaxValue); } else { mainImage[bytePos32bit + 2] = (byte)tempByte; } //green tempByte = (ratio * mainImage[bytePos32bit + 1]) + (ratioTC) * ((colours[PTLD_Layers.Primary][1] * layers[PTLD_Layers.Primary][bytePos] / 255.0) + (colours[PTLD_Layers.Secondary][1] * layers[PTLD_Layers.Secondary][bytePos] / 255.0) + (colours[PTLD_Layers.Weapon][1] * layers[PTLD_Layers.Weapon][bytePos] / 255.0) + (colours[PTLD_Layers.Trim][1] * layers[PTLD_Layers.Trim][bytePos] / 255.0) + (colours[PTLD_Layers.Eyes][1] * layers[PTLD_Layers.Eyes][bytePos] / 255.0)) + extra + ratio2 * badgeByte[1] + ratio3 * bannerByte[1]; if (tempByte > byte.MaxValue) { //CompilationEvent("Colour overflow at X:"+j.ToString()+", Y:"+i.ToString()+". Colour will show as Magenta or other bright colour with some colour schemes.", true); mainImage[bytePos32bit + 1] = byte.MaxValue; extra = extra + tempByte - byte.MaxValue; tempByte = mainImage[bytePos32bit + 2] + extra; if (tempByte > byte.MaxValue) { tempByte = byte.MaxValue; } mainImage[bytePos32bit + 2] = (byte)tempByte; } else { mainImage[bytePos32bit + 1] = (byte)tempByte; } //blue tempByte = (ratio * mainImage[bytePos32bit]) + (ratioTC) * ((colours[PTLD_Layers.Primary][2] * layers[PTLD_Layers.Primary][bytePos] / 255.0) + (colours[PTLD_Layers.Secondary][2] * layers[PTLD_Layers.Secondary][bytePos] / 255.0) + (colours[PTLD_Layers.Weapon][2] * layers[PTLD_Layers.Weapon][bytePos] / 255.0) + (colours[PTLD_Layers.Trim][2] * layers[PTLD_Layers.Trim][bytePos] / 255.0) + (colours[PTLD_Layers.Eyes][2] * layers[PTLD_Layers.Eyes][bytePos] / 255.0)) + extra + ratio2 * badgeByte[0] + ratio3 * bannerByte[0]; if (tempByte > byte.MaxValue) { //CompilationEvent("Colour overflow at X:"+j.ToString()+", Y:"+i.ToString()+". Colour will show as Magenta or other bright colour with some colour schemes.", true); mainImage[bytePos32bit] = byte.MaxValue; extra = extra + tempByte - byte.MaxValue; tempByte = mainImage[bytePos32bit + 2] + extra; if (tempByte > byte.MaxValue) { tempByte = byte.MaxValue; } mainImage[bytePos32bit + 2] = (byte)tempByte; tempByte = mainImage[bytePos32bit + 1] + extra; if (tempByte > byte.MaxValue) { tempByte = byte.MaxValue; } mainImage[bytePos32bit + 1] = (byte)tempByte; } else { mainImage[bytePos32bit] = (byte)tempByte; } } } return(mainImage); }