From 15a1a269b2e29d47e843631f241e2015ed6667f0 Mon Sep 17 00:00:00 2001 From: Moonling <52844176+VelouriasMoon@users.noreply.github.com> Date: Sat, 21 Aug 2021 22:55:08 -0300 Subject: [PATCH] Update CTPK.cs --- FE3D/CTPK.cs | 251 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 186 insertions(+), 65 deletions(-) diff --git a/FE3D/CTPK.cs b/FE3D/CTPK.cs index 9306fef..44a9027 100644 --- a/FE3D/CTPK.cs +++ b/FE3D/CTPK.cs @@ -1,28 +1,32 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using FE3D.GovanifY; +using SPICA.PICA.Converters; +using SPICA.PICA.Commands; +using SPICA.Formats.Common; +using System.IO; namespace FE3D.Image { public class CTPK { - public string Magic { get; set; } - public ushort Version { get; set; } - public ushort TexNum { get; set; } - public uint TexDataOffset { get; set; } - public uint TotalTexSize { get; set; } - public uint HashOffset { get; set; } - public uint FormatInfoOffset { get; set; } - public ulong HeaderPadding { get; set; } - public List TextureData { get; set; } - public List TextureSize { get; set; } - public List FileName { get; set; } - public List NameHash { get; set; } - public List FormatInfo { get; set; } - public List Textures { get; set; } + public string Magic { get; } = "CTPK"; //Always CTPK + public ushort Version { get; set; } = 1; //only seen 1 but other versions might exist + public ushort TexNum { get; set; } //number of textures stored + public uint TexDataOffset { get; set; } //pointer to the raw texture data + public uint TotalTexSize { get; set; } //total size of all texture data + public uint HashOffset { get; set; } //Pointer to the hash data location + public uint FormatInfoOffset { get; set; } //Pointer to the Format Info location + public List TextureData { get; set; } //Data for each texture split into 0x20 sized sections + public List TextureSize { get; set; } //Each image size stored as a uint + public List FileName { get; set; } //Each file name stored null terminate Shift-JIS encoded strings + public List NameHash { get; set; } //Name hash info for each image split into 0x8 sized sections + public List FormatInfo { get; set; } //Format info for each image split into 0x4 sized sections + public List Textures { get; set; } //Raw bytes for each image based on image encoding public void Read(BinaryStream binaryStream) { @@ -31,18 +35,24 @@ public void Read(BinaryStream binaryStream) FileName = new List(); NameHash = new List(); FormatInfo = new List(); - Textures = new List(); + Textures = new List(); //Read header data - Magic = Encoding.UTF8.GetString(binaryStream.ReadBytes(4)); + if (Magic != Encoding.UTF8.GetString(binaryStream.ReadBytes(4))) + { + throw new InvalidOperationException("Input File not valid CTPK file"); + } + Version = binaryStream.ReadUInt16(); TexNum = binaryStream.ReadUInt16(); TexDataOffset = binaryStream.ReadUInt32(); TotalTexSize = binaryStream.ReadUInt32(); HashOffset = binaryStream.ReadUInt32(); FormatInfoOffset = binaryStream.ReadUInt32(); - HeaderPadding = binaryStream.ReadUInt64(); + + //Skip header padding + binaryStream.Seek(8, System.IO.SeekOrigin.Current); //Read texture data for each texture for (int i = 0; i < TexNum; i++) @@ -86,72 +96,183 @@ public void Read(BinaryStream binaryStream) FormatInfo.Add(info); } + //Skip section padding binaryStream.Seek(TexDataOffset, System.IO.SeekOrigin.Begin); //Read Raw Texture bytes for (int i = 0; i < TexNum; i++) { - Textures.Add(binaryStream.ReadBytes((int)TextureSize[i])); + Bitmap texture = TextureConverter.DecodeBitmap(binaryStream.ReadBytes((int)TextureSize[i]), TextureData[i].Width, TextureData[i].Height, (PICATextureFormat)TextureData[i].Format); + Textures.Add(texture); } } - } + public void Write(BinaryStream binaryStream, CTPKFormat imageformat = CTPKFormat.Rgba8) + { + if (imageformat != CTPKFormat.Rgba8) + throw new NotImplementedException(); + var ShiftJIS = Encoding.GetEncoding(932); - public class CTPKData - { - public uint NameOffset { get; set; } //real location in ctpk - public uint ImageSize { get; set; } - public uint TexDataOffset { get; set; } //relative to texture data start - public uint Format { get; set; } - public ushort Width { get; set; } - public ushort Hight { get; set; } - public byte Mipmap { get; set; } - public byte Type { get; set; } - public ushort CubeMap { get; set; } - public uint BitmapSizeOffset { get; set; } - public uint TimeStamp { get; set; } + //Write data first pass + binaryStream.Write(Encoding.UTF8.GetBytes("CTPK")); + binaryStream.Write(Version); + binaryStream.Write(TexNum); + binaryStream.Write(TexDataOffset); + binaryStream.Write(TotalTexSize); + binaryStream.Write(HashOffset); + binaryStream.Write(FormatInfoOffset); + binaryStream.Write((ulong)0); - public void Read(BinaryStream binaryStream) - { - NameOffset = binaryStream.ReadUInt32(); - ImageSize = binaryStream.ReadUInt32(); - TexDataOffset = binaryStream.ReadUInt32(); - Format = binaryStream.ReadUInt32(); - Width = binaryStream.ReadUInt16(); - Hight = binaryStream.ReadUInt16(); - Mipmap = binaryStream.ReadByte(); - Type = binaryStream.ReadByte(); - CubeMap = binaryStream.ReadUInt16(); - BitmapSizeOffset = binaryStream.ReadUInt32(); - TimeStamp = binaryStream.ReadUInt32(); + foreach (CTPKData data in TextureData) + { + data.Write(binaryStream); + } + + foreach (uint size in TextureSize) + { + binaryStream.Write(size); + } + + foreach (string name in FileName) + { + binaryStream.Write(ShiftJIS.GetBytes(name)); + binaryStream.Write((byte)0); + } + + foreach (CTPKHash hash in NameHash) + { + hash.Write(binaryStream); + } + + foreach (CTPKInfo info in FormatInfo) + { + info.Write(binaryStream); + } + + while ((int)binaryStream.Tell() % 0x80 != 0) + { + binaryStream.Write((byte)0); + } + + foreach (Bitmap texture in Textures) + { + binaryStream.Write(TextureConverter.Encode(texture, (PICATextureFormat)imageformat)); + } + + //Rewrite Data properly + binaryStream.Seek(0, SeekOrigin.Begin); } - } - public class CTPKHash - { - public uint crc32Hash { get; set; } - public uint Index { get; set; } + public CTPK Makectpk(string[] infiles, CTPKFormat imageformat = CTPKFormat.Rgba8) + { + if (imageformat != CTPKFormat.Rgba8) + throw new NotImplementedException(); + CTPK ctpk = new CTPK(); + var ShiftJIS = Encoding.GetEncoding(932); - public void Read(BinaryStream binaryStream) + ctpk.TexNum = (ushort)infiles.Length; + + for (int i = 0; i < infiles.Length; i++) + { + Bitmap texture = (Bitmap)Bitmap.FromFile(infiles[i]); + + ctpk.TextureData.Add(new CTPKData() { Width = (ushort)texture.Width, Height = (ushort)texture.Height, Format = imageformat }); + ctpk.TextureSize.Add(0); + ctpk.NameHash.Add(new CTPKHash() { crc32Hash = CRC32Hash.Hash(ShiftJIS.GetBytes(Path.GetFileName(infiles[i]))), Index = (uint)i}); + ctpk.FormatInfo.Add(new CTPKInfo() { TextureFormat = imageformat, Compression = false, ETCEncoding = 8}); + + ctpk.FileName.Add(Path.GetFileName(infiles[i])); + ctpk.Textures.Add(texture); + } + + return ctpk; + } + + public class CTPKData { - crc32Hash = binaryStream.ReadUInt32(); - Index = binaryStream.ReadUInt32(); + public uint NameOffset { get; set; } //real location in ctpk + public uint ImageSize { get; set; } //size of image data after encoding + public uint TexDataOffset { get; set; } //relative to texture data start + public CTPKFormat Format { get; set; } //encoding format + public ushort Width { get; set; } + public ushort Height { get; set; } + public byte Mipmap { get; set; } //number of mipmap + public byte Type { get; set; } = 2; //only seen type 2 but there might be more + public ushort CubeMap { get; } = 0; //used for cubemaps + public uint BitmapSizeOffset { get; set; } //real location of size in Texture size list / 4 + public uint TimeStamp { get; set; } //timestamp when file was created, can be disable + + public void Read(BinaryStream binaryStream) + { + NameOffset = binaryStream.ReadUInt32(); + ImageSize = binaryStream.ReadUInt32(); + TexDataOffset = binaryStream.ReadUInt32(); + Format = (CTPKFormat)binaryStream.ReadUInt32(); + Width = binaryStream.ReadUInt16(); + Height = binaryStream.ReadUInt16(); + Mipmap = binaryStream.ReadByte(); + Type = binaryStream.ReadByte(); + //CubeMap = binaryStream.ReadUInt16(); + BitmapSizeOffset = binaryStream.ReadUInt32(); + TimeStamp = binaryStream.ReadUInt32(); + } + + public void Write(BinaryStream binaryStream) + { + binaryStream.Write(NameOffset); + binaryStream.Write(ImageSize); + binaryStream.Write(TexDataOffset); + binaryStream.Write((uint)Format); + binaryStream.Write(Width); + binaryStream.Write(Height); + binaryStream.Write(Mipmap); + binaryStream.Write(Type); + binaryStream.Write(CubeMap); + binaryStream.Write(BitmapSizeOffset); + binaryStream.Write(TimeStamp); + } } - } - public class CTPKInfo - { - public byte TextureFormat { get; set; } - public byte Unk { get; set; } - public byte Compression { get; set; } - public byte ETCEncoding { get; set; } + public class CTPKHash + { + public uint crc32Hash { get; set; } //crc32 Hash of the file name + public uint Index { get; set; } //Index of the texture hashed - public void Read(BinaryStream binaryStream) + public void Read(BinaryStream binaryStream) + { + crc32Hash = binaryStream.ReadUInt32(); + Index = binaryStream.ReadUInt32(); + } + + public void Write(BinaryStream binaryStream) + { + binaryStream.Write(crc32Hash); + binaryStream.Write(Index); + } + } + + public class CTPKInfo { - TextureFormat = binaryStream.ReadByte(); - Unk = binaryStream.ReadByte(); - Compression = binaryStream.ReadByte(); - ETCEncoding = binaryStream.ReadByte(); + public CTPKFormat TextureFormat { get; set; } //image encoding format + public byte Unk { get; set; } = 1; + public bool Compression { get; set; } + public byte ETCEncoding { get; set; } //type of ETC encoding + + public void Read(BinaryStream binaryStream) + { + TextureFormat = (CTPKFormat)binaryStream.ReadByte(); + Unk = binaryStream.ReadByte(); + Compression = Convert.ToBoolean(binaryStream.ReadByte()); + ETCEncoding = binaryStream.ReadByte(); + } + + public void Write(BinaryStream binaryStream) + { + binaryStream.Write((byte)TextureFormat); + binaryStream.Write(Unk); + binaryStream.Write(Convert.ToByte(Compression)); + binaryStream.Write(ETCEncoding); + } } }