/// <summary> /// Internal write implementation of the sub classes. /// </summary> /// <param name="writer"></param> /// <exception cref="Exception">Expected DDS RGB24 or RGBA32 color format!</exception> /// <exception cref="InvalidOperationException"> /// </exception> protected override void _Write(BinaryWriter writer) { long baseOffset = writer.BaseStream.Position; Bitmap bmp = CreateBitmap(); //bmp.RotateFlip(RotateFlipType.RotateNoneFlipY); if (DataFormat == PvrDataFormat.DDS || DataFormat == PvrDataFormat.DDS_2) { if (!(PixelFormat == PvrPixelFormat.DDS_DXT1_RGB24 || PixelFormat == PvrPixelFormat.DDS_DXT3_RGBA32)) { throw new Exception("Expected DDS RGB24 or RGBA32 color format!"); } byte[] ddsBuffer = null; if (PixelFormat == PvrPixelFormat.DDS_DXT1_RGB24) { DDSFormatDetails ddsFormatDetails = new DDSFormatDetails(DDSFormat.DDS_DXT1); ddsBuffer = DDSGeneral.Save(MipMaps, ddsFormatDetails, DDSGeneral.AlphaSettings.KeepAlpha, DDSGeneral.MipHandling.Default); } else if (PixelFormat == PvrPixelFormat.DDS_DXT3_RGBA32) { DDSFormatDetails ddsFormatDetails = new DDSFormatDetails(DDSFormat.DDS_DXT3); ddsBuffer = DDSGeneral.Save(MipMaps, ddsFormatDetails, DDSGeneral.AlphaSettings.KeepAlpha, DDSGeneral.MipHandling.Default); } if (HasGlobalIndex) { writer.Write(m_gbix); writer.Write(GlobalIndexSize); writer.Write(GlobalIndex); } writer.Write(m_pvrt); writer.Write(ddsBuffer.Length + 16); writer.Write((byte)PixelFormat); writer.Write((byte)DataFormat); writer.Write((ushort)0); writer.Write((ushort)Width); writer.Write((ushort)Height); writer.Write(ddsBuffer); } else { // Set the data format and pixel format and load the appropiate codecs PixelCodec = PvrPixelCodec.GetPixelCodec(PixelFormat); DataCodec = PvrDataCodec.GetDataCodec(DataFormat); // Make sure the pixel and data codecs exists and we can encode to it if (PixelCodec == null || !PixelCodec.CanEncode) { throw new InvalidOperationException(); } if (DataCodec == null || !DataCodec.CanEncode) { throw new InvalidOperationException(); } DataCodec.PixelCodec = PixelCodec; byte[] decodedData = null; if (DataCodec.PaletteEntries != 0) { if (DataCodec.VQ) { decodedData = BitmapToRawVQ(bmp, DataCodec.PaletteEntries, out m_texturePalette); } else { // Convert the bitmap to an array containing indicies. decodedData = BitmapToRawIndexed(bmp, DataCodec.PaletteEntries, out m_texturePalette); // If this texture has an external palette file, set up the palette encoder if (DataCodec.NeedsExternalPalette) { PaletteEncoder = new PvpPaletteEncoder(m_texturePalette, (ushort)DataCodec.PaletteEntries, PixelFormat, PixelCodec); } } } else { decodedData = BitmapToRaw(bmp); } // Calculate what the length of the texture will be int textureLength = 16 + (int)(Width * Height * (DataCodec.Bpp / 8.0)); if (HasGlobalIndex) { textureLength += 16; } if (DataCodec.PaletteEntries != 0 && !DataCodec.NeedsExternalPalette) { textureLength += (DataCodec.PaletteEntries * PixelCodec.Bpp / 8); } // Calculate the mipmap padding (if the texture contains mipmaps) int mipmapPadding = 0; if (DataCodec.HasMipmaps) { if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP) { // A 1x1 mipmap takes up as much space as a 2x1 mipmap // There are also 4 extra bytes at the end of the file mipmapPadding = (DataCodec.Bpp) >> 3; textureLength += 4; } else if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP_ALT) { // A 1x1 mipmap takes up as much space as a 2x2 mipmap mipmapPadding = (3 * DataCodec.Bpp) >> 3; } textureLength += mipmapPadding; for (int size = 1; size < Width; size <<= 1) { textureLength += Math.Max((size * size * DataCodec.Bpp) >> 3, 1); } } MemoryStream output = new MemoryStream(textureLength); BinaryWriter outputWriter = new BinaryWriter(output); // Write out the GBIX header (if we are including one) if (HasGlobalIndex) { outputWriter.Write(m_gbix); outputWriter.Write(GlobalIndexSize); outputWriter.Write(GlobalIndex); } // Write out the PVRT header outputWriter.Write(m_pvrt); if (HasGlobalIndex) { outputWriter.Write(textureLength - 24); } else { outputWriter.Write(textureLength - 8); } outputWriter.Write((byte)PixelFormat); outputWriter.Write((byte)DataFormat); outputWriter.Write((ushort)0); outputWriter.Write((ushort)Width); outputWriter.Write((ushort)Height); // If we have an internal palette, write it if (DataCodec.PaletteEntries != 0 && !DataCodec.NeedsExternalPalette) { byte[] palette = PixelCodec.EncodePalette(m_texturePalette, DataCodec.PaletteEntries); output.Write(palette, 0, palette.Length); } // Write out any mipmaps if (DataCodec.HasMipmaps) { // Write out any padding bytes before the 1x1 mipmap for (int i = 0; i < mipmapPadding; i++) { output.WriteByte(0); } for (int size = 1; size < Width; size <<= 1) { byte[] mipmapDecodedData = null; if (DataCodec.NeedsExternalPalette) { if (DataCodec.VQ) { mipmapDecodedData = BitmapToRawVQResized(bmp, size, 1, m_codeBook); } else { mipmapDecodedData = BitmapToRawIndexedResized(bmp, size, 1, m_palette); } } else { mipmapDecodedData = BitmapToRawResized(bmp, size, 1); } byte[] mipmapTextureData = DataCodec.Encode(mipmapDecodedData, 0, size, size); output.Write(mipmapTextureData, 0, mipmapTextureData.Length); } } // Write the texture data byte[] textureData = DataCodec.Encode(decodedData, Width, Height, null); output.Write(textureData, 0, textureData.Length); // If the data format is square twiddled with mipmaps, write out the extra bytes. if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP) { output.Write(new byte[] { 0, 0, 0, 0 }, 0, 4); } // Compress the texture if (CompressionFormat != PvrCompressionFormat.NONE) { CompressionCodec = PvrCompressionCodec.GetCompressionCodec(CompressionFormat); if (CompressionCodec != null) { // Ok, we need to convert the current stream to an array, compress it, then write it back to a new stream byte[] buffer = output.ToArray(); buffer = CompressionCodec.Compress(buffer, (HasGlobalIndex ? 0x20 : 0x10), PixelCodec, DataCodec); writer.Write(buffer); } } else { writer.Write(output.GetBuffer()); } } }
/// <summary> /// 查找当前类型的图片 /// </summary> /// <param name="byData">当前打开文件的字节数据</param> /// <param name="file">当前文件</param> /// <param name="imgInfos">查找到的图片的信息</param> /// <returns>是否查找成功</returns> public override List <byte[]> SearchImg(byte[] byData, string file, List <string> imgInfos) { List <byte[]> imgList = new List <byte[]>(); // 分析文件内部是否包括Pvr文件 for (int i = 0; i < byData.Length - 1024; i++) { if (byData[i] == 0x50 && byData[i + 1] == 0x56 && byData[i + 2] == 0x52 && byData[i + 3] == 0x54 && byData[i - 0x10] == 0x47 && byData[i - 0xf] == 0x42 && byData[i - 0xe] == 0x49 && byData[i - 0xd] == 0x58) { PvrPixelFormat pixelFormat = (PvrPixelFormat)byData[i + 0x08]; PvrDataFormat dataFormat = (PvrDataFormat)byData[i + 0x09]; PvrPixelCodec pixelCodec = PvrPixelCodec.GetPixelCodec(pixelFormat); PvrDataCodec dataCodec = PvrDataCodec.GetDataCodec(dataFormat); if (dataCodec == null) { continue; } dataCodec.PixelCodec = pixelCodec; int byCountPalette = 0; if (pixelCodec == null && byData[i + 0x08] == 0x6) { byCountPalette = 4; } else { byCountPalette = pixelCodec.Bpp >> 3; } // 取得图片大小 int width = (byData[i + 0xD] << 8) | byData[i + 0xC]; int height = (byData[i + 0xF] << 8) | byData[i + 0xE]; int imgByCount = (width * height * dataCodec.Bpp / 8) + (dataCodec.PaletteEntries * byCountPalette); if (imgByCount == 0 || imgByCount > byData.Length) { throw new Exception("图片容量异常格式 : " + imgByCount); } byte[] byGvrData = new byte[0x20 + imgByCount]; Array.Copy(byData, i - 0x10, byGvrData, 0, byGvrData.Length); string imgFileInfo = Util.GetShortName(file) + " " + (i - 0x10).ToString("x") + "--" + (i - 0x10 + byGvrData.Length).ToString("x"); imgList.Add(byGvrData); imgInfos.Add(imgFileInfo); // 取得调色板大小 if (dataCodec.NeedsExternalPalette) { int paletteCount = dataCodec.PaletteEntries * byCountPalette; byte[] byPalette = new byte[paletteCount + 0x10]; int paletteDataStart = i - 0x30; if (byData[paletteDataStart] == 0x50 && byData[paletteDataStart + 1] == 0x50 && byData[paletteDataStart + 2] == 0x56 && byData[paletteDataStart + 3] == 0x52) { paletteDataStart = i - 0x20 - paletteCount - 0x10 - 0x20; } else { paletteDataStart = i - 0x20 - paletteCount - 0x10; } Array.Copy(byData, paletteDataStart, byPalette, 0, byPalette.Length); this.paletteData.Add(imgFileInfo + " " + paletteDataStart.ToString(), byPalette); } } } return(imgList); }
/// <summary> /// Internal read implementation of the sub classes. /// </summary> /// <param name="reader"></param> /// <exception cref="Exception">Expected DDS RGB24 or RGBA32 color format!</exception> /// <exception cref="NotImplementedException">TODO</exception> protected override void _Read(BinaryReader reader) { long baseOffset = reader.BaseStream.Position; int gbixOffset = 0; int pvrtOffset = 0; uint identifier = reader.ReadUInt32(); if (identifier == m_gbix) //"GBIX" { HasGlobalIndex = true; GlobalIndexSize = reader.ReadUInt32(); GlobalIndex = reader.ReadBytes((int)GlobalIndexSize); reader.BaseStream.Seek(4, SeekOrigin.Current); //Skip "PVRT" gbixOffset = 0x00; pvrtOffset = 0x08 + (int)GlobalIndexSize; } else { identifier = reader.ReadUInt32(); if (identifier == m_gbix) { HasGlobalIndex = true; GlobalIndexSize = reader.ReadUInt32(); GlobalIndex = reader.ReadBytes((int)GlobalIndexSize); gbixOffset = 0x04; pvrtOffset = 0x0C + (int)GlobalIndexSize; } else if (identifier == m_pvrt) { gbixOffset = -1; pvrtOffset = 0x04; } else { gbixOffset = -1; pvrtOffset = 0x00; reader.BaseStream.Seek(-4, SeekOrigin.Current); } } // Read information about the texture ContentSize = reader.ReadUInt32(); PixelFormat = (PvrPixelFormat)reader.ReadByte(); DataFormat = (PvrDataFormat)reader.ReadByte(); reader.BaseStream.Seek(2, SeekOrigin.Current); Width = reader.ReadUInt16(); Height = reader.ReadUInt16(); if (DataFormat == PvrDataFormat.DDS || DataFormat == PvrDataFormat.DDS_2) { if (!(PixelFormat == PvrPixelFormat.DDS_DXT1_RGB24 || PixelFormat == PvrPixelFormat.DDS_DXT3_RGBA32)) { throw new Exception("Expected DDS RGB24 or RGBA32 color format!"); } long ddsOffset = reader.BaseStream.Position; DDS_Header header = new DDS_Header(reader.BaseStream); DDSFormatDetails format = new DDSFormatDetails(header.Format, header.DX10_DXGI_AdditionalHeader.dxgiFormat); reader.BaseStream.Seek(ddsOffset, SeekOrigin.Begin); byte[] ddsBuffer = reader.ReadBytes(header.dwPitchOrLinearSize + header.dwSize + 128); MemoryStream memoryStream = new MemoryStream(ddsBuffer, 0, ddsBuffer.Length, true, true); MipMaps = DDSGeneral.LoadDDS(memoryStream, header, 0, format); memoryStream.Close(); Width = header.Width; Height = header.Height; } else { // Get the codecs and make sure we can decode using them PixelCodec = PvrPixelCodec.GetPixelCodec(PixelFormat); DataCodec = PvrDataCodec.GetDataCodec(DataFormat); if (DataCodec != null && PixelCodec != null) { DataCodec.PixelCodec = PixelCodec; } // Set the number of palette entries int paletteEntries = DataCodec.PaletteEntries; if (DataFormat == PvrDataFormat.VECTOR_QUANTIZATION_SMALL || DataFormat == PvrDataFormat.VECTOR_QUANTIZATION_SMALL_MIPMAP) { if (Width <= 16) { paletteEntries = 64; // Actually 16 } else if (Width <= 32) { paletteEntries = 256; // Actually 64 } else if (Width <= 64) { paletteEntries = 512; // Actually 128 } else { paletteEntries = 1024; // Actually 256 } } // Set the palette and data offsets int paletteOffset = 0; int dataOffset = 0; if (paletteEntries == 0 || DataCodec.NeedsExternalPalette) { paletteOffset = -1; dataOffset = pvrtOffset + 0x10; } else { paletteOffset = pvrtOffset + 0x10; dataOffset = paletteOffset + (paletteEntries * (PixelCodec.Bpp >> 3)); } // Get the compression format and determine if we need to decompress this texture reader.BaseStream.Seek(baseOffset, SeekOrigin.Begin); uint first = reader.ReadUInt32(); reader.BaseStream.Seek(baseOffset + pvrtOffset + 4, SeekOrigin.Begin); uint second = reader.ReadUInt32(); if (first == second - pvrtOffset + dataOffset + 8) { CompressionFormat = PvrCompressionFormat.RLE; } else { CompressionFormat = PvrCompressionFormat.NONE; } CompressionCodec = PvrCompressionCodec.GetCompressionCodec(CompressionFormat); if (CompressionFormat != PvrCompressionFormat.NONE && CompressionCodec != null) { //TODO: Convert to stream compatible code throw new NotImplementedException("TODO"); //m_encodedData = CompressionCodec.Decompress(m_encodedData, dataOffset, PixelCodec, DataCodec); // Now place the offsets in the appropiate area if (CompressionFormat == PvrCompressionFormat.RLE) { if (gbixOffset != -1) { gbixOffset -= 4; } pvrtOffset -= 4; if (paletteOffset != -1) { paletteOffset -= 4; } dataOffset -= 4; } } // If the texture contains mipmaps, gets the offsets of them int[] mipmapOffsets; if (DataCodec.HasMipmaps) { int mipmapOffset = 0; mipmapOffsets = new int[(int)Math.Log(Width, 2) + 1]; // Calculate the padding for the first mipmap offset if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP) { mipmapOffset = (DataCodec.Bpp) >> 3; // A 1x1 mipmap takes up as much space as a 2x1 mipmap } else if (DataFormat == PvrDataFormat.SQUARE_TWIDDLED_MIPMAP_ALT) { mipmapOffset = (3 * DataCodec.Bpp) >> 3; // A 1x1 mipmap takes up as much space as a 2x2 mipmap } for (int i = mipmapOffsets.Length - 1, size = 1; i >= 0; i--, size <<= 1) { mipmapOffsets[i] = mipmapOffset; mipmapOffset += Math.Max((size * size * DataCodec.Bpp) >> 3, 1); } } else { mipmapOffsets = new int[1] { 0 }; } //DecodeMipmaps() if (paletteOffset != -1) // The texture contains an embedded palette { reader.BaseStream.Seek(baseOffset + paletteOffset, SeekOrigin.Begin); DataCodec.SetPalette(reader, paletteEntries); } MipMaps = new List <MipMap>(); if (DataCodec.HasMipmaps) { for (int i = 0, size = Width; i < mipmapOffsets.Length; i++, size >>= 1) { reader.BaseStream.Seek(baseOffset + dataOffset + mipmapOffsets[i], SeekOrigin.Begin); byte[] pixels = DataCodec.Decode(reader, size, size, PixelCodec); MipMaps.Add(new MipMap(pixels, size, size)); } } else { reader.BaseStream.Seek(baseOffset + dataOffset + mipmapOffsets[0], SeekOrigin.Begin); byte[] pixels = DataCodec.Decode(reader, Width, Height, PixelCodec); MipMaps.Add(new MipMap(pixels, Width, Height)); } } if (HasGlobalIndex) { reader.BaseStream.Seek(baseOffset + ContentSize + 0xC, SeekOrigin.Begin); } else { reader.BaseStream.Seek(baseOffset + ContentSize, SeekOrigin.Begin); } }