public Dune2ShpReader(Stream stream) { BinaryReader reader = new BinaryReader(stream); ImageCount = reader.ReadUInt16(); //Last offset is pointer to end of file. uint[] offsets = new uint[ImageCount + 1]; uint temp = reader.ReadUInt32(); //If fourth byte in file is non-zero, the offsets are two bytes each. bool twoByteOffsets = (temp & 0xFF0000) > 0; if (twoByteOffsets) { offsets[0] = ((temp & 0xFFFF0000) >> 16) + 2; //Offset does not account for image count bytes offsets[1] = (temp & 0xFFFF) + 2; } else { offsets[0] = temp + 2; } for (int i = twoByteOffsets ? 2 : 1; i < ImageCount + 1; i++) { offsets[i] = (twoByteOffsets ? reader.ReadUInt16() : reader.ReadUInt32()) + 2; } for (int i = 0; i < ImageCount; i++) { reader.BaseStream.Seek(offsets[i], SeekOrigin.Begin); Dune2ImageHeader header = new Dune2ImageHeader(reader); byte[] imgData = reader.ReadBytes(header.FileSize); header.Image = new byte[header.Height * header.Width]; //Decode image data if (header.Flags != Dune2ImageFlags.F2) { byte[] tempData = new byte[header.DataSize]; Format80.DecodeInto(imgData, tempData); Format2.DecodeInto(tempData, header.Image); } else { Format2.DecodeInto(imgData, header.Image); } //Lookup values in lookup table if (header.LookupTable != null) { for (int j = 0; j < header.Image.Length; j++) { header.Image[j] = header.LookupTable[header.Image[j]]; } } headers.Add(header); } }
public Frame(Stream s) { var flags = (FormatFlags)s.ReadUInt16(); s.Position += 1; var width = s.ReadUInt16(); var height = s.ReadUInt8(); Size = new Size(width, height); // Subtract header size var dataLeft = s.ReadUInt16() - 10; var dataSize = s.ReadUInt16(); byte[] table; if ((flags & FormatFlags.PaletteTable) != 0) { var n = (flags & FormatFlags.VariableLengthTable) != 0 ? s.ReadUInt8() : (byte)16; table = new byte[n]; for (var i = 0; i < n; i++) { table[i] = s.ReadUInt8(); } dataLeft -= n; } else { table = new byte[256]; for (var i = 0; i < 256; i++) { table[i] = (byte)i; } table[1] = 0x7f; table[2] = 0x7e; table[3] = 0x7d; table[4] = 0x7c; } Data = new byte[width * height]; // Decode image data var compressed = s.ReadBytes(dataLeft); if ((flags & FormatFlags.SkipFormat80) == 0) { var temp = new byte[dataSize]; Format80.DecodeInto(compressed, temp); compressed = temp; } Format2.DecodeInto(compressed, Data, 0); // Lookup values in lookup table for (var j = 0; j < Data.Length; j++) { Data[j] = table[Data[j]]; } }
void Decompress(Stream stream, ImageHeader h) { // No extra work is required for empty frames if (h.Size.Width == 0 || h.Size.Height == 0) { return; } if (recurseDepth > imageCount) { throw new InvalidDataException("Format20/40 headers contain infinite loop"); } switch (h.Format) { case Format.Format20: case Format.Format40: { if (h.RefImage.Data == null) { ++recurseDepth; Decompress(stream, h.RefImage); --recurseDepth; } h.Data = CopyImageData(h.RefImage.Data); Format40.DecodeInto(ReadCompressedData(stream, h), h.Data); break; } case Format.Format80: { var imageBytes = new byte[Size.Width * Size.Height]; Format80.DecodeInto(ReadCompressedData(stream, h), imageBytes); h.Data = imageBytes; break; } default: throw new InvalidDataException(); } }
public static void Write(Stream s, Size size, IEnumerable <byte[]> frames) { var compressedFrames = frames.Select(f => Format80.Encode(f)).ToArray(); // note: end-of-file and all-zeroes headers var dataOffset = 14 + (compressedFrames.Length + 2) * 8; using (var bw = new BinaryWriter(s)) { bw.Write((ushort)compressedFrames.Length); bw.Write((ushort)0); bw.Write((ushort)0); bw.Write((ushort)size.Width); bw.Write((ushort)size.Height); bw.Write((uint)0); foreach (var f in compressedFrames) { var ih = new ImageHeader { Format = Format.Format80, FileOffset = (uint)dataOffset }; dataOffset += f.Length; ih.WriteTo(bw); } var eof = new ImageHeader { FileOffset = (uint)dataOffset }; eof.WriteTo(bw); var allZeroes = new ImageHeader { }; allZeroes.WriteTo(bw); foreach (var f in compressedFrames) { bw.Write(f); } } }
void Decompress(Stream stream, ImageHeader h) { if (recurseDepth > ImageCount) { throw new InvalidDataException("Format20/40 headers contain infinite loop"); } switch (h.Format) { case Format.Format20: case Format.Format40: { if (h.RefImage.Image == null) { ++recurseDepth; Decompress(stream, h.RefImage); --recurseDepth; } h.Image = CopyImageData(h.RefImage.Image); Format40.DecodeInto(ReadCompressedData(stream, h), h.Image); break; } case Format.Format80: { var imageBytes = new byte[Width * Height]; Format80.DecodeInto(ReadCompressedData(stream, h), imageBytes); h.Image = imageBytes; break; } default: throw new InvalidDataException(); } }
// VQA Frame public void DecodeVQFR(Stream s) { while (true) { // Chunks are aligned on even bytes; may be padded with a single null if (s.Peek() == 0) { s.ReadByte(); } var type = s.ReadASCII(4); int subchunkLength = (int)int2.Swap(s.ReadUInt32()); switch (type) { // Full frame-modifier case "CBFZ": Format80.DecodeInto(s.ReadBytes(subchunkLength), cbf); break; case "CBF0": cbf = s.ReadBytes(subchunkLength); break; // frame-modifier chunk case "CBP0": case "CBPZ": // Partial buffer is full; dump and recreate if (cbChunk == cbParts) { if (type == "CBP0") { cbf = (byte[])cbp.Clone(); } else { Format80.DecodeInto(cbp, cbf); } cbOffset = cbChunk = 0; } var bytes = s.ReadBytes(subchunkLength); bytes.CopyTo(cbp, cbOffset); cbOffset += subchunkLength; cbChunk++; break; // Palette case "CPL0": for (int i = 0; i < numColors; i++) { byte r = (byte)(s.ReadUInt8() << 2); byte g = (byte)(s.ReadUInt8() << 2); byte b = (byte)(s.ReadUInt8() << 2); palette[i] = (uint)((255 << 24) | (r << 16) | (g << 8) | b); } break; // Frame data case "VPTZ": Format80.DecodeInto(s.ReadBytes(subchunkLength), origData); // This is the last subchunk return; default: throw new InvalidDataException("Unknown sub-chunk {0}".F(type)); } } }
// VQA Frame public void DecodeVQFR(Stream s, string parentType = "VQFR") { while (true) { // Chunks are aligned on even bytes; may be padded with a single null if (s.Peek() == 0) { s.ReadByte(); } var type = s.ReadASCII(4); var subchunkLength = (int)int2.Swap(s.ReadUInt32()); switch (type) { // Full frame-modifier case "CBFZ": var decodeMode = s.Peek() == 0; s.ReadBytes(fileBuffer, 0, subchunkLength); Array.Clear(cbf, 0, cbf.Length); Array.Clear(cbfBuffer, 0, cbfBuffer.Length); var decodeCount = 0; decodeCount = Format80.DecodeInto(fileBuffer, cbfBuffer, decodeMode ? 1 : 0, decodeMode); if ((videoFlags & 0x10) == 16) { var p = 0; for (var i = 0; i < decodeCount; i += 2) { var packed = cbfBuffer[i + 1] << 8 | cbfBuffer[i]; /* 15 bit 0 * 0rrrrrgg gggbbbbb * HI byte LO byte*/ cbf[p++] = (byte)((packed & 0x7C00) >> 7); cbf[p++] = (byte)((packed & 0x3E0) >> 2); cbf[p++] = (byte)((packed & 0x1f) << 3); } } else { cbf = cbfBuffer; } if (parentType == "VQFL") { return; } break; case "CBF0": cbf = s.ReadBytes(subchunkLength); break; // frame-modifier chunk case "CBP0": case "CBPZ": // Partial buffer is full; dump and recreate if (currentChunkBuffer == chunkBufferParts) { if (type == "CBP0") { cbf = (byte[])cbp.Clone(); } else { Format80.DecodeInto(cbp, cbf); } chunkBufferOffset = currentChunkBuffer = 0; } var bytes = s.ReadBytes(subchunkLength); bytes.CopyTo(cbp, chunkBufferOffset); chunkBufferOffset += subchunkLength; currentChunkBuffer++; break; // Palette case "CPL0": for (var i = 0; i < numColors; i++) { var r = (byte)(s.ReadUInt8() << 2); var g = (byte)(s.ReadUInt8() << 2); var b = (byte)(s.ReadUInt8() << 2); palette[i] = (uint)((255 << 24) | (r << 16) | (g << 8) | b); } break; // Frame data case "VPTZ": Format80.DecodeInto(s.ReadBytes(subchunkLength), origData); // This is the last subchunk return; case "VPRZ": Array.Clear(origData, 0, origData.Length); s.ReadBytes(fileBuffer, 0, subchunkLength); if (fileBuffer[0] != 0) { vtprSize = Format80.DecodeInto(fileBuffer, origData); } else { Format80.DecodeInto(fileBuffer, origData, 1, true); } return; case "VPTR": Array.Clear(origData, 0, origData.Length); s.ReadBytes(origData, 0, subchunkLength); vtprSize = subchunkLength; return; default: throw new InvalidDataException("Unknown sub-chunk {0}".F(type)); } } }