diff --git a/OpenRA.FileFormats/Graphics/ShpReader.cs b/OpenRA.FileFormats/Graphics/ShpReader.cs index 517d505978..c10d61d8af 100644 --- a/OpenRA.FileFormats/Graphics/ShpReader.cs +++ b/OpenRA.FileFormats/Graphics/ShpReader.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2013 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation. For more information, @@ -15,10 +15,12 @@ using System.Linq; namespace OpenRA.FileFormats { + enum Format { Format20 = 0x20, Format40 = 0x40, Format80 = 0x80 } + class ImageHeader : ISpriteFrame { - public Size Size { get; private set; } - public Size FrameSize { get { return Size; } } + public Size Size { get { return reader.Size; } } + public Size FrameSize { get { return reader.Size; } } public float2 Offset { get { return float2.Zero; } } public byte[] Data { get; set; } @@ -29,22 +31,21 @@ namespace OpenRA.FileFormats public Format RefFormat; public ImageHeader RefImage; + ShpReader reader; // Used by ShpWriter public ImageHeader() { } - public ImageHeader(BinaryReader reader, Size size) + public ImageHeader(Stream stream, ShpReader reader) { - var data = reader.ReadUInt32(); - Size = size; + this.reader = reader; + var data = stream.ReadUInt32(); FileOffset = data & 0xffffff; Format = (Format)(data >> 24); - RefOffset = reader.ReadUInt16(); - RefFormat = (Format)reader.ReadUInt16(); + RefOffset = stream.ReadUInt16(); + RefFormat = (Format)stream.ReadUInt16(); } - public static readonly int SizeOnDisk = 8; - public void WriteTo(BinaryWriter writer) { writer.Write(FileOffset | ((uint)Format << 24)); @@ -53,106 +54,96 @@ namespace OpenRA.FileFormats } } - public enum Format { Format20 = 0x20, Format40 = 0x40, Format80 = 0x80 } - public class ShpReader : ISpriteSource { - public readonly int ImageCount; - public readonly ushort Width; - public readonly ushort Height; - - public Size Size { get { return new Size(Width, Height); } } - readonly List headers = new List(); public IEnumerable Frames { get { return headers.Cast(); } } + public readonly Size Size; int recurseDepth = 0; + readonly int imageCount; public ShpReader(Stream stream) { - using (var reader = new BinaryReader(stream)) + imageCount = stream.ReadUInt16(); + stream.Position += 4; + var width = stream.ReadUInt16(); + var height = stream.ReadUInt16(); + Size = new Size(width, height); + + stream.Position += 4; + for (var i = 0; i < imageCount; i++) + headers.Add(new ImageHeader(stream, this)); + + // Skip eof and zero headers + stream.Position += 16; + + var offsets = headers.ToDictionary(h => h.FileOffset, h => h); + for (var i = 0; i < imageCount; i++) { - ImageCount = reader.ReadUInt16(); - reader.ReadUInt16(); - reader.ReadUInt16(); - Width = reader.ReadUInt16(); - Height = reader.ReadUInt16(); - reader.ReadUInt32(); + var h = headers[i]; + if (h.Format == Format.Format20) + h.RefImage = headers[i - 1]; - var size = new Size(Width, Height); - for (int i = 0 ; i < ImageCount ; i++) - headers.Add(new ImageHeader(reader, size)); - - new ImageHeader(reader, size); // end-of-file header - new ImageHeader(reader, size); // all-zeroes header - - var offsets = headers.ToDictionary(h => h.FileOffset, h =>h); - - for (int i = 0 ; i < ImageCount ; i++) - { - var h = headers[ i ]; - if (h.Format == Format.Format20) - h.RefImage = headers[i - 1]; - - else if (h.Format == Format.Format40) - if (!offsets.TryGetValue(h.RefOffset, out h.RefImage)) - throw new InvalidDataException("Reference doesnt point to image data {0}->{1}".F(h.FileOffset, h.RefOffset)); - } - - foreach (ImageHeader h in headers) - Decompress(stream, h); + else if (h.Format == Format.Format40 && !offsets.TryGetValue(h.RefOffset, out h.RefImage)) + throw new InvalidDataException("Reference doesnt point to image data {0}->{1}".F(h.FileOffset, h.RefOffset)); } - } - 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.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[Width * Height]; - Format80.DecodeInto(ReadCompressedData(stream, h), imageBytes); - h.Data = imageBytes; - break; - } - default: - throw new InvalidDataException(); - } + foreach (var h in headers) + Decompress(stream, h); } static byte[] ReadCompressedData(Stream stream, ImageHeader h) { stream.Position = h.FileOffset; - // TODO: Actually, far too big. There's no length field with the correct length though :( - var compressedLength = (int)(stream.Length - stream.Position); - var compressedBytes = new byte[ compressedLength ]; - stream.Read( compressedBytes, 0, compressedLength ); + // Actually, far too big. There's no length field with the correct length though :( + var compressedLength = (int)(stream.Length - stream.Position); + var compressedBytes = new byte[compressedLength]; + stream.Read(compressedBytes, 0, compressedLength); return compressedBytes; } + 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.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(); + } + } + byte[] CopyImageData(byte[] baseImage) { - var imageData = new byte[Width * Height]; - for (int i = 0 ; i < Width * Height ; i++) + var imageData = new byte[Size.Width * Size.Height]; + for (var i = 0; i < Size.Width * Size.Height; i++) imageData[i] = baseImage[i]; return imageData; @@ -164,21 +155,21 @@ namespace OpenRA.FileFormats return new ShpReader(s); } - public static void Write(Stream s, int width, int height, IEnumerable frames) + public static void Write(Stream s, Size size, IEnumerable frames) { var compressedFrames = frames.Select(f => Format80.Encode(f)).ToArray(); // note: end-of-file and all-zeroes headers - var dataOffset = 14 + (compressedFrames.Length + 2) * ImageHeader.SizeOnDisk; + var dataOffset = 14 + (compressedFrames.Length + 2) * 8; using (var bw = new BinaryWriter(s)) { bw.Write((ushort)compressedFrames.Length); - bw.Write((ushort)0); // unused - bw.Write((ushort)0); // unused - bw.Write((ushort)width); - bw.Write((ushort)height); - bw.Write((uint)0); // unused + 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) { diff --git a/OpenRA.Utility/Command.cs b/OpenRA.Utility/Command.cs index ba60a2e740..4e251d724f 100644 --- a/OpenRA.Utility/Command.cs +++ b/OpenRA.Utility/Command.cs @@ -51,7 +51,7 @@ namespace OpenRA.Utility throw new InvalidOperationException("All frames must be the same size"); using (var destStream = File.Create(dest)) - ShpReader.Write(destStream, size.Width, size.Height, frames.Select(f => f.ToBytes())); + ShpReader.Write(destStream, size, frames.Select(f => f.ToBytes())); Console.WriteLine(dest + " saved."); } @@ -213,7 +213,7 @@ namespace OpenRA.Utility var srcImage = ShpReader.Load(args[3]); using (var destStream = File.Create(args[4])) - ShpReader.Write(destStream, srcImage.Width, srcImage.Height, + ShpReader.Write(destStream, srcImage.Size, srcImage.Frames.Select(im => im.Data.Select(px => (byte)remap[px]).ToArray())); } @@ -236,8 +236,7 @@ namespace OpenRA.Utility } using (var destStream = File.Create(args[2])) - ShpReader.Write(destStream, srcImage.Width, srcImage.Height, - destFrames.Select(f => f.Data)); + ShpReader.Write(destStream, srcImage.Size, destFrames.Select(f => f.Data)); } static string FriendlyTypeName(Type t)