#region Copyright & License Information /* * Copyright 2007-2014 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, * see COPYING. */ #endregion using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using OpenRA.FileFormats; using OpenRA.Graphics; namespace OpenRA.Mods.Common.SpriteLoaders { public class ShpD2Loader : ISpriteLoader { [Flags] enum FormatFlags : int { PaletteTable = 1, SkipFormat80 = 2, VariableLengthTable = 4 } class ShpD2Frame : ISpriteFrame { public Size Size { get; private set; } public Size FrameSize { get { return Size; } } public float2 Offset { get { return float2.Zero; } } public byte[] Data { get; set; } public bool DisableExportPadding { get { return false; } } public ShpD2Frame(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]]; } } bool IsShpD2(Stream s) { var start = s.Position; // First word is the image count var imageCount = s.ReadUInt16(); if (imageCount == 0) { s.Position = start; return false; } // Test for two vs four byte offset var testOffset = s.ReadUInt32(); var offsetSize = (testOffset & 0xFF0000) > 0 ? 2 : 4; // Last offset should point to the end of file var finalOffset = start + 2 + offsetSize * imageCount; if (finalOffset > s.Length) { s.Position = start; return false; } s.Position = finalOffset; var eof = offsetSize == 2 ? s.ReadUInt16() : s.ReadUInt32(); if (eof + 2 != s.Length) { s.Position = start; return false; } // Check the format flag on the first frame var b = s.ReadUInt16(); s.Position = start; return b == 5 || b <= 3; } ShpD2Frame[] ParseFrames(Stream s) { var start = s.Position; var imageCount = s.ReadUInt16(); // Last offset is pointer to end of file. var offsets = new uint[imageCount + 1]; var temp = s.ReadUInt32(); // If fourth byte in file is non-zero, the offsets are two bytes each. var twoByteOffset = (temp & 0xFF0000) > 0; s.Position = 2; for (var i = 0; i < imageCount + 1; i++) offsets[i] = (twoByteOffset ? s.ReadUInt16() : s.ReadUInt32()) + 2; var frames = new ShpD2Frame[imageCount]; for (var i = 0; i < frames.Length; i++) { s.Position = offsets[i]; frames[i] = new ShpD2Frame(s); } s.Position = start; return frames; } public bool TryParseSprite(Stream s, out ISpriteFrame[] frames) { if (!IsShpD2(s)) { frames = null; return false; } frames = ParseFrames(s); return true; } } }