diff --git a/OpenRA.Game/Graphics/SpriteSource.cs b/OpenRA.Game/Graphics/SpriteSource.cs index b2e530332e..09c6f35e95 100644 --- a/OpenRA.Game/Graphics/SpriteSource.cs +++ b/OpenRA.Game/Graphics/SpriteSource.cs @@ -15,7 +15,7 @@ using OpenRA.FileFormats; namespace OpenRA.Graphics { // TODO: Most of this should be moved into the format parsers themselves. - public enum SpriteType { Unknown, ShpTS, ShpD2, TmpTD, TmpRA, TmpTS, R8 } + public enum SpriteType { Unknown, ShpD2, TmpTD, TmpRA, TmpTS, R8 } public static class SpriteSource { static bool IsTmpRA(Stream s) @@ -66,43 +66,6 @@ namespace OpenRA.Graphics return test == sx * sy / 2 + 52; } - static bool IsShpTS(Stream s) - { - var start = s.Position; - - // First word is zero - if (s.ReadUInt16() != 0) - { - s.Position = start; - return false; - } - - // Sanity Check the image count - s.Position += 4; - var imageCount = s.ReadUInt16(); - if (s.Position + 24 * imageCount > s.Length) - { - s.Position = start; - return false; - } - - // Check the size and format flag - // Some files define bogus frames, so loop until we find a valid one - s.Position += 4; - ushort w, h, f = 0; - byte type; - do - { - w = s.ReadUInt16(); - h = s.ReadUInt16(); - type = s.ReadUInt8(); - } - while (w == 0 && h == 0 && f++ < imageCount); - - s.Position = start; - return type < 4; - } - static bool IsShpD2(Stream s) { var start = s.Position; @@ -162,9 +125,6 @@ namespace OpenRA.Graphics public static SpriteType DetectSpriteType(Stream s) { - if (IsShpTS(s)) - return SpriteType.ShpTS; - if (IsR8(s)) return SpriteType.R8; @@ -188,8 +148,6 @@ namespace OpenRA.Graphics var type = DetectSpriteType(s); switch (type) { - case SpriteType.ShpTS: - return new ShpTSReader(s); case SpriteType.R8: return new R8Reader(s); case SpriteType.TmpRA: diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index aaad4818f0..83112e9a2d 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -279,7 +279,6 @@ - diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 984386022d..98520994d1 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -114,6 +114,7 @@ + diff --git a/OpenRA.Game/FileFormats/ShpTSReader.cs b/OpenRA.Mods.Common/SpriteLoaders/ShpTSLoader.cs similarity index 53% rename from OpenRA.Game/FileFormats/ShpTSReader.cs rename to OpenRA.Mods.Common/SpriteLoaders/ShpTSLoader.cs index f45a412323..6471be0aab 100644 --- a/OpenRA.Game/FileFormats/ShpTSReader.cs +++ b/OpenRA.Mods.Common/SpriteLoaders/ShpTSLoader.cs @@ -8,15 +8,19 @@ */ #endregion +using System; +using System.Collections.Generic; using System.Drawing; using System.IO; +using System.Linq; +using OpenRA.FileFormats; using OpenRA.Graphics; -namespace OpenRA.FileFormats +namespace OpenRA.Mods.Common.SpriteLoaders { - public class ShpTSReader : ISpriteSource + public class ShpTSLoader : ISpriteLoader { - class FrameHeader : ISpriteFrame + class ShpTSFrame : ISpriteFrame { public Size Size { get; private set; } public Size FrameSize { get; private set; } @@ -27,7 +31,7 @@ namespace OpenRA.FileFormats public readonly uint FileOffset; public readonly byte Format; - public FrameHeader(Stream stream, Size frameSize) + public ShpTSFrame(Stream stream, Size frameSize) { var x = stream.ReadUInt16(); var y = stream.ReadUInt16(); @@ -45,20 +49,56 @@ namespace OpenRA.FileFormats } } - public IReadOnlyList Frames { get; private set; } - - public ShpTSReader(Stream stream) + bool IsShpTS(Stream s) { - stream.ReadUInt16(); - var width = stream.ReadUInt16(); - var height = stream.ReadUInt16(); - var size = new Size(width, height); - var frameCount = stream.ReadUInt16(); + var start = s.Position; - var frames = new FrameHeader[frameCount]; - Frames = frames.AsReadOnly(); + // First word is zero + if (s.ReadUInt16() != 0) + { + s.Position = start; + return false; + } + + // Sanity Check the image count + s.Position += 4; + var imageCount = s.ReadUInt16(); + if (s.Position + 24 * imageCount > s.Length) + { + s.Position = start; + return false; + } + + // Check the size and format flag + // Some files define bogus frames, so loop until we find a valid one + s.Position += 4; + ushort w, h, f = 0; + byte type; + do + { + w = s.ReadUInt16(); + h = s.ReadUInt16(); + type = s.ReadUInt8(); + } + while (w == 0 && h == 0 && f++ < imageCount); + + s.Position = start; + return type < 4; + } + + ShpTSFrame[] ParseFrames(Stream s) + { + var start = s.Position; + + s.ReadUInt16(); + var width = s.ReadUInt16(); + var height = s.ReadUInt16(); + var size = new Size(width, height); + var frameCount = s.ReadUInt16(); + + var frames = new ShpTSFrame[frameCount]; for (var i = 0; i < frames.Length; i++) - frames[i] = new FrameHeader(stream, size); + frames[i] = new ShpTSFrame(s, size); for (var i = 0; i < frameCount; i++) { @@ -66,13 +106,13 @@ namespace OpenRA.FileFormats if (f.FileOffset == 0) continue; - stream.Position = f.FileOffset; + s.Position = f.FileOffset; var frameSize = f.Size.Width * f.Size.Height; // Uncompressed if (f.Format == 1 || f.Format == 0) - f.Data = stream.ReadBytes(frameSize); + f.Data = s.ReadBytes(frameSize); // Uncompressed scanlines else if (f.Format == 2) @@ -80,9 +120,9 @@ namespace OpenRA.FileFormats f.Data = new byte[frameSize]; for (var j = 0; j < f.Size.Height; j++) { - var length = stream.ReadUInt16() - 2; + var length = s.ReadUInt16() - 2; var offset = f.Size.Width * j; - stream.ReadBytes(f.Data, offset, length); + s.ReadBytes(f.Data, offset, length); } } @@ -92,12 +132,27 @@ namespace OpenRA.FileFormats f.Data = new byte[frameSize]; for (var j = 0; j < f.Size.Height; j++) { - var length = stream.ReadUInt16() - 2; + var length = s.ReadUInt16() - 2; var offset = f.Size.Width * j; - Format2.DecodeInto(stream.ReadBytes(length), f.Data, offset); + Format2.DecodeInto(s.ReadBytes(length), f.Data, offset); } } } + + s.Position = start; + return frames; + } + + public bool TryParseSprite(Stream s, out ISpriteFrame[] frames) + { + if (!IsShpTS(s)) + { + frames = null; + return false; + } + + frames = ParseFrames(s); + return true; } } -} \ No newline at end of file +} diff --git a/mods/cnc/mod.yaml b/mods/cnc/mod.yaml index a7e412249e..2bffd79982 100644 --- a/mods/cnc/mod.yaml +++ b/mods/cnc/mod.yaml @@ -211,4 +211,4 @@ Missions: SupportsMapsFrom: cnc -SpriteFormats: ShpTD \ No newline at end of file +SpriteFormats: ShpTD, ShpTS \ No newline at end of file diff --git a/mods/ra/mod.yaml b/mods/ra/mod.yaml index 2807d787b6..24882a5e1c 100644 --- a/mods/ra/mod.yaml +++ b/mods/ra/mod.yaml @@ -208,4 +208,4 @@ Missions: SupportsMapsFrom: ra -SpriteFormats: ShpTD \ No newline at end of file +SpriteFormats: ShpTD, ShpTS \ No newline at end of file diff --git a/mods/ts/mod.yaml b/mods/ts/mod.yaml index 28fcce5127..ce7a0b001e 100644 --- a/mods/ts/mod.yaml +++ b/mods/ts/mod.yaml @@ -230,4 +230,4 @@ LuaScripts: SupportsMapsFrom: ts -SpriteFormats: ShpTD \ No newline at end of file +SpriteFormats: ShpTD, ShpTS \ No newline at end of file