diff --git a/VTFXUtil/MipMaker.cs b/VTFXUtil/MipMaker.cs new file mode 100644 index 0000000..c54a41f --- /dev/null +++ b/VTFXUtil/MipMaker.cs @@ -0,0 +1,231 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +/* + HOLY FUCK + ALL OF THIS CODE ALMOST WORKED ON THE FIRST TRY + HOW THE FUCK +*/ +namespace VTFXUtil +{ + class MipMaker + { + readonly private static int[] OneByteFormats = { 5, 7, 8 }; + readonly private static int[] TwoByteFormats = { 4, 6, 17, 18, 19, 21, 22 }; + readonly private static int[] ThreeByteFormats = {2, 3, 9, 10}; + readonly private static int[] FourByteFormats = { 0, 1, 11, 12, 16, 23, 26, 27 }; + readonly private static int[] DXTFormats = { 13, 14, 15 }; + public class MipMap + { + /// + /// Width of the current mip. + /// + public int Width; + /// + /// Height of the current mip. + /// + public int Height; + /// + /// Total number of bytes in the current mip. + /// + public int TotalBytes; + + public MipMap(int w, int h, int tb) + { + Width = w; + Height = h; + TotalBytes = tb; + } + } + /// + /// Generates information on every possible mip within the current image. + /// + /// + /// + /// + /// + public static MipMap[] GenMipInfo(int SrcWidth, int SrcHeight, int TexFormat) + { + double ChunkMult = 1; + bool ImageIsTall = false; + bool ImageIsWide = false; + if (SrcWidth > SrcHeight) + { + ImageIsWide = true; + } + else if (SrcHeight > SrcWidth) + { + ImageIsTall = true; + } + + List MipList = new List(); + + //begin chunknum estimation + + if (TwoByteFormats.Contains(TexFormat)) + { + ChunkMult = 2; + } + if (ThreeByteFormats.Contains(TexFormat)) + { + ChunkMult = 3; + } + else if (FourByteFormats.Contains(TexFormat)) + { + ChunkMult = 4; + } + else + { + if (DXTFormats.Contains(TexFormat)) + { + if (TexFormat == (int)VTFGeneral.TEXTURE_FORMATS.IMAGE_FORMAT_DXT1) + { + ChunkMult = 0.25; + } + else + { + //ChunkMult = 0.5; //ignore trying to change values for these, as they do nothing but fuck up mip calculations + } + } + } + + bool AttemptedPast1x1 = false; + double TempWidth = SrcWidth; + double TempHeight = SrcHeight; + while (!AttemptedPast1x1) + { + if (DXTFormats.Contains(TexFormat)) + { + double CurrentMipSize; + if (TexFormat == (int)VTFGeneral.TEXTURE_FORMATS.IMAGE_FORMAT_DXT1) + { + double HorChunkNum = TempWidth * ChunkMult; + if (TempWidth < 4) + { + HorChunkNum = 4 * ChunkMult; + } + double VerChunkNum = TempHeight * ChunkMult; + if (TempHeight < 4) + { + VerChunkNum = 4 * ChunkMult; + } + double TotalChunkNum = HorChunkNum * VerChunkNum; + CurrentMipSize = TotalChunkNum * 8; + } + else + { + CurrentMipSize = TempWidth * TempHeight; //for some strange reason, DXT3 and DXT5's byte size for a single image somehow manages to be just the width multiplied by the height + //thinking about that actually makes sense, in a dumb way + //as each 4x4 chunk is 16 bytes + //so if you think about it like a dumbass, you could get a weird analogy of "hurr each 4 byte is like 1 lines of pixels" + if(CurrentMipSize < 16) + { + CurrentMipSize = 16; //has to be added here, because code would crash otherwise + } + } + Console.WriteLine(TempWidth + "x" + TempHeight + " | Chunk size: " + CurrentMipSize); + MipList.Add(new MipMap((int)TempWidth, (int)TempHeight, (int)CurrentMipSize)); + } + else + { + double CurrentMipSizeNoMult = TempWidth * TempHeight; + double CurrentMipSize = CurrentMipSizeNoMult * ChunkMult; + MipList.Add(new MipMap((int)TempWidth, (int)TempHeight, (int)CurrentMipSize)); + } + + TempWidth *= .5; + TempHeight *= .5; + + if (ImageIsTall) + { + if(TempWidth < 1) + { + TempWidth = 1; + } + if(TempHeight < 1) + { + AttemptedPast1x1 = true; + } + } + else if (ImageIsWide) + { + if(TempHeight < 1) + { + TempHeight = 1; + } + if(TempWidth < 1) + { + AttemptedPast1x1 = true; + } + } + else + { + if(TempWidth < 1 && TempHeight < 1) + { + AttemptedPast1x1 = true; + } + } + } + + MipMap[] TheList = MipList.ToArray(); + Array.Reverse(TheList); // miplist must be reversed, as VTF is a cursed format that has the mips backwards + return TheList; + + } + /// + /// Checks to see if the current image is mipmapped. + /// + /// + /// + /// + /// + /// + public static bool IsMipped(int SrcWidth, int SrcHeight, int TexFormat, int RealTexSize) + { + double ChunkMult = 1; + double PixelCount = SrcWidth * SrcHeight; + double EstimatedSize; + if (TwoByteFormats.Contains(TexFormat)) + { + ChunkMult = 2; + } + if (ThreeByteFormats.Contains(TexFormat)) + { + ChunkMult = 3; + } + else if (FourByteFormats.Contains(TexFormat)) + { + ChunkMult = 4; + } + else + { + if (DXTFormats.Contains(TexFormat)) + { + if(TexFormat == (int)VTFGeneral.TEXTURE_FORMATS.IMAGE_FORMAT_DXT1) + { + ChunkMult = 0.5; // for some reason, setting this to 0.5 works for dxt1, but doing it in the actual mip detection code causes it to break + } + else + { + //ChunkMult = 0.5; // disregard, screws up mipinfo for non-mipped textures + } + } + } + EstimatedSize = PixelCount * ChunkMult; + if(RealTexSize > (int)EstimatedSize) + { + Console.WriteLine("We believe we can mipmap this. Real size: " + RealTexSize + " bytes."); + Console.WriteLine("Estimated size: " + EstimatedSize + " bytes."); + Console.WriteLine(RealTexSize + " > " + EstimatedSize); + return true; + } + else + { + return false; + } + } + } +} diff --git a/VTFXUtil/Program.cs b/VTFXUtil/Program.cs new file mode 100644 index 0000000..dd3200a --- /dev/null +++ b/VTFXUtil/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace VTFXUtil +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Form1()); + } + } +} diff --git a/VTFXUtil/VTFGeneral.cs b/VTFXUtil/VTFGeneral.cs new file mode 100644 index 0000000..bce81aa --- /dev/null +++ b/VTFXUtil/VTFGeneral.cs @@ -0,0 +1,243 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VTFXUtil +{ + public class VTFGeneral + { + public struct VTFResource + { + public byte[] ResourceType; + public byte ResourceFlag; + public int Offset; + + public VTFResource(byte[] Kind, byte Flag, int Off) + { + ResourceType = Kind; + ResourceFlag = Flag; + Offset = Off; + } + } + + public static readonly string[] TexFmtStrArr = { "RGBA8888", "ABGR8888", "RGB888", "BGR888", "RGB565", "I8", "IA88", "P8", "A8", "RGB888 Bluescreen", "BGR888 Bluescreen", "ARGB8888", "BGRA8888", "DXT1", "DXT3", "DXT5", "BGRX8888", "BGR565", "BGRX5551", "BGRA4444", "DXT1 One Bit Alpha", "BGRA5551", "UV88", "UVWQ8888", "RGBA16161616F", "RGBA16161616", "UVLX8888", "R32F", "RGB323232F", "RGBA32323232F", "NVIDIA DST16", "NVIDIA DST24", "NVIDIA INTZ", "NVIDIA RAWZ", "ATI DST16", "ATI DST24", "NVIDIA NULL", "ATI1N", "ATI2N", "Xbox 360 DST16", "Xbox 360 DST24", "Xbox 360 DST24F", "Linear BGRX8888", "Linear RGBA8888", "Linear ABGR8888", "Linear ARGB8888", "Linear BGRA8888", "Linear RGB888", "Linear BGR888", "Linear BGRX5551", "Linear I8", "Linear RGBA16161616", "LE BGRX8888", "LE BGRA8888" }; + public static readonly Dictionary VTFFlags = new Dictionary() { { 0x1, "Point Sample" }, { 0x2, "Trilinear" }, { 0x4, "Clamp S" }, { 0x8, "Clamp T" }, { 0x10, "Anisotropic" }, { 0x20, "Hint DXT5" }, { 0x40, "PWL Corrected" }, { 0x80, "Normal Map" }, { 0x100, "No Mipmap" }, { 0x200, "No Level of Detail" }, { 0x400, "All Mipmaps" }, { 0x800, "All Procedural" }, { 0x1000, "One Bit Alpha" }, { 0x2000, "Eight Bit Alpha" }, { 0x4000, "Environment Map" }, { 0x8000, "Rendertarget" }, { 0x10000, "Depth Rendertarget" }, { 0x20000, "No Debug Override" }, { 0x40000, "Single Copy" }, { 0x80000, "Pre sRGB" }, { 0x100000, "Unused" }, { 0x200000, "Unused" }, { 0x400000, "Unused" }, { 0x800000, "No Depth Buffer" }, { 0x1000000, "Unused" }, { 0x2000000, "Clamp U" }, { 0x4000000, "Vertex Texture" }, { 0x8000000, "SS Bump" }, { 0x10000000, "Unused" }, { 0x20000000, "Border" }, { 0x40000000, "Unused" }, { 0x80000000, "Unused" } }; + // enum list below is from the Source SDK's imageformat.h "/sp/src/public/bitmap/imageformat.h" https://github.com/ValveSoftware/source-sdk-2013 + public enum TEXTURE_FORMATS + { + IMAGE_FORMAT_RGBA8888, + IMAGE_FORMAT_ABGR8888, + IMAGE_FORMAT_RGB888, + IMAGE_FORMAT_BGR888, + IMAGE_FORMAT_RGB565, + IMAGE_FORMAT_I8, + IMAGE_FORMAT_IA88, + IMAGE_FORMAT_P8, + IMAGE_FORMAT_A8, + IMAGE_FORMAT_RGB888_BLUESCREEN, + IMAGE_FORMAT_BGR888_BLUESCREEN, + IMAGE_FORMAT_ARGB8888, + IMAGE_FORMAT_BGRA8888, + IMAGE_FORMAT_DXT1, + IMAGE_FORMAT_DXT3, + IMAGE_FORMAT_DXT5, + IMAGE_FORMAT_BGRX8888, + IMAGE_FORMAT_BGR565, + IMAGE_FORMAT_BGRX5551, + IMAGE_FORMAT_BGRA4444, + IMAGE_FORMAT_DXT1_ONEBITALPHA, + IMAGE_FORMAT_BGRA5551, + IMAGE_FORMAT_UV88, + IMAGE_FORMAT_UVWQ8888, + IMAGE_FORMAT_RGBA16161616F, + IMAGE_FORMAT_RGBA16161616, + IMAGE_FORMAT_UVLX8888, + IMAGE_FORMAT_R32F, + IMAGE_FORMAT_RGB323232F, + IMAGE_FORMAT_RGBA32323232F, + IMAGE_FORMAT_NV_DST16, + IMAGE_FORMAT_NV_DST24, + IMAGE_FORMAT_NV_INTZ, + IMAGE_FORMAT_NV_RAWZ, + IMAGE_FORMAT_ATI_DST16, + IMAGE_FORMAT_ATI_DST24, + IMAGE_FORMAT_NV_NULL, + IMAGE_FORMAT_ATI1N, + IMAGE_FORMAT_ATI2N, + IMAGE_FORMAT_X360_DST16, + IMAGE_FORMAT_X360_DST24, + IMAGE_FORMAT_X360_DST24F, + IMAGE_FORMAT_LINEAR_BGRX8888, + IMAGE_FORMAT_LINEAR_RGBA8888, + IMAGE_FORMAT_LINEAR_ABGR8888, + IMAGE_FORMAT_LINEAR_ARGB8888, + IMAGE_FORMAT_LINEAR_BGRA8888, + IMAGE_FORMAT_LINEAR_RGB888, + IMAGE_FORMAT_LINEAR_BGR888, + IMAGE_FORMAT_LINEAR_BGRX5551, + IMAGE_FORMAT_LINEAR_I8, + IMAGE_FORMAT_LINEAR_RGBA16161616, + IMAGE_FORMAT_LE_BGRX8888, + IMAGE_FORMAT_LE_BGRA8888 + } + private static readonly byte[] LRImgBytes = { 0x1, 0x0, 0x0 }; + private static readonly byte[] HRImgBytes = { 0x30, 0x0, 0x0 }; + private static readonly byte[] PSDatBytes = { 0x10, 0x0, 0x0 }; + private static readonly byte[] CRCBytes = { 0x43, 0x52, 0x43 }; + private static readonly byte[] LODBytes = { 0x4c, 0x4f, 0x44 }; + private static readonly byte[] ExtVTFFlagsBytes = { 0x54, 0x53, 0x4f }; + private static readonly byte[] ArbKVFlags = { 0x4b, 0x56, 0x44 }; + public static readonly Dictionary ResourceTypes = new Dictionary() { { Encoding.UTF8.GetString(LRImgBytes), "Low Resolution Image Data" }, { Encoding.UTF8.GetString(HRImgBytes), "High Resolution Image Data" }, { Encoding.UTF8.GetString(PSDatBytes), "Particle Sheet Data" }, { Encoding.UTF8.GetString(CRCBytes), "File CRC" }, { Encoding.UTF8.GetString(LODBytes), "Texture LOD Information" }, { Encoding.UTF8.GetString(ExtVTFFlagsBytes), "Extended VTF Flags" }, { Encoding.UTF8.GetString(ArbKVFlags), "Arbitrary KeyValue Flags" } }; + + public static string GetResourceType(byte[] ByteArray) + { + return ResourceTypes[Encoding.UTF8.GetString(ByteArray)]; + } + + /// + /// Determines if the specified format has an alpha channel. + /// + /// + /// + public static bool HasAlpha(int Format) + { + string[] ValidAlphaFormats = { "RGBA8888", "ABGR8888", null, null, null, null, "IA88", null, "A8", null, null, "ARGB8888", "BGRA8888", null, "DXT3", "DXT5", "BGRX8888", null, "BGRX5551", "BGRA4444", "DXT1 One Bit Alpha", "BGRA5551", null, null, "RGBA16161616F", "RGBA16161616", null, null, null, "RGBA32323232F", null, null, null, null, null, null, null, null, null, null, null, null, "Linear BGRX8888", "Linear RGBA8888", "Linear ABGR8888", "Linear ARGB8888", "Linear BGRA8888", null, null, "Linear BGRX5551", null, "Linear RGBA16161616", "LE BGRX8888", "LE BGRA8888" }; + if (ValidAlphaFormats[Format] != null) + { + return true; + } + return false; + } + public class VTFFormatProps + { + public int Format; + public bool IsInCSIL; + public bool IsInTIN; + public int bpp; + public bool IsCompressed; + public bool IsCurrentlySupported; + public int ColorByteAmount; + public VTFFormatProps() + { + Format = -1; + IsInCSIL = false; + IsInTIN = false; + bpp = 0; + IsCompressed = false; + IsCurrentlySupported = false; + ColorByteAmount = 0; + } + public VTFFormatProps(int format, bool SupportsCSIL, bool SupportsTIN, int BPP, bool Compressed, bool CurrentlySupported, int ColBytes) + { + Format = format; + IsInCSIL = SupportsCSIL; + IsInTIN = SupportsTIN; + bpp = BPP; + IsCompressed = Compressed; + IsCurrentlySupported = CurrentlySupported; + ColorByteAmount = ColBytes; + } + } + public static readonly VTFFormatProps[] FormatTable = + { + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_RGBA8888, true, true, 32, false, true, 4), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_ABGR8888, true, true, 32, false, true, 4), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_RGB888, true, true, 24, false, true, 3), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_BGR888, true, true, 24, false, true, 3), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_RGB565, true, true, 16, false, true, 2), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_I8, false, false, 8, false, true, 1), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_IA88, false, false, 16, false, true, 2), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_P8, false, false, 8, false, true, 1), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_A8, true, false, 8, false, true, 1), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_RGB888_BLUESCREEN, true, false, 24, false, true, 3), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_BGR888_BLUESCREEN, true, false, 24, false, true, 3), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_ARGB8888, true, false, 32, false, true, 4), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_BGRA8888, true, false, 32, false, true, 4), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_DXT1, true, false, 4, true, true, 4), //define color bits as negative for compressed textures, so program can ignore it, dxt1 has a fit so it needs to be defined + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_DXT3, true, false, 8, true, true, -1), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_DXT5, true, false, 8, true, true, -1), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_BGRX8888, true, false, 32, false, true, 4), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_BGR565, true, true, 16, false, true, 3), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_BGRX5551, true, true, 16, false, true, 4), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_BGRA4444, true, true, 16, false, true, 4), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_DXT1_ONEBITALPHA, false, true, 4, true, true, -1), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_BGRA5551, false, true, 16, false, true, 4), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_UV88, true, false, 16, false, true, 2), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_UVWQ8888, false, false, 32, false, false, 4), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_RGBA16161616F, true, false, 64, false, true, 8), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_RGBA16161616, true, false, 64, false, true, 8), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_UVLX8888, false, false, 32, false, false, 4), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_R32F, false, false, 32, false, false, 4), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_RGB323232F, true, false, 96, false, true, 12), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_RGBA32323232F, true, false, 128, false, true, 16), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_NV_DST16, false, false, 16, false, false, 2), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_NV_DST24, false, false, 24, false, false, 3), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_NV_INTZ, false, false, 32, false, false, 4), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_NV_RAWZ, false, false, 32, false, false, 4), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_ATI_DST16, false, false, 16, false, false, 2), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_ATI_DST24, false, false, 24, false, false, 3), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_NV_NULL, false, false, 32, false, false, 4), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_ATI1N, true, false, 4, true, true, -1), + new VTFFormatProps((int) TEXTURE_FORMATS.IMAGE_FORMAT_ATI2N, true, false, 8, true, true, -1), + }; + public enum TEXTURE_FORMATS_INTEROP + { + IMAGE_FORMAT_RGBA8888 = CSharpImageLibrary.ImageEngineFormat.DDS_ARGB_8, //move rgb values down, alpha to end + IMAGE_FORMAT_ABGR8888 = CSharpImageLibrary.ImageEngineFormat.DDS_ABGR_8, + IMAGE_FORMAT_RGB888 = CSharpImageLibrary.ImageEngineFormat.DDS_RGB_8, + IMAGE_FORMAT_BGR888 = CSharpImageLibrary.ImageEngineFormat.DDS_RGB_8, //reverse color order + IMAGE_FORMAT_RGB565 = CSharpImageLibrary.ImageEngineFormat.DDS_R5G6B5, + IMAGE_FORMAT_I8 = CSharpImageLibrary.ImageEngineFormat.Unknown, //will reparse unknowns later in code + IMAGE_FORMAT_IA88 = CSharpImageLibrary.ImageEngineFormat.Unknown, + IMAGE_FORMAT_P8 = CSharpImageLibrary.ImageEngineFormat.Unknown, + IMAGE_FORMAT_A8 = CSharpImageLibrary.ImageEngineFormat.DDS_A8, + IMAGE_FORMAT_RGB888_BLUESCREEN = CSharpImageLibrary.ImageEngineFormat.DDS_RGB_8, + IMAGE_FORMAT_BGR888_BLUESCREEN = CSharpImageLibrary.ImageEngineFormat.DDS_RGB_8, //reverse these too!! + IMAGE_FORMAT_ARGB8888 = CSharpImageLibrary.ImageEngineFormat.DDS_ARGB_8, + IMAGE_FORMAT_BGRA8888 = CSharpImageLibrary.ImageEngineFormat.DDS_ARGB_8, // reverse!! + IMAGE_FORMAT_DXT1 = CSharpImageLibrary.ImageEngineFormat.DDS_DXT1, + IMAGE_FORMAT_DXT3 = CSharpImageLibrary.ImageEngineFormat.DDS_DXT3, + IMAGE_FORMAT_DXT5 = CSharpImageLibrary.ImageEngineFormat.DDS_DXT5, + IMAGE_FORMAT_BGRX8888 = CSharpImageLibrary.ImageEngineFormat.DDS_ARGB_8, //reverse!! + IMAGE_FORMAT_BGR565 = CSharpImageLibrary.ImageEngineFormat.DDS_R5G6B5, //reverse!! + IMAGE_FORMAT_BGRX5551 = CSharpImageLibrary.ImageEngineFormat.DDS_CUSTOM, //will need to possibly redefine!!! + IMAGE_FORMAT_BGRA4444 = CSharpImageLibrary.ImageEngineFormat.DDS_ARGB_4, //reverse!! + IMAGE_FORMAT_DXT1_ONEBITALPHA = TeximpNet.Compression.CompressionFormat.DXT1a, //no idea if CSharpImageLibrary has this + IMAGE_FORMAT_BGRA5551 = TeximpNet.Compression.CompressionFormat.BGRA, + IMAGE_FORMAT_UV88 = CSharpImageLibrary.ImageEngineFormat.DDS_V8U8, //reverse these!! + IMAGE_FORMAT_UVWQ8888, + IMAGE_FORMAT_RGBA16161616F = CSharpImageLibrary.ImageEngineFormat.DDS_CUSTOM, //possible define + IMAGE_FORMAT_RGBA16161616 = CSharpImageLibrary.ImageEngineFormat.DDS_CUSTOM, + IMAGE_FORMAT_UVLX8888, + IMAGE_FORMAT_R32F, + IMAGE_FORMAT_RGB323232F = CSharpImageLibrary.ImageEngineFormat.DDS_CUSTOM, + IMAGE_FORMAT_RGBA32323232F = CSharpImageLibrary.ImageEngineFormat.DDS_CUSTOM, + IMAGE_FORMAT_NV_DST16, + IMAGE_FORMAT_NV_DST24, + IMAGE_FORMAT_NV_INTZ, + IMAGE_FORMAT_NV_RAWZ, + IMAGE_FORMAT_ATI_DST16, + IMAGE_FORMAT_ATI_DST24, + IMAGE_FORMAT_NV_NULL, + IMAGE_FORMAT_ATI1N = CSharpImageLibrary.ImageEngineFormat.DDS_ATI1, + IMAGE_FORMAT_ATI2N = CSharpImageLibrary.ImageEngineFormat.DDS_ATI2_3Dc, + IMAGE_FORMAT_X360_DST16, + IMAGE_FORMAT_X360_DST24, + IMAGE_FORMAT_X360_DST24F, + IMAGE_FORMAT_LINEAR_BGRX8888, + IMAGE_FORMAT_LINEAR_RGBA8888, + IMAGE_FORMAT_LINEAR_ABGR8888, + IMAGE_FORMAT_LINEAR_ARGB8888, + IMAGE_FORMAT_LINEAR_BGRA8888, + IMAGE_FORMAT_LINEAR_RGB888, + IMAGE_FORMAT_LINEAR_BGR888, + IMAGE_FORMAT_LINEAR_BGRX5551, + IMAGE_FORMAT_LINEAR_I8, + IMAGE_FORMAT_LINEAR_RGBA16161616, + IMAGE_FORMAT_LE_BGRX8888, + IMAGE_FORMAT_LE_BGRA8888 + } + } +} diff --git a/VTFXUtil/VTFXHandler.cs b/VTFXUtil/VTFXHandler.cs new file mode 100644 index 0000000..845fdc9 --- /dev/null +++ b/VTFXUtil/VTFXHandler.cs @@ -0,0 +1,309 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace VTFXUtil +{ + class VTFXHandler + { + readonly private static string FileMagicVTFX = "VTFX"; + + /// + /// Represents a VTFX file. + /// + public class VTFXFile + { + public string FileMagic; //VTFX + public byte[] VersionNum = new byte[8]; // major version is 0x00000360 for Xbox 360 files, 0x00000333 for PS3 files, minor version always 0x00000008 + public int HeaderSize; //usually 0x44 or 68 in base 10 + public byte[] VTFFlags = new byte[4]; //same as standard VTF, endianswapped + public int ImageWidth; //ditto + public int ImageHeight; //ditto + public int BitDepth; // this is incorrectly named, this is actually for how much depth a volume texture has + public int FrameCount; // oh come on its obvious + public int PreloadSize; //the size of the preload chunk that exists in __preload_section.pre for this texture + public int MipSkipCount; // basically used for forced texture quality reduction + public int ResourceCount; //same as normal VTF, also needs endianswapping + public byte[] ReflectivityVec = new byte[12]; //ditto + public float BumpScale; //ditto + public byte[] TextureFormat = new byte[4]; //ditto + public byte[] LowResSamplingAmt = new byte[4]; // also misnamed by me, turns out this is for doing light sampling with VRAD + public int CompressedSize; //exact size of the texture data itself once compressed, PS3 files will have this section zeroed out, due to textures being compressed outside of the file + public Dictionary Resources; //contains resource data + public byte[] FileData; //data contained in the file + } + + /// + /// Returns an imported VTFX file from a path. + /// + /// + /// + public static VTFXFile ImportVTFX(string PathToFile) // why did i make this import from a fucking file path + { + if (PathToFile.Equals("")) + { + return null; + } + System.IO.FileStream ImportFile = new System.IO.FileStream(PathToFile, System.IO.FileMode.Open, System.IO.FileAccess.Read); + byte[] TempAll = System.IO.File.ReadAllBytes(PathToFile); + byte[] HeaderLenArr = new byte[4]; + Array.Copy(TempAll, 0xc, HeaderLenArr, 0, 4); + Array.Reverse(HeaderLenArr); + int BufferLen = System.BitConverter.ToInt32(HeaderLenArr, 0); + byte[] Header = new byte[BufferLen]; + using (ImportFile) + { + ImportFile.Read(Header, 0, BufferLen); + } + string FileMagic = System.Text.Encoding.UTF8.GetString(Header).Substring(0, 4); + Console.WriteLine(FileMagic); + if (FileMagic.Equals(FileMagicVTFX)) + { + VTFXFile InMem = new VTFXFile(); + InMem.FileMagic = FileMagic; + Array.Copy(Header, 4, InMem.VersionNum, 0, 8); + byte[] TempLen = new byte[4]; + Array.Copy(Header, 12, TempLen, 0, 4); + Array.Reverse(TempLen); // yay endianswapping + InMem.HeaderSize = System.BitConverter.ToInt32(TempLen, 0); + byte[] TempVTFFlags = new byte[4]; + Array.Copy(Header, 16, TempVTFFlags, 0, 4); + Array.Reverse(TempVTFFlags); + InMem.VTFFlags = TempVTFFlags; + byte[] TempWidth = new byte[2]; + Array.Copy(Header, 20, TempWidth, 0, 2); + Array.Reverse(TempWidth); + InMem.ImageWidth = System.BitConverter.ToInt16(TempWidth, 0); + byte[] TempHeight = new byte[2]; + Array.Copy(Header, 22, TempHeight, 0, 2); + Array.Reverse(TempHeight); + InMem.ImageHeight = System.BitConverter.ToInt16(TempHeight, 0); + byte[] TempDepth = new byte[2]; + Array.Copy(Header, 24, TempDepth, 0, 2); + Array.Reverse(TempDepth); + InMem.BitDepth = System.BitConverter.ToInt16(TempDepth, 0); + byte[] TempFC = new byte[2]; + Array.Copy(Header, 25, TempFC, 0, 2); + InMem.FrameCount = System.BitConverter.ToInt16(TempFC, 0); + byte[] TempPreload = new byte[2]; + Array.Copy(Header, 28, TempPreload, 0, 2); + Array.Reverse(TempPreload); + InMem.PreloadSize = System.BitConverter.ToInt16(TempPreload, 0); + byte TempMip = Header[30]; + InMem.MipSkipCount = Convert.ToSByte(TempMip); + InMem.ResourceCount = Convert.ToSByte(Header[31]); + byte[] TempVec = new byte[12]; + byte[] Vec1 = new byte[4]; + Array.Copy(Header, 32, Vec1, 0, 4); + byte[] Vec2 = new byte[4]; + Array.Copy(Header, 36, Vec2, 0, 4); + byte[] Vec3 = new byte[4]; + Array.Copy(Header, 40, Vec3, 0, 4); + Array.Reverse(Vec1); + Array.Reverse(Vec2); + Array.Reverse(Vec3); + Array.Copy(Vec1, 0, TempVec, 0, 4); + Array.Copy(Vec2, 0, TempVec, 4, 4); + Array.Copy(Vec3, 0, TempVec, 8, 4); + InMem.ReflectivityVec = TempVec; + byte[] TempBump = new byte[4]; + Array.Copy(Header, 44, TempBump, 0, 4); + Array.Reverse(TempBump); + InMem.BumpScale = System.BitConverter.ToSingle(TempBump, 0); + Array.Copy(Header, 48, InMem.TextureFormat, 0, 4); + Array.Reverse(InMem.TextureFormat); + Array.Copy(Header, 52, InMem.LowResSamplingAmt, 0, 4); + byte[] TempCompSize = new byte[4]; + Array.Copy(Header, 56, TempCompSize, 0, 4); + Array.Reverse(TempCompSize); + InMem.CompressedSize = System.BitConverter.ToInt32(TempCompSize, 0); + InMem.Resources = ParseResourceInfo(InMem, PathToFile); + InMem.FileData = new byte[TempAll.Length - InMem.HeaderSize]; + Array.Copy(TempAll, InMem.HeaderSize, InMem.FileData, 0, TempAll.Length - InMem.HeaderSize); + GC.Collect(); + return InMem; + } + else + { + MessageBox.Show("Specified file was not in the VTFX format.\nMagic expected was \"VTFX\", magic returned was {0}", FileMagic); + return new VTFXFile(); + } + } + + /// + /// Returns a VTFX file from the given MemoryStream. + /// + /// + /// + public static VTFXFile ImportVTFX(System.IO.MemoryStream ImportFile) // this is better, but the way i handled this is fucking terrible + { + byte[] TempAll = ImportFile.ToArray(); + byte[] HeaderLenArr = new byte[4]; + Array.Copy(TempAll, 0xc, HeaderLenArr, 0, 4); + Array.Reverse(HeaderLenArr); + int BufferLen = System.BitConverter.ToInt32(HeaderLenArr, 0); + byte[] Header = new byte[BufferLen]; + using (ImportFile) + { + ImportFile.Read(Header, 0, BufferLen); + } + string FileMagic = System.Text.Encoding.UTF8.GetString(Header).Substring(0, 4); + Console.WriteLine(FileMagic); + if (FileMagic.Equals(FileMagicVTFX)) + { + VTFXFile InMem = new VTFXFile(); + InMem.FileMagic = FileMagic; + Array.Copy(Header, 4, InMem.VersionNum, 0, 8); + byte[] TempLen = new byte[4]; + Array.Copy(Header, 12, TempLen, 0, 4); + Array.Reverse(TempLen); // yay endianswapping + InMem.HeaderSize = System.BitConverter.ToInt32(TempLen, 0); + byte[] TempVTFFlags = new byte[4]; + Array.Copy(Header, 16, TempVTFFlags, 0, 4); + Array.Reverse(TempVTFFlags); + InMem.VTFFlags = TempVTFFlags; + byte[] TempWidth = new byte[2]; + Array.Copy(Header, 20, TempWidth, 0, 2); + Array.Reverse(TempWidth); + InMem.ImageWidth = System.BitConverter.ToInt16(TempWidth, 0); + byte[] TempHeight = new byte[2]; + Array.Copy(Header, 22, TempHeight, 0, 2); + Array.Reverse(TempHeight); + InMem.ImageHeight = System.BitConverter.ToInt16(TempHeight, 0); + byte[] TempDepth = new byte[2]; + Array.Copy(Header, 24, TempDepth, 0, 2); + Array.Reverse(TempDepth); + InMem.BitDepth = System.BitConverter.ToInt16(TempDepth, 0); + byte[] TempFC = new byte[2]; + Array.Copy(Header, 25, TempFC, 0, 2); + InMem.FrameCount = System.BitConverter.ToInt16(TempFC, 0); + byte[] TempPreload = new byte[2]; + Array.Copy(Header, 28, TempPreload, 0, 2); + Array.Reverse(TempPreload); + InMem.PreloadSize = System.BitConverter.ToInt16(TempPreload, 0); + byte TempMip = Header[30]; + InMem.MipSkipCount = Convert.ToSByte(TempMip); + InMem.ResourceCount = Convert.ToSByte(Header[31]); + byte[] TempVec = new byte[12]; + byte[] Vec1 = new byte[4]; + Array.Copy(Header, 32, Vec1, 0, 4); + byte[] Vec2 = new byte[4]; + Array.Copy(Header, 36, Vec2, 0, 4); + byte[] Vec3 = new byte[4]; + Array.Copy(Header, 40, Vec3, 0, 4); + Array.Reverse(Vec1); + Array.Reverse(Vec2); + Array.Reverse(Vec3); + Array.Copy(Vec1, 0, TempVec, 0, 4); + Array.Copy(Vec2, 0, TempVec, 4, 4); + Array.Copy(Vec3, 0, TempVec, 8, 4); + InMem.ReflectivityVec = TempVec; + byte[] TempBump = new byte[4]; + Array.Copy(Header, 44, TempBump, 0, 4); + Array.Reverse(TempBump); + InMem.BumpScale = System.BitConverter.ToSingle(TempBump, 0); + Array.Copy(Header, 48, InMem.TextureFormat, 0, 4); + Array.Reverse(InMem.TextureFormat); + Array.Copy(Header, 52, InMem.LowResSamplingAmt, 0, 4); + byte[] TempCompSize = new byte[4]; + Array.Copy(Header, 56, TempCompSize, 0, 4); + Array.Reverse(TempCompSize); + InMem.CompressedSize = System.BitConverter.ToInt32(TempCompSize, 0); + InMem.Resources = ParseResourceInfo(InMem, ImportFile); + InMem.FileData = new byte[TempAll.Length - InMem.HeaderSize]; + Array.Copy(TempAll, InMem.HeaderSize, InMem.FileData, 0, TempAll.Length - InMem.HeaderSize); + GC.Collect(); + return InMem; + } + else + { + MessageBox.Show("Specified file was not in the VTFX format.\nMagic expected was \"VTFX\", magic returned was {0}", FileMagic); + return new VTFXFile(); + } + } + + public static VTFXFile ConvertVTFToVTFX(Formats.VTFFile Input, byte[] TextureData, byte[] Platform, byte[] ColorSamplingBytes) + { + //byte[] FourBytePadding = { 0x00, 0x00, 0x00, 0x00 }; + Dictionary VTFXResources = new Dictionary(); + foreach(var Resource in Input.Resources) + { + VTFXResources.Add(Resource.ResourceType, Resource.Offset); + } + VTFXFile Amalgamation = new VTFXFile + { + BitDepth = Input.ImageDepth, + BumpScale = Input.BumpScale, + // do some stuff with an if statement here for PS3 files, they don't compress anything + FileData = TextureData, + FileMagic = "VTFX", // i shouldn't have made this property for the VTFXFile object + FrameCount = Input.Frames, + HeaderSize = Input.HeaderLength, + ImageHeight = Input.Height, + ImageWidth = Input.Width, + LowResSamplingAmt = ColorSamplingBytes, // this is apparently for color sampling, so i need to go and do some code refactors + MipSkipCount = 0, // basically just a built in texture quality setting, and i had no idea that something like this was even a thing + PreloadSize = 0, // make this zero, as i don't understand the format of __preload_section.pre, i hope to fuck this works + ReflectivityVec = Input.ReflectivityVector, + ResourceCount = Input.ResourceCount, + Resources = VTFXResources, + TextureFormat = Input.Format, + VTFFlags = Input.Flags, + VersionNum = Platform + }; + return Amalgamation; + } + + public static Dictionary ParseResourceInfo(VTFXFile File, string PathToFile) + { + int ResourceBuffer = 8; + System.IO.FileStream ImportFile = new System.IO.FileStream(PathToFile, System.IO.FileMode.Open, System.IO.FileAccess.Read); + byte[] Temp = new byte[File.HeaderSize]; + byte[] Header = new byte[File.HeaderSize - 0x3c]; + using (ImportFile) + { + ImportFile.Read(Temp, 0, File.HeaderSize); + } + Array.Copy(Temp, 0x3c, Header, 0, File.HeaderSize - 0x3c); + Dictionary Resources = new Dictionary(); + for(int i = 0; i < File.ResourceCount; i++) + { + byte[] ResType = new byte[3]; + byte[] OffsetArray = new byte[4]; + Array.Copy(Header, ResourceBuffer * i, ResType, 0, 3); + Array.Copy(Header, ResourceBuffer * i + 4, OffsetArray, 0, 4); + Array.Reverse(OffsetArray); + Console.WriteLine(System.BitConverter.ToInt32(OffsetArray, 0)); + Resources.Add(ResType, (int)System.BitConverter.ToInt32(OffsetArray, 0)); + } + return Resources; + } + + public static Dictionary ParseResourceInfo(VTFXFile File, System.IO.MemoryStream ImportFile) + { + int ResourceBuffer = 8; + byte[] Temp = new byte[File.HeaderSize]; + byte[] Header = new byte[File.HeaderSize - 0x3c]; + using (ImportFile) + { + ImportFile.Read(Temp, 0, File.HeaderSize); + } + Array.Copy(Temp, 0x3c, Header, 0, File.HeaderSize - 0x3c); + Dictionary Resources = new Dictionary(); + for (int i = 0; i < File.ResourceCount; i++) + { + byte[] ResType = new byte[3]; + byte[] OffsetArray = new byte[4]; + Array.Copy(Header, ResourceBuffer * i, ResType, 0, 3); + Array.Copy(Header, ResourceBuffer * i + 4, OffsetArray, 0, 4); + Array.Reverse(OffsetArray); + Console.WriteLine(System.BitConverter.ToInt32(OffsetArray, 0)); + Resources.Add(ResType, (int)System.BitConverter.ToInt32(OffsetArray, 0)); + } + return Resources; + } + } +} diff --git a/VTFXUtil/packages.config b/VTFXUtil/packages.config new file mode 100644 index 0000000..921725a --- /dev/null +++ b/VTFXUtil/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file