using System; using System.IO; using FSO.Files.Utils; using Microsoft.Xna.Framework; namespace FSO.Files.Formats.IFF.Chunks { /// <summary> /// 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. /// </summary> public class DGRP : IffChunk { public DGRPImage[] Images { get; set; } /// <summary> /// Gets a DGRPImage instance from this DGRP instance. /// </summary> /// <param name="direction">The direction the DGRP is facing.</param> /// <param name="zoom">Zoom level DGRP is drawn at.</param> /// <param name="worldRotation">Current rotation of world.</param> /// <returns>A DGRPImage instance.</returns> 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; } /// <summary> /// Reads a DGRP from a stream instance. /// </summary> /// <param name="iff">An Iff instance.</param> /// <param name="stream">A Stream instance holding a DGRP chunk.</param> 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; } } /// <summary> /// A DGRP is made up of multiple DGRPImages, /// which are made up of multiple DGRPSprites. /// </summary> public class DGRPImage { private DGRP Parent; public uint Direction; public uint Zoom; public DGRPSprite[] Sprites; public DGRPImage(DGRP parent) { this.Parent = parent; } /// <summary> /// Reads a DGRPImage from a stream. /// </summary> /// <param name="iff">An Iff instance.</param> /// <param name="stream">A Stream object holding a DGRPImage.</param> 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 } /// <summary> /// Makes up a DGRPImage. /// </summary> 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; } /// <summary> /// Reads a DGRPSprite from a stream. /// </summary> /// <param name="iff">An Iff instance.</param> /// <param name="stream">A Stream object holding a DGRPSprite.</param> 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); } /// <summary> /// Gets position of this sprite. /// </summary> /// <returns>A Vector2 instance holding position of this sprite.</returns> public Vector2 GetPosition() { var iff = Parent.ChunkParent; var spr2 = iff.Get<SPR2>((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<SPR2>((ushort)this.SpriteID); if (spr2 != null){ return spr2.Frames[this.SpriteFrameIndex].GetTexture(device); } var spr1 = iff.Get<SPR>((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<SPR2>((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<SPR2>((ushort)this.SpriteID); if (spr2 != null) { return spr2.Frames[this.SpriteFrameIndex].GetWorldTexture(device); } var spr1 = iff.Get<SPR>((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<SPR2>((ushort)this.SpriteID); if (spr2 != null) { var frame = spr2.Frames[this.SpriteFrameIndex]; return new Point(frame.Width, frame.Height); } var spr1 = iff.Get<SPR>((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 } }