public DATAField DATA; // Data public override bool CreateField(UnityBinaryReader r, GameFormatId format, string type, int dataSize) { if (format == GameFormatId.TES3) { switch (type) { case "NAME": EDID = r.ReadSTRV(dataSize); return(true); case "FNAM": FULL = r.ReadSTRV(dataSize); return(true); case "CLDT": r.SkipBytes(dataSize); return(true); case "DESC": DESC = r.ReadSTRV(dataSize); return(true); default: return(false); } } switch (type) { case "EDID": EDID = r.ReadSTRV(dataSize); return(true); case "FULL": FULL = r.ReadSTRV(dataSize); return(true); case "DESC": DESC = r.ReadSTRV(dataSize); return(true); case "ICON": ICON = r.ReadSTRV(dataSize); return(true); case "DATA": DATA = new DATAField(r, dataSize); return(true); default: return(false); } }
public FMIDField <SOUNRecord> QNAM; // Close sound public override bool CreateField(UnityBinaryReader r, GameFormatId format, string type, int dataSize) { switch (type) { case "EDID": case "NAME": EDID = r.ReadSTRV(dataSize); return(true); case "MODL": MODL = new MODLGroup(r, dataSize); return(true); case "MODB": MODL.MODBField(r, dataSize); return(true); case "MODT": MODL.MODTField(r, dataSize); return(true); case "FULL": case "FNAM": FULL = r.ReadSTRV(dataSize); return(true); case "DATA": case "CNDT": DATA = new DATAField(r, dataSize, format); return(true); case "FLAG": DATA.FLAGField(r, dataSize); return(true); case "CNTO": case "NPCO": CNTOs.Add(new CNTOField(r, dataSize, format)); return(true); case "SCRI": SCRI = new FMIDField <SCPTRecord>(r, dataSize); return(true); case "SNAM": SNAM = new FMIDField <SOUNRecord>(r, dataSize); return(true); case "QNAM": QNAM = new FMIDField <SOUNRecord>(r, dataSize); return(true); default: return(false); } }
/// <summary> /// Loads a DDS texture from an input stream. /// </summary> public static Texture2DInfo LoadDDSTexture(Stream inputStream, bool flipVertically = false) { using (var reader = new UnityBinaryReader(inputStream)) { // Check the magic string. var magicString = reader.ReadBytes(4); if (!StringUtils.Equals(magicString, "DDS ")) { throw new FileFormatException("Invalid DDS file magic string: \"" + System.Text.Encoding.ASCII.GetString(magicString) + "\"."); } // Deserialize the DDS file header. var header = new DDSHeader(); header.Deserialize(reader); // Figure out the texture format and load the texture data. bool hasMipmaps; uint DDSMipmapLevelCount; TextureFormat textureFormat; int bytesPerPixel; byte[] textureData; ExtractDDSTextureFormatAndData(header, reader, out hasMipmaps, out DDSMipmapLevelCount, out textureFormat, out bytesPerPixel, out textureData); // Post-process the texture to generate missing mipmaps and possibly flip it vertically. PostProcessDDSTexture((int)header.dwWidth, (int)header.dwHeight, bytesPerPixel, hasMipmaps, (int)DDSMipmapLevelCount, textureData, flipVertically); return(new Texture2DInfo((int)header.dwWidth, (int)header.dwHeight, textureFormat, hasMipmaps, textureData)); } }
public RDATField(UnityBinaryReader r, int dataSize) { Type = r.ReadLEUInt32(); Flags = (REGNType)r.ReadByte(); Priority = r.ReadByte(); r.SkipBytes(2); // Unused }
public TNAMField TNAM; // Timing public override bool CreateField(UnityBinaryReader r, GameFormatId format, string type, int dataSize) { switch (type) { case "EDID": EDID = r.ReadSTRV(dataSize); return(true); case "MODL": MODL = new MODLGroup(r, dataSize); return(true); case "MODB": MODL.MODBField(r, dataSize); return(true); case "FNAM": FNAM = r.ReadFILE(dataSize); return(true); case "GNAM": GNAM = r.ReadFILE(dataSize); return(true); case "WLST": for (var i = 0; i < dataSize >> 3; i++) { WLSTs.Add(new WLSTField(r, dataSize)); } return(true); case "TNAM": TNAM = new TNAMField(r, dataSize); return(true); default: return(false); } }
public IN16Field?ANAM; // Enchantment points (optional) public override bool CreateField(UnityBinaryReader r, GameFormatId format, string type, int dataSize) { switch (type) { case "EDID": case "NAME": EDID = r.ReadSTRV(dataSize); return(true); case "MODL": MODL = new MODLGroup(r, dataSize); return(true); case "MODB": MODL.MODBField(r, dataSize); return(true); case "MODT": MODL.MODTField(r, dataSize); return(true); case "FULL": case "FNAM": FULL = r.ReadSTRV(dataSize); return(true); case "DATA": case "BKDT": DATA = new DATAField(r, dataSize, format); return(true); case "ICON": case "ITEX": ICON = r.ReadFILE(dataSize); return(true); case "SCRI": SCRI = new FMIDField <SCPTRecord>(r, dataSize); return(true); case "DESC": case "TEXT": DESC = r.ReadSTRV(dataSize); return(true); case "ENAM": ENAM = new FMIDField <ENCHRecord>(r, dataSize); return(true); case "ANAM": ANAM = r.ReadT <IN16Field>(dataSize); return(true); default: return(false); } }
public DATAField(UnityBinaryReader r, int dataSize) { Flags = r.ReadLEUInt32(); BaseCost = r.ReadLESingle(); AssocItem = r.ReadLEInt32(); //wbUnion('Assoc. Item', wbMGEFFAssocItemDecider, [ // wbFormIDCk('Unused', [NULL]), // wbFormIDCk('Assoc. Weapon', [WEAP]), // wbFormIDCk('Assoc. Armor', [ARMO, NULL{?}]), // wbFormIDCk('Assoc. Creature', [CREA, LVLC, NPC_]), // wbInteger('Assoc. Actor Value', itS32, wbActorValueEnum) MagicSchool = r.ReadLEInt32(); ResistValue = r.ReadLEInt32(); CounterEffectCount = r.ReadLEUInt16(); r.SkipBytes(2); // Unused Light = new FormId <LIGHRecord>(r.ReadLEUInt32()); ProjectileSpeed = r.ReadLESingle(); EffectShader = new FormId <EFSHRecord>(r.ReadLEUInt32()); if (dataSize == 36) { return; } EnchantEffect = new FormId <EFSHRecord>(r.ReadLEUInt32()); CastingSound = new FormId <SOUNRecord>(r.ReadLEUInt32()); BoltSound = new FormId <SOUNRecord>(r.ReadLEUInt32()); HitSound = new FormId <SOUNRecord>(r.ReadLEUInt32()); AreaSound = new FormId <SOUNRecord>(r.ReadLEUInt32()); ConstantEffectEnchantmentFactor = r.ReadLESingle(); ConstantEffectBarterFactor = r.ReadLESingle(); }
public BYTVField?XRGD; // Ragdoll Data (optional) public override bool CreateField(UnityBinaryReader r, GameFormatId format, string type, int dataSize) { switch (type) { case "EDID": EDID = r.ReadSTRV(dataSize); return(true); case "NAME": NAME = new FMIDField <Record>(r, dataSize); return(true); case "DATA": DATA = new REFRRecord.DATAField(r, dataSize); return(true); case "XPCI": XPCI = new FMIDField <CELLRecord>(r, dataSize); return(true); case "FULL": XPCI.Value.AddName(r.ReadASCIIString(dataSize)); return(true); case "XLOD": XLOD = r.ReadBYTV(dataSize); return(true); case "XESP": XESP = new REFRRecord.XESPField(r, dataSize); return(true); case "XMRC": XMRC = new FMIDField <REFRRecord>(r, dataSize); return(true); case "XHRS": XHRS = new FMIDField <ACRERecord>(r, dataSize); return(true); case "XSCL": XSCL = r.ReadT <FLTVField>(dataSize); return(true); case "XRGD": XRGD = r.ReadBYTV(dataSize); return(true); default: return(false); } }
public RecordGroup(UnityBinaryReader r, string filePath, GameFormatId format, int recordLevel) { _r = r; _filePath = filePath; _formatId = format; _recordLevel = recordLevel; }
public void ConstructTest_BinaryRange() { UnityBinaryReader reader = new UnityBinaryReader(TestData, 0, 8); Assert.ThrowsException <NullReferenceException>(delegate() { new UnityBinaryReader((byte[])null, 0, 8); }); Assert.ThrowsException <ArgumentOutOfRangeException>(delegate() { new UnityBinaryReader(TestData, 0, 9); }); Assert.ThrowsException <ArgumentOutOfRangeException>(delegate() { new UnityBinaryReader(TestData, 1, 8); }); }
public void Close() { if (_r != null) { _r.Close(); _r = null; } }
public HEDRField(UnityBinaryReader r, int dataSize) { Version = r.ReadLESingle(); FileType = r.ReadLEUInt32(); CompanyName = r.ReadASCIIString(32, ASCIIFormat.ZeroPadded); FileDescription = r.ReadASCIIString(256, ASCIIFormat.ZeroPadded); NumRecords = r.ReadLEUInt32(); }
public void ReadULongBE() { UnityBinaryReader r = new UnityBinaryReader(TestData); Assert.AreEqual <ulong>(0x0123456789ABCDEF, r.ReadULongBE()); // Forward Test Assert.ThrowsException <IndexOutOfRangeException>(delegate() { r.ReadByte(); }); }
public AI_WField(UnityBinaryReader r, int dataSize) { Distance = r.ReadLEInt16(); Duration = r.ReadLEInt16(); TimeOfDay = r.ReadByte(); Idle = r.ReadBytes(8); Unknown = r.ReadByte(); }
public override void DeserializeData(UnityBinaryReader reader, uint dataSize) { distance = reader.ReadLEInt16(); duration = reader.ReadLEInt16(); timeOfDay = reader.ReadByte(); idle = reader.ReadBytes(8); unknow = reader.ReadByte(); }
public void ReadString() { UnityBinaryReader r = new UnityBinaryReader(TestStringBytes); Assert.AreEqual <string>(TestString, r.ReadString(TestStringBytes.Length)); // Forward Test Assert.ThrowsException <IndexOutOfRangeException>(delegate() { r.ReadByte(); }); }
public XSEDField(UnityBinaryReader r, int dataSize) { Seed = r.ReadByte(); if (dataSize == 4) { r.SkipBytes(3); // Unused } }
public void ReadDouble() { UnityBinaryReader r = new UnityBinaryReader(TestDoubleBytes); Assert.AreEqual <double>(TestDouble, r.ReadDouble()); // Forward Test Assert.ThrowsException <IndexOutOfRangeException>(delegate() { r.ReadByte(); }); }
public void RPLDField(UnityBinaryReader r, int dataSize) { Points = new Vector2[dataSize >> 3]; for (var i = 0; i < Points.Length; i++) { Points[i] = new Vector2(r.ReadLESingle(), r.ReadLESingle()); } }
public void ReadSingle() { UnityBinaryReader r = new UnityBinaryReader(TestFloatBytes); Assert.AreEqual <float>(TestFloat, r.ReadFloat()); // Forward Test Assert.ThrowsException <IndexOutOfRangeException>(delegate() { r.ReadByte(); }); }
public SNAMField(UnityBinaryReader r, int dataSize) { Values = new int[dataSize >> 2]; for (var i = 0; i < Values.Length; i++) { Values[i] = r.ReadLEInt32(); } }
public DATAField(UnityBinaryReader r, int dataSize) { Speed = r.ReadLESingle(); Flags = r.ReadLEUInt32(); Value = r.ReadLEUInt32(); Weight = r.ReadLESingle(); Damage = r.ReadLEUInt16(); }
public TRDTField(UnityBinaryReader r, int dataSize) { EmotionType = r.ReadLEUInt32(); EmotionValue = r.ReadLEInt32(); r.SkipBytes(4); // Unused ResponseNumber = r.ReadByte(); r.SkipBytes(3); // Unused }
public void ReadULong() { UnityBinaryReader r = new UnityBinaryReader(TestData); Assert.AreEqual <ulong>(0xEFCDAB8967452301, r.ReadULong()); // Forward Test Assert.ThrowsException <IndexOutOfRangeException>(delegate() { r.ReadByte(); }); }
public PSDTField(UnityBinaryReader r, int dataSize) { Month = r.ReadByte(); DayOfWeek = r.ReadByte(); Date = r.ReadByte(); Time = (sbyte)r.ReadByte(); Duration = r.ReadLEInt32(); }
public void ReadLong() { UnityBinaryReader r = new UnityBinaryReader(TestData); // r_bin Assert.AreEqual <long>(unchecked ((long)0xEFCDAB8967452301), r.ReadLong()); // Forward Test Assert.ThrowsException <IndexOutOfRangeException>(delegate() { r.ReadByte(); }); }
public AI_FField(UnityBinaryReader r, int dataSize) { X = r.ReadLESingle(); Y = r.ReadLESingle(); Z = r.ReadLESingle(); Duration = r.ReadLEInt16(); Id = r.ReadASCIIString(32, ASCIIFormat.ZeroPadded); Unknown = r.ReadLEInt16(); }
// TODO: Endianness? public static PCMAudioBuffer ReadWAV(string filePath) { using (var reader = new UnityBinaryReader(File.Open(filePath, FileMode.Open, FileAccess.Read))) { var chunkID = reader.ReadBytes(4); if (!StringUtils.Equals(chunkID, "RIFF")) { throw new FileFormatException("Invalid chunk ID."); } var chunkSize = reader.ReadLEUInt32(); // Size of the rest of the chunk after this number. var format = reader.ReadBytes(4); if (!StringUtils.Equals(format, "WAVE")) { throw new FileFormatException("Invalid chunk format."); } var subchunk1ID = reader.ReadBytes(4); if (!StringUtils.Equals(subchunk1ID, "fmt ")) { throw new FileFormatException("Invalid subchunk ID."); } var subchunk1Size = reader.ReadLEUInt32(); // Size of rest of subchunk. var audioFormat = reader.ReadLEUInt16(); if (audioFormat != 1) // 1 = PCM { throw new NotImplementedException("Unsupported audio format."); } var numChannels = reader.ReadLEUInt16(); var samplingRate = reader.ReadLEUInt32(); // # of samples per second (not including all channels). var byteRate = reader.ReadLEUInt32(); // # of bytes per second (including all channels). var blockAlign = reader.ReadLEUInt16(); // # of bytes for one sample (including all channels). var bitsPerSample = reader.ReadLEUInt16(); // # of bits per sample (not including all channels). if (subchunk1Size == 18) { // Read any extra values. var subchunk1ExtraSize = reader.ReadLEUInt16(); reader.ReadBytes(subchunk1ExtraSize); } var subchunk2ID = reader.ReadBytes(4); // "data" if (!StringUtils.Equals(subchunk2ID, "data")) { throw new FileFormatException("Invalid subchunk ID."); } var subchunk2Size = reader.ReadLEUInt32(); // Size of rest of subchunk. byte[] audioData = reader.ReadBytes((int)subchunk2Size); return(new PCMAudioBuffer((int)numChannels, (int)bitsPerSample, (int)samplingRate, audioData)); } }
public LNAMField(UnityBinaryReader r, int dataSize) { Direct = new FormId <Record>(r.ReadLEUInt32()); //if (dataSize == 0) IndirectWorld = new FormId <WRLDRecord>(r.ReadLEUInt32()); //if (dataSize == 0) IndirectGridX = r.ReadLEInt16(); IndirectGridY = r.ReadLEInt16(); }
} // Editor ID public override bool CreateField(UnityBinaryReader r, GameFormatId format, string type, int dataSize) { switch (type) { case "EDID": EDID = r.ReadSTRV(dataSize); return(true); default: return(false); } }
public void Deserialize(UnityBinaryReader reader) { dwSize = reader.ReadLEUInt32(); if(dwSize != 124) { throw new FileFormatException("Invalid DDS file header size: " + dwSize.ToString() + '.'); } dwFlags = reader.ReadLEUInt32(); if(!Utils.ContainsBitFlags(dwFlags, (uint)DDSFlags.Height, (uint)DDSFlags.Width)) { throw new FileFormatException("Invalid DDS file flags: " + dwFlags.ToString() + '.'); } dwHeight = reader.ReadLEUInt32(); dwWidth = reader.ReadLEUInt32(); dwPitchOrLinearSize = reader.ReadLEUInt32(); dwDepth = reader.ReadLEUInt32(); dwMipMapCount = reader.ReadLEUInt32(); dwReserved1 = new uint[11]; for(int i = 0; i < dwReserved1.Length; i++) { dwReserved1[i] = reader.ReadLEUInt32(); } pixelFormat = new DDSPixelFormat(); pixelFormat.Deserialize(reader); dwCaps = reader.ReadLEUInt32(); if(!Utils.ContainsBitFlags(dwCaps, (uint)DDSCaps.Texture)) { throw new FileFormatException("Invalid DDS file caps: " + dwCaps.ToString() + '.'); } dwCaps2 = reader.ReadLEUInt32(); dwCaps3 = reader.ReadLEUInt32(); dwCaps4 = reader.ReadLEUInt32(); dwReserved2 = reader.ReadLEUInt32(); }
/// <summary> /// Extracts a DDS file's texture format and pixel data. /// </summary> private static void ExtractDDSTextureFormatAndData(DDSHeader header, UnityBinaryReader reader, out bool hasMipmaps, out uint DDSMipmapLevelCount, out TextureFormat textureFormat, out int bytesPerPixel, out byte[] textureData) { hasMipmaps = Utils.ContainsBitFlags(header.dwCaps, (uint)DDSCaps.Mipmap); // Non-mipmapped textures still have one mipmap level: the texture itself. DDSMipmapLevelCount = hasMipmaps ? header.dwMipMapCount : 1; // If the DDS file contains uncompressed data. if(Utils.ContainsBitFlags(header.pixelFormat.flags, (uint)DDSPixelFormatFlags.RGB)) { // some permutation of RGB if(!Utils.ContainsBitFlags(header.pixelFormat.flags, (uint)DDSPixelFormatFlags.AlphaPixels)) { throw new NotImplementedException("Unsupported DDS file pixel format."); } // some permutation of RGBA else { // There should be 32 bits per pixel. if(header.pixelFormat.RGBBitCount != 32) { throw new FileFormatException("Invalid DDS file pixel format."); } // BGRA32 if((header.pixelFormat.BBitMask == 0x000000FF) && (header.pixelFormat.GBitMask == 0x0000FF00) && (header.pixelFormat.RBitMask == 0x00FF0000) && (header.pixelFormat.ABitMask == 0xFF000000)) { textureFormat = TextureFormat.BGRA32; bytesPerPixel = 4; } // ARGB32 else if((header.pixelFormat.ABitMask == 0x000000FF) && (header.pixelFormat.RBitMask == 0x0000FF00) && (header.pixelFormat.GBitMask == 0x00FF0000) && (header.pixelFormat.BBitMask == 0xFF000000)) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; } else { throw new NotImplementedException("Unsupported DDS file pixel format."); } if(!hasMipmaps) { textureData = new byte[header.dwPitchOrLinearSize * header.dwHeight]; } else { // Create a data buffer to hold all mipmap levels down to 1x1. textureData = new byte[TextureUtils.CalculateMipMappedTextureDataSize((int)header.dwWidth, (int)header.dwHeight, bytesPerPixel)]; } reader.ReadRestOfBytes(textureData, 0); } } else if(StringUtils.Equals(header.pixelFormat.fourCC, "DXT1")) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; var compressedTextureData = reader.ReadRestOfBytes(); textureData = DecodeDXT1ToARGB(compressedTextureData, header.dwWidth, header.dwHeight, header.pixelFormat, DDSMipmapLevelCount); } else if(StringUtils.Equals(header.pixelFormat.fourCC, "DXT3")) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; var compressedTextureData = reader.ReadRestOfBytes(); textureData = DecodeDXT3ToARGB(compressedTextureData, header.dwWidth, header.dwHeight, header.pixelFormat, DDSMipmapLevelCount); } else if(StringUtils.Equals(header.pixelFormat.fourCC, "DXT5")) { textureFormat = TextureFormat.ARGB32; bytesPerPixel = 4; var compressedTextureData = reader.ReadRestOfBytes(); textureData = DecodeDXT5ToARGB(compressedTextureData, header.dwWidth, header.dwHeight, header.pixelFormat, DDSMipmapLevelCount); } else { throw new NotImplementedException("Unsupported DDS file pixel format."); } }
public void Deserialize(UnityBinaryReader reader) { size = reader.ReadLEUInt32(); if(size != 32) { throw new FileFormatException("Invalid DDS file pixel format size: " + size.ToString() + '.'); } flags = reader.ReadLEUInt32(); fourCC = reader.ReadBytes(4); RGBBitCount = reader.ReadLEUInt32(); RBitMask = reader.ReadLEUInt32(); GBitMask = reader.ReadLEUInt32(); BBitMask = reader.ReadLEUInt32(); ABitMask = reader.ReadLEUInt32(); }
/// <summary> /// Loads a DDS texture from an input stream. /// </summary> public static Texture2DInfo LoadDDSTexture(Stream inputStream, bool flipVertically = false) { using(var reader = new UnityBinaryReader(inputStream)) { // Check the magic string. var magicString = reader.ReadBytes(4); if(!StringUtils.Equals(magicString, "DDS ")) { throw new FileFormatException("Invalid DDS file magic string: \"" + System.Text.Encoding.ASCII.GetString(magicString) + "\"."); } // Deserialize the DDS file header. var header = new DDSHeader(); header.Deserialize(reader); // Figure out the texture format and load the texture data. bool hasMipmaps; uint DDSMipmapLevelCount; TextureFormat textureFormat; int bytesPerPixel; byte[] textureData; ExtractDDSTextureFormatAndData(header, reader, out hasMipmaps, out DDSMipmapLevelCount, out textureFormat, out bytesPerPixel, out textureData); // Post-process the texture to generate missing mipmaps and possibly flip it vertically. PostProcessDDSTexture((int)header.dwWidth, (int)header.dwHeight, bytesPerPixel, hasMipmaps, (int)DDSMipmapLevelCount, textureData, flipVertically); return new Texture2DInfo((int)header.dwWidth, (int)header.dwHeight, textureFormat, hasMipmaps, textureData); } }
/// <summary> /// Decodes a DXT1-compressed 4x4 block of texels using a prebuilt 4-color color table. /// </summary> /// <remarks>See https://msdn.microsoft.com/en-us/library/windows/desktop/bb694531(v=vs.85).aspx#BC1 </remarks> private static Color32[] DecodeDXT1TexelBlock(UnityBinaryReader reader, Color[] colorTable) { Debug.Assert(colorTable.Length == 4); // Read pixel color indices. var colorIndices = new uint[16]; var colorIndexBytes = new byte[4]; reader.Read(colorIndexBytes, 0, colorIndexBytes.Length); const uint bitsPerColorIndex = 2; for(uint rowIndex = 0; rowIndex < 4; rowIndex++) { var rowBaseColorIndexIndex = 4 * rowIndex; var rowBaseBitOffset = 8 * rowIndex; for(uint columnIndex = 0; columnIndex < 4; columnIndex++) { // Color indices are arranged from right to left. var bitOffset = rowBaseBitOffset + (bitsPerColorIndex * (3 - columnIndex)); colorIndices[rowBaseColorIndexIndex + columnIndex] = (uint)Utils.GetBits(bitOffset, bitsPerColorIndex, colorIndexBytes); } } // Calculate pixel colors. var colors = new Color32[16]; for(int i = 0; i < 16; i++) { colors[i] = colorTable[colorIndices[i]]; } return colors; }
/// <summary> /// Builds a 4-color color table for a DXT1-compressed 4x4 block of texels and then decodes the texels. /// </summary> /// <remarks>See https://msdn.microsoft.com/en-us/library/windows/desktop/bb694531(v=vs.85).aspx#BC1 </remarks> private static Color32[] DecodeDXT1TexelBlock(UnityBinaryReader reader, bool containsAlpha) { // Create the color table. var colorTable = new Color[4]; colorTable[0] = ColorUtils.R5G6B5ToColor(reader.ReadLEUInt16()); colorTable[1] = ColorUtils.R5G6B5ToColor(reader.ReadLEUInt16()); if(!containsAlpha) { colorTable[2] = Color.Lerp(colorTable[0], colorTable[1], 1.0f / 3); colorTable[3] = Color.Lerp(colorTable[0], colorTable[1], 2.0f / 3); } else { colorTable[2] = Color.Lerp(colorTable[0], colorTable[1], 1.0f / 2); colorTable[3] = new Color(0, 0, 0, 0); } // Calculate pixel colors. return DecodeDXT1TexelBlock(reader, colorTable); }
/// <summary> /// Decodes a DXT3-compressed 4x4 block of texels. /// </summary> /// <remarks>See https://msdn.microsoft.com/en-us/library/windows/desktop/bb694531(v=vs.85).aspx#BC2 </remarks> private static Color32[] DecodeDXT3TexelBlock(UnityBinaryReader reader) { // Read compressed pixel alphas. var compressedAlphas = new byte[16]; for(int rowIndex = 0; rowIndex < 4; rowIndex++) { var compressedAlphaRow = reader.ReadLEUInt16(); for(int columnIndex = 0; columnIndex < 4; columnIndex++) { // Each compressed alpha is 4 bits. compressedAlphas[(4 * rowIndex) + columnIndex] = (byte)((compressedAlphaRow >> (columnIndex * 4)) & 0xF); } } // Calculate pixel alphas. var alphas = new byte[16]; for(int i = 0; i < 16; i++) { var alphaPercent = (float)compressedAlphas[i] / 15; alphas[i] = (byte)Mathf.RoundToInt(alphaPercent * 255); } // Create the color table. var colorTable = new Color[4]; colorTable[0] = ColorUtils.R5G6B5ToColor(reader.ReadLEUInt16()); colorTable[1] = ColorUtils.R5G6B5ToColor(reader.ReadLEUInt16()); colorTable[2] = Color.Lerp(colorTable[0], colorTable[1], 1.0f / 3); colorTable[3] = Color.Lerp(colorTable[0], colorTable[1], 2.0f / 3); // Calculate pixel colors. var colors = DecodeDXT1TexelBlock(reader, colorTable); for(int i = 0; i < 16; i++) { colors[i].a = alphas[i]; } return colors; }
/// <summary> /// Decodes a DXT5-compressed 4x4 block of texels. /// </summary> /// <remarks>See https://msdn.microsoft.com/en-us/library/windows/desktop/bb694531(v=vs.85).aspx#BC3 </remarks> private static Color32[] DecodeDXT5TexelBlock(UnityBinaryReader reader) { // Create the alpha table. var alphaTable = new float[8]; alphaTable[0] = reader.ReadByte(); alphaTable[1] = reader.ReadByte(); if(alphaTable[0] > alphaTable[1]) { for(int i = 0; i < 6; i++) { alphaTable[2 + i] = Mathf.Lerp(alphaTable[0], alphaTable[1], (float)(1 + i) / 7); } } else { for(int i = 0; i < 4; i++) { alphaTable[2 + i] = Mathf.Lerp(alphaTable[0], alphaTable[1], (float)(1 + i) / 5); } alphaTable[6] = 0; alphaTable[7] = 255; } // Read pixel alpha indices. var alphaIndices = new uint[16]; var alphaIndexBytesRow0 = new byte[3]; reader.Read(alphaIndexBytesRow0, 0, alphaIndexBytesRow0.Length); Array.Reverse(alphaIndexBytesRow0); // Take care of little-endianness. var alphaIndexBytesRow1 = new byte[3]; reader.Read(alphaIndexBytesRow1, 0, alphaIndexBytesRow1.Length); Array.Reverse(alphaIndexBytesRow1); // Take care of little-endianness. const uint bitsPerAlphaIndex = 3; alphaIndices[0] = (uint)Utils.GetBits(21, bitsPerAlphaIndex, alphaIndexBytesRow0); alphaIndices[1] = (uint)Utils.GetBits(18, bitsPerAlphaIndex, alphaIndexBytesRow0); alphaIndices[2] = (uint)Utils.GetBits(15, bitsPerAlphaIndex, alphaIndexBytesRow0); alphaIndices[3] = (uint)Utils.GetBits(12, bitsPerAlphaIndex, alphaIndexBytesRow0); alphaIndices[4] = (uint)Utils.GetBits(9, bitsPerAlphaIndex, alphaIndexBytesRow0); alphaIndices[5] = (uint)Utils.GetBits(6, bitsPerAlphaIndex, alphaIndexBytesRow0); alphaIndices[6] = (uint)Utils.GetBits(3, bitsPerAlphaIndex, alphaIndexBytesRow0); alphaIndices[7] = (uint)Utils.GetBits(0, bitsPerAlphaIndex, alphaIndexBytesRow0); alphaIndices[8] = (uint)Utils.GetBits(21, bitsPerAlphaIndex, alphaIndexBytesRow1); alphaIndices[9] = (uint)Utils.GetBits(18, bitsPerAlphaIndex, alphaIndexBytesRow1); alphaIndices[10] = (uint)Utils.GetBits(15, bitsPerAlphaIndex, alphaIndexBytesRow1); alphaIndices[11] = (uint)Utils.GetBits(12, bitsPerAlphaIndex, alphaIndexBytesRow1); alphaIndices[12] = (uint)Utils.GetBits(9, bitsPerAlphaIndex, alphaIndexBytesRow1); alphaIndices[13] = (uint)Utils.GetBits(6, bitsPerAlphaIndex, alphaIndexBytesRow1); alphaIndices[14] = (uint)Utils.GetBits(3, bitsPerAlphaIndex, alphaIndexBytesRow1); alphaIndices[15] = (uint)Utils.GetBits(0, bitsPerAlphaIndex, alphaIndexBytesRow1); // Create the color table. var colorTable = new Color[4]; colorTable[0] = ColorUtils.R5G6B5ToColor(reader.ReadLEUInt16()); colorTable[1] = ColorUtils.R5G6B5ToColor(reader.ReadLEUInt16()); colorTable[2] = Color.Lerp(colorTable[0], colorTable[1], 1.0f / 3); colorTable[3] = Color.Lerp(colorTable[0], colorTable[1], 2.0f / 3); // Calculate pixel colors. var colors = DecodeDXT1TexelBlock(reader, colorTable); for(int i = 0; i < 16; i++) { colors[i].a = (byte)Mathf.Round(alphaTable[alphaIndices[i]]); } return colors; }
/// <summary> /// Decodes DXT data to ARGB. /// </summary> private static byte[] DecodeDXTToARGB(int DXTVersion, byte[] compressedData, uint width, uint height, DDSPixelFormat pixelFormat, uint mipmapCount) { bool alphaFlag = Utils.ContainsBitFlags(pixelFormat.flags, (uint)DDSPixelFormatFlags.AlphaPixels); bool containsAlpha = alphaFlag || ((pixelFormat.RGBBitCount == 32) && (pixelFormat.ABitMask != 0)); var reader = new UnityBinaryReader(new MemoryStream(compressedData)); var argb = new byte[TextureUtils.CalculateMipMappedTextureDataSize((int)width, (int)height, 4)]; int mipMapWidth = (int)width; int mipMapHeight = (int)height; int baseARGBIndex = 0; for(int mipMapIndex = 0; mipMapIndex < mipmapCount; mipMapIndex++) { for(int rowIndex = 0; rowIndex < mipMapHeight; rowIndex += 4) { for(int columnIndex = 0; columnIndex < mipMapWidth; columnIndex += 4) { Color32[] colors = null; // Doing a switch instead of using a delegate for speed. switch(DXTVersion) { case 1: colors = DecodeDXT1TexelBlock(reader, containsAlpha); break; case 3: colors = DecodeDXT3TexelBlock(reader); break; case 5: colors = DecodeDXT5TexelBlock(reader); break; default: throw new NotImplementedException("Tried decoding a DDS file using an unsupported DXT format: DXT" + DXTVersion.ToString()); } CopyDecodedTexelBlock(colors, argb, baseARGBIndex, rowIndex, columnIndex, mipMapWidth, mipMapHeight); } } baseARGBIndex += (mipMapWidth * mipMapHeight * 4); mipMapWidth /= 2; mipMapHeight /= 2; } return argb; }