public MPQDCCDirectionFrame(BitMuncher bits, MPQDCCDirection direction) { bits.GetBits(direction.Variable0Bits); // Variable0 Width = (int)bits.GetBits(direction.WidthBits); Height = (int)bits.GetBits(direction.HeightBits); XOffset = bits.GetSignedBits(direction.XOffsetBits); YOffset = bits.GetSignedBits(direction.YOffsetBits); NumberOfOptionalBytes = (int)bits.GetBits(direction.OptionalDataBits); NumberOfCodedBytes = (int)bits.GetBits(direction.CodedBytesBits); FrameIsBottomUp = bits.GetBit() == 1; Box = new Rectangle( XOffset, YOffset - Height + 1, Width, Height ); }
private void FillPixelBuffer(BitMuncher pcd, BitMuncher ec, BitMuncher pm, BitMuncher et, BitMuncher rp) { UInt32 lastPixel = 0; var maxCellX = Frames.Sum(x => x.HorizontalCellCount); var maxCellY = Frames.Sum(x => x.VerticalCellCount); PixelBuffer = new PixelBufferEntry[maxCellX * maxCellY]; for (var i = 0; i < maxCellX * maxCellY; i++) { PixelBuffer[i] = new PixelBufferEntry { Frame = -1, FrameCellIndex = -1, Value = new byte[4] } } ; var cellBuffer = new PixelBufferEntry[HorizontalCellCount * VerticalCellCount]; var frameIndex = -1; var pbIndex = -1; UInt32 pixelMask = 0x00; foreach (var frame in Frames) { frameIndex++; var originCellX = (frame.Box.Left - Box.Left) / 4; var originCellY = (frame.Box.Top - Box.Top) / 4; var frameCellIndex = 0; for (var cellY = 0; cellY < frame.VerticalCellCount; cellY++) { var currentCellY = cellY + originCellY; for (var cellX = 0; cellX < frame.HorizontalCellCount; cellX++, frameCellIndex++) { var currentCell = originCellX + cellX + (currentCellY * HorizontalCellCount); var nextCell = false; var tmp = 0; if (cellBuffer[currentCell] != null) { if (EqualCellsBitstreamSize > 0) { tmp = (int)ec.GetBit(); } else { tmp = 0; } if (tmp == 0) { pixelMask = pm.GetBits(4); } else { nextCell = true; } } else { pixelMask = 0x0F; } if (nextCell) { continue; } // Decode the pixels var pixelStack = new UInt32[4]; lastPixel = 0; int numberOfPixelBits = pixelMaskLookup[pixelMask]; int encodingType = 0; if ((numberOfPixelBits != 0) && (EncodingTypeBitsreamSize > 0)) { encodingType = (int)et.GetBit(); } else { encodingType = 0; } int decodedPixel = 0; for (int i = 0; i < numberOfPixelBits; i++) { if (encodingType != 0) { pixelStack[i] = rp.GetBits(8); } else { pixelStack[i] = lastPixel; var pixelDisplacement = pcd.GetBits(4); pixelStack[i] += pixelDisplacement; while (pixelDisplacement == 15) { pixelDisplacement = pcd.GetBits(4); pixelStack[i] += pixelDisplacement; } } if (pixelStack[i] == lastPixel) { pixelStack[i] = 0; i = numberOfPixelBits; // Just break here.... } else { lastPixel = pixelStack[i]; decodedPixel++; } } var oldEntry = cellBuffer[currentCell]; pbIndex++; var newEntry = PixelBuffer[pbIndex]; var curIdx = decodedPixel - 1; for (int i = 0; i < 4; i++) { if ((pixelMask & (1 << i)) != 0) { if (curIdx >= 0) { newEntry.Value[i] = (byte)pixelStack[curIdx--]; } else { newEntry.Value[i] = 0; } } else { newEntry.Value[i] = oldEntry.Value[i]; } } cellBuffer[currentCell] = newEntry; newEntry.Frame = frameIndex; newEntry.FrameCellIndex = cellX + (cellY * frame.HorizontalCellCount); } } } // Convert the palette entry index into actual palette entries for (var i = 0; i <= pbIndex; i++) { for (var x = 0; x < 4; x++) { PixelBuffer[i].Value[x] = PaletteEntries[PixelBuffer[i].Value[x]]; } } }
public MPQDCCDirection(BitMuncher bm, MPQDCC file) { OutSizeCoded = (int)bm.GetUInt32(); CompressionFlags = (int)bm.GetBits(2); Variable0Bits = crazyBitTable[bm.GetBits(4)]; WidthBits = crazyBitTable[bm.GetBits(4)]; HeightBits = crazyBitTable[bm.GetBits(4)]; XOffsetBits = crazyBitTable[bm.GetBits(4)]; YOffsetBits = crazyBitTable[bm.GetBits(4)]; OptionalDataBits = crazyBitTable[bm.GetBits(4)]; CodedBytesBits = crazyBitTable[bm.GetBits(4)]; Frames = new MPQDCCDirectionFrame[file.FramesPerDirection]; var minx = long.MaxValue; var miny = long.MaxValue; var maxx = long.MinValue; var maxy = long.MinValue; // Load the frame headers for (var frameIdx = 0; frameIdx < file.FramesPerDirection; frameIdx++) { Frames[frameIdx] = new MPQDCCDirectionFrame(bm, this); minx = Math.Min(Frames[frameIdx].Box.X, minx); miny = Math.Min(Frames[frameIdx].Box.Y, miny); maxx = Math.Max(Frames[frameIdx].Box.Right, maxx); maxy = Math.Max(Frames[frameIdx].Box.Bottom, maxy); } Box = new Rectangle { X = (int)minx, Y = (int)miny, Width = (int)(maxx - minx), Height = (int)(maxy - miny) }; if (OptionalDataBits > 0) { throw new OpenDiablo2Exception("Optional bits in DCC data is not currently supported."); } if ((CompressionFlags & 0x2) > 0) { EqualCellsBitstreamSize = (int)bm.GetBits(20); } PixelMaskBitstreamSize = (int)bm.GetBits(20); if ((CompressionFlags & 0x1) > 0) { EncodingTypeBitsreamSize = (int)bm.GetBits(20); RawPixelCodesBitstreamSize = (int)bm.GetBits(20); } // PixelValuesKey var paletteEntries = new List <bool>(); for (var i = 0; i < 256; i++) { paletteEntries.Add(bm.GetBit() != 0); } PaletteEntries = new byte[paletteEntries.Count(x => x)]; var paletteOffset = 0; for (var i = 0; i < 256; i++) { if (!paletteEntries[i]) { continue; } PaletteEntries[paletteOffset++] = (byte)i; } // HERE BE GIANTS: // Because of the way this thing mashes bits together, BIT offset matters // here. For example, if you are on byte offset 3, bit offset 6, and // the EqualCellsBitstreamSize is 20 bytes, then the next bit stream // will be located at byte 23, bit offset 6! var equalCellsBitstream = new BitMuncher(bm); bm.SkipBits(EqualCellsBitstreamSize); var pixelMaskBitstream = new BitMuncher(bm); bm.SkipBits(PixelMaskBitstreamSize); var encodingTypeBitsream = new BitMuncher(bm); bm.SkipBits(EncodingTypeBitsreamSize); var rawPixelCodesBitstream = new BitMuncher(bm); bm.SkipBits(RawPixelCodesBitstreamSize); var pixelCodeandDisplacement = new BitMuncher(bm); // Calculate the cells for the direction CaculateCells(); // Caculate the cells for each of the frames foreach (var frame in Frames) { frame.CalculateCells(this); } // Fill in the pixel buffer FillPixelBuffer(pixelCodeandDisplacement, equalCellsBitstream, pixelMaskBitstream, encodingTypeBitsream, rawPixelCodesBitstream); // Generate the actual frame pixel data GenerateFrames(pixelCodeandDisplacement); // Verify that everything we expected to read was actually read (sanity check)... if (equalCellsBitstream.BitsRead != EqualCellsBitstreamSize) { throw new OpenDiablo2Exception("Did not read the correct number of bits!"); } if (pixelMaskBitstream.BitsRead != PixelMaskBitstreamSize) { throw new OpenDiablo2Exception("Did not read the correct number of bits!"); } if (encodingTypeBitsream.BitsRead != EncodingTypeBitsreamSize) { throw new OpenDiablo2Exception("Did not read the correct number of bits!"); } if (rawPixelCodesBitstream.BitsRead != RawPixelCodesBitstreamSize) { throw new OpenDiablo2Exception("Did not read the correct number of bits!"); } bm.SkipBits(pixelCodeandDisplacement.BitsRead); }