using System; using System.IO; using FSO.Files.Utils; using Microsoft.Xna.Framework; namespace FSO.Files.Formats.IFF.Chunks { /// /// This chunk type collects SPR# and SPR2 resources into a "drawing group" which /// can be used to display one tile of an object from all directions and zoom levels. /// Objects which span across multiple tiles have a separate DGRP chunk for each tile. /// A DGRP chunk always consists of 12 images (one for every direction/zoom level combination), /// which in turn contain info about one or more sprites. /// public class DGRP : IffChunk { public DGRPImage[] Images { get; set; } /// /// Gets a DGRPImage instance from this DGRP instance. /// /// The direction the DGRP is facing. /// Zoom level DGRP is drawn at. /// Current rotation of world. /// A DGRPImage instance. public DGRPImage GetImage(uint direction, uint zoom, uint worldRotation){ uint rotatedDirection = 0; /**LeftFront = 0x10, LeftBack = 0x40, RightFront = 0x04, RightBack = 0x01**/ int rotateBits = (int)direction << ((int)worldRotation * 2); rotatedDirection = (uint)((rotateBits & 255) | (rotateBits >> 8)); foreach(DGRPImage image in Images) { if (image.Direction == rotatedDirection && image.Zoom == zoom) { return image; } } return null; } /// /// Reads a DGRP from a stream instance. /// /// An Iff instance. /// A Stream instance holding a DGRP chunk. public override void Read(IffFile iff, Stream stream) { using (var io = IoBuffer.FromStream(stream, ByteOrder.LITTLE_ENDIAN)) { var version = io.ReadUInt16(); uint imageCount = version < 20003 ? io.ReadUInt16() : io.ReadUInt32(); Images = new DGRPImage[imageCount]; for (var i = 0; i < imageCount; i++) { var image = new DGRPImage(this); image.Read(version, io); Images[i] = image; } } } public override bool Write(IffFile iff, Stream stream) { using (var io = IoWriter.FromStream(stream, ByteOrder.LITTLE_ENDIAN)) { io.WriteUInt16(20004); io.WriteUInt32((uint)Images.Length); foreach (var img in Images) { img.Write(io); } } return true; } } /// /// A DGRP is made up of multiple DGRPImages, /// which are made up of multiple DGRPSprites. /// public class DGRPImage { private DGRP Parent; public uint Direction; public uint Zoom; public DGRPSprite[] Sprites; public DGRPImage(DGRP parent) { this.Parent = parent; } /// /// Reads a DGRPImage from a stream. /// /// An Iff instance. /// A Stream object holding a DGRPImage. public void Read(uint version, IoBuffer io) { uint spriteCount = 0; if (version < 20003){ spriteCount = io.ReadUInt16(); Direction = io.ReadByte(); Zoom = io.ReadByte(); }else{ Direction = io.ReadUInt32(); Zoom = io.ReadUInt32(); spriteCount = io.ReadUInt32(); } this.Sprites = new DGRPSprite[spriteCount]; for (var i = 0; i < spriteCount; i++){ var sprite = new DGRPSprite(Parent); sprite.Read(version, io); this.Sprites[i] = sprite; } } public void Write(IoWriter io) { io.WriteUInt32(Direction); io.WriteUInt32(Zoom); io.WriteUInt32((uint)Sprites.Length); foreach (var spr in Sprites) { spr.Write(io); } } } [Flags] public enum DGRPSpriteFlags { Flip = 0x1, Unknown = 0x2, //set for end table Luminous = 0x4, Unknown2 = 0x8, Unknown3 = 0x10 //set for end table } /// /// Makes up a DGRPImage. /// public class DGRPSprite : ITextureProvider, IWorldTextureProvider { private DGRP Parent; public uint SpriteID; public uint SpriteFrameIndex; public DGRPSpriteFlags Flags; public Vector2 SpriteOffset; public Vector3 ObjectOffset; public bool Flip { get { return (Flags & DGRPSpriteFlags.Flip) > 0; } set { Flags = Flags & (~DGRPSpriteFlags.Flip); if (value) Flags |= DGRPSpriteFlags.Flip; } } public bool Luminous { get { return (Flags & DGRPSpriteFlags.Luminous) > 0; } set { Flags = Flags & (~DGRPSpriteFlags.Luminous); if (value) Flags |= DGRPSpriteFlags.Luminous; } } public DGRPSprite(DGRP parent) { this.Parent = parent; } /// /// Reads a DGRPSprite from a stream. /// /// An Iff instance. /// A Stream object holding a DGRPSprite. public void Read(uint version, IoBuffer io) { if (version < 20003) { //Unknown ignored "Type" field var type = io.ReadUInt16(); SpriteID = io.ReadUInt16(); SpriteFrameIndex = io.ReadUInt16(); var flagsRaw = io.ReadUInt16(); Flags = (DGRPSpriteFlags)flagsRaw; SpriteOffset.X = io.ReadInt16(); SpriteOffset.Y = io.ReadInt16(); if (version == 20001) { ObjectOffset.Z = io.ReadFloat(); } } else { SpriteID = io.ReadUInt32(); SpriteFrameIndex = io.ReadUInt32(); SpriteOffset.X = io.ReadInt32(); SpriteOffset.Y = io.ReadInt32(); ObjectOffset.Z = io.ReadFloat(); Flags = (DGRPSpriteFlags)io.ReadUInt32(); if (version == 20004) { ObjectOffset.X = io.ReadFloat(); ObjectOffset.Y = io.ReadFloat(); } } } public void Write(IoWriter io) { io.WriteUInt32(SpriteID); io.WriteUInt32(SpriteFrameIndex); io.WriteInt32((int)SpriteOffset.X); io.WriteInt32((int)SpriteOffset.Y); io.WriteFloat(ObjectOffset.Z); io.WriteUInt32((uint)Flags); io.WriteFloat(ObjectOffset.X); io.WriteFloat(ObjectOffset.Y); } /// /// Gets position of this sprite. /// /// A Vector2 instance holding position of this sprite. public Vector2 GetPosition() { var iff = Parent.ChunkParent; var spr2 = iff.Get((ushort)this.SpriteID); if (spr2 != null) { return spr2.Frames[this.SpriteFrameIndex].Position; } return new Vector2(0, 0); } #region ITextureProvider Members public Microsoft.Xna.Framework.Graphics.Texture2D GetTexture(Microsoft.Xna.Framework.Graphics.GraphicsDevice device){ var iff = Parent.ChunkParent; var spr2 = iff.Get((ushort)this.SpriteID); if (spr2 != null){ return spr2.Frames[this.SpriteFrameIndex].GetTexture(device); } var spr1 = iff.Get((ushort)this.SpriteID); if (spr1 != null){ return spr1.Frames[(int)this.SpriteFrameIndex].GetTexture(device); } return null; } #endregion #region IWorldTextureProvider Members public byte[] GetDepth() { var iff = Parent.ChunkParent; var spr2 = iff.Get((ushort)this.SpriteID); if (spr2 != null) { spr2.Frames[this.SpriteFrameIndex].DecodeIfRequired(true); var buf = spr2.Frames[this.SpriteFrameIndex].ZBufferData; return buf; } return null; } public WorldTexture GetWorldTexture(Microsoft.Xna.Framework.Graphics.GraphicsDevice device) { var iff = Parent.ChunkParent; var spr2 = iff.Get((ushort)this.SpriteID); if (spr2 != null) { return spr2.Frames[this.SpriteFrameIndex].GetWorldTexture(device); } var spr1 = iff.Get((ushort)this.SpriteID); if (spr1 != null) { var result = new WorldTexture(); result.Pixel = spr1.Frames[(int)this.SpriteFrameIndex].GetTexture(device); return result; } return null; } public Point GetDimensions() { var iff = Parent.ChunkParent; var spr2 = iff.Get((ushort)this.SpriteID); if (spr2 != null) { var frame = spr2.Frames[this.SpriteFrameIndex]; return new Point(frame.Width, frame.Height); } var spr1 = iff.Get((ushort)this.SpriteID); if (spr1 != null) { var result = new WorldTexture(); var frame = spr1.Frames[(int)this.SpriteFrameIndex]; return new Point(frame.Width, frame.Height); } return new Point(1, 1); } #endregion } }