From 20a6c75ba423a82ec030fdfe1e6f5fa4192fbee6 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Fri, 29 Nov 2013 18:33:54 +1300 Subject: [PATCH] Rewrite ShpTSReader. Closes #3746. --- OpenRA.FileFormats/Graphics/ShpTSReader.cs | 650 +++------------------ OpenRA.Game/Graphics/SpriteLoader.cs | 2 +- 2 files changed, 75 insertions(+), 577 deletions(-) diff --git a/OpenRA.FileFormats/Graphics/ShpTSReader.cs b/OpenRA.FileFormats/Graphics/ShpTSReader.cs index 68cfbb918b..66f8e40d6b 100644 --- a/OpenRA.FileFormats/Graphics/ShpTSReader.cs +++ b/OpenRA.FileFormats/Graphics/ShpTSReader.cs @@ -1,615 +1,113 @@ #region Copyright & License Information /* * Copyright 2007-2013 The OpenRA Developers (see AUTHORS) - * This file is part of OpenRA, which is free software. It is made + * 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 LICENSE. + * see COPYING. */ #endregion -using System; -using System.Collections; using System.Collections.Generic; using System.Drawing; using System.IO; +using System.Linq; namespace OpenRA.FileFormats { - - public struct Header + public class TSImageHeader { - public ushort A; - // Unknown - // Width and Height of the images - public ushort Width; - public ushort Height; - public ushort NumImages; - } + public readonly Size Size; + public readonly float2 Offset; + + public readonly uint FileOffset; + public readonly byte Format; - public class HeaderImage - { - public ushort x; - public ushort y; - public ushort cx; - public ushort cy; - // cx and cy are width n height of stored image - public byte compression; - public byte[] align; - public byte[] transparent; - public int zero; - public int offset; public byte[] Image; + + public TSImageHeader(Stream stream, Size frameSize) + { + var x = stream.ReadUInt16(); + var y = stream.ReadUInt16(); + var width = stream.ReadUInt16(); + var height = stream.ReadUInt16(); + + Offset = new float2(x + 0.5f * (width - frameSize.Width), y + 0.5f * (height - frameSize.Height)); + Size = new Size(width, height); + + Format = stream.ReadUInt8(); + stream.Position += 11; + FileOffset = stream.ReadUInt32(); + } } - public struct SHPData - { - public HeaderImage HeaderImage; - public byte[] Databuffer; - public byte[] FrameImage; - } - - public struct SHP - { - public Header Header; - public SHPData[] Data; - } - - - public class ShpTSReader : IEnumerable + public class ShpTSReader { public readonly int ImageCount; - public readonly ushort Width; - public readonly ushort Height; - public readonly ushort Width2; - public readonly ushort Height2; - public int arroff = 0; - public int erri = 0; - public int errj = 0; - public int errk = 0; - public int errl = 0; + public readonly Size Size; - public static int FindNextOffsetFrom(SHP SHP, int Init, int Last) + readonly List frames = new List(); + public IEnumerable Frames { get { return frames; } } + + public ShpTSReader(Stream stream) { - int result; - result = 0; - Last++; - while ((result == 0) && (Init < Last)) + stream.ReadUInt16(); + var width = stream.ReadUInt16(); + var height = stream.ReadUInt16(); + Size = new Size(width, height); + ImageCount = stream.ReadUInt16(); + + for (var i = 0; i < ImageCount; i++) + frames.Add(new TSImageHeader(stream, Size)); + + for (var i = 0; i < ImageCount; i++) { - result = SHP.Data[Init].HeaderImage.offset; - Init++; - } - return result; - } + var f = frames[i]; + if (f.FileOffset == 0) + continue; - private readonly List headers = new List(); + stream.Position = f.FileOffset; - public ShpTSReader(Stream s) - { + // Uncompressed + if (f.Format == 1 || f.Format == 0) + f.Image = stream.ReadBytes(f.Size.Width * f.Size.Height); - SHP SHP = new SHP(); - int FileSize; - int x; - int k = 0; - int l = 0; - - int ImageSize; - int NextOffset; - - byte[] FData; - byte cp; - byte[] Databuffer; - - FileSize = (int)s.Length; - // Get Header - SHP.Header.A = s.ReadUInt16(); - SHP.Header.Width = s.ReadUInt16(); - SHP.Header.Height = s.ReadUInt16(); - SHP.Header.NumImages = s.ReadUInt16(); - - SHP.Data = new SHPData[SHP.Header.NumImages + 1]; - - ImageCount = SHP.Header.NumImages; - - for (x = 1; x <= SHP.Header.NumImages; x++) + // Uncompressed scanlines + else if (f.Format == 2) { - SHP.Data[x].HeaderImage = new HeaderImage(); - - SHP.Data[x].HeaderImage.x = s.ReadUInt16(); - SHP.Data[x].HeaderImage.y = s.ReadUInt16(); - SHP.Data[x].HeaderImage.cx = s.ReadUInt16(); - SHP.Data[x].HeaderImage.cy = s.ReadUInt16(); - - SHP.Data[x].HeaderImage.compression = s.ReadUInt8(); - SHP.Data[x].HeaderImage.align = s.ReadBytes(3); - s.ReadInt32(); - SHP.Data[x].HeaderImage.zero = s.ReadUInt8(); - SHP.Data[x].HeaderImage.transparent = s.ReadBytes(3); - - SHP.Data[x].HeaderImage.offset = s.ReadInt32(); - - } - - Width = SHP.Header.Width; - Height = SHP.Header.Height; - - for (int i = 0; i < ImageCount; i++) - { - headers.Add(SHP.Data[i+1].HeaderImage); - } - - // Read and decode each image from the file - for (x = 1; x <= SHP.Header.NumImages; x++) - { - headers[x - 1].Image = new byte[(Width * Height)]; - for (int i = 0; i < headers[x - 1].Image.Length; i++) - headers[x - 1].Image[i] = 0; - - FData = new byte[(Width * Height)]; - - // Does it really reads the frame? - if (SHP.Data[x].HeaderImage.offset != 0) + f.Image = new byte[f.Size.Width * f.Size.Height]; + for (var j = 0; j < f.Size.Height; j++) { - try + var length = stream.ReadUInt16() - 2; + stream.Read(f.Image, f.Size.Width * j, length); + } + } + + // RLE-zero compressed scanlines + else if (f.Format == 3) + { + f.Image = new byte[f.Size.Width * f.Size.Height]; + + for (var j = 0; j < f.Size.Height; j++) + { + var k = j * f.Size.Width; + var length = stream.ReadUInt16() - 2; + while (length > 0) { - // Now it checks the compression: - if ((SHP.Data[x].HeaderImage.compression == 3)) - { - // decode it - // Compression 3 - NextOffset = FindNextOffsetFrom(SHP, x + 1, SHP.Header.NumImages); - if (NextOffset != 0) + var b = stream.ReadUInt8(); + length--; + + if (b == 0) { - - ImageSize = NextOffset - SHP.Data[x].HeaderImage.offset; - Databuffer = new byte[ImageSize]; - for (int i = 0; i < ImageSize; i++) - { - s.Seek(SHP.Data[x].HeaderImage.offset + i, SeekOrigin.Begin); - Databuffer[i] = s.ReadUInt8(); - } - SHP.Data[x].Databuffer = new byte[(SHP.Data[x].HeaderImage.cx * SHP.Data[x].HeaderImage.cy)]; - Decode3(Databuffer, ref SHP.Data[x].Databuffer, SHP.Data[x].HeaderImage.cx, SHP.Data[x].HeaderImage.cy, ref FileSize); - - k = 0; - l = 0; - for (int i = 0; i < Height; i++) - { - erri = i; - for (int j = SHP.Data[x].HeaderImage.x; j < Width; j++) - { - errj = j; - errl = l; - errk = k; - arroff = i + j + l; - - if (((j + 1) > (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x)) || ((i + 1) > (SHP.Data[x].HeaderImage.cy))) - cp = 0; - else - cp = SHP.Data[x].Databuffer[i + (j - SHP.Data[x].HeaderImage.x) + l]; - - FData[i + j + k] = cp; - - if (j == (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x - 1)) - l = l + (SHP.Data[x].HeaderImage.cx - 1); - - if (j == (Width - 1)) - k = k + (Width - 1); - } - } - //FData = headers[x - 1].Image; - k = 0; - for (int i = 0; i < (Height - SHP.Data[x].HeaderImage.y); i++) - { - for (int j = 0; j < Width; j++) - { - headers[x - 1].Image[i + j + k + (Width * SHP.Data[x].HeaderImage.y)] = FData[i + j + k]; - if (j == (Width - 1)) - { - k = k + (Width - 1); - } - } - } - + k += stream.ReadUInt8(); + length--; } else - { - - ImageSize = 0; - ImageSize = FileSize - SHP.Data[x].HeaderImage.offset; - Databuffer = new byte[ImageSize]; - for (int i = 0; i < ImageSize; i++) - { - s.Seek(SHP.Data[x].HeaderImage.offset + i, SeekOrigin.Begin); - Databuffer[i] = s.ReadUInt8(); - } - SHP.Data[x].Databuffer = new byte[((SHP.Data[x].HeaderImage.cx * SHP.Data[x].HeaderImage.cy))]; - - Decode3(Databuffer, ref SHP.Data[x].Databuffer, SHP.Data[x].HeaderImage.cx, SHP.Data[x].HeaderImage.cy, ref ImageSize); - - k = 0; - l = 0; - for (int i = 0; i < Height; i++) - { - erri = i; - for (int j = SHP.Data[x].HeaderImage.x; j < Width; j++) - { - errj = j; - errl = l; - errk = k; - arroff = i + j + l; - - if (((j + 1) > (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x)) || ((i + 1) > (SHP.Data[x].HeaderImage.cy))) - cp = 0; - else - cp = SHP.Data[x].Databuffer[i + (j - SHP.Data[x].HeaderImage.x) + l]; - - FData[i + j + k] = cp; - - if (j == (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x - 1)) - l = l + (SHP.Data[x].HeaderImage.cx - 1); - - if (j == (Width - 1)) - k = k + (Width - 1); - } - } - //FData = headers[x - 1].Image; - k = 0; - for (int i = 0; i < (Height - SHP.Data[x].HeaderImage.y); i++) - { - for (int j = 0; j < Width; j++) - { - headers[x - 1].Image[i + j + k + (Width * SHP.Data[x].HeaderImage.y)] = FData[i + j + k]; - if (j == (Width - 1)) - { - k = k + (Width - 1); - } - } - } - } + f.Image[k++] = b; } - else if ((SHP.Data[x].HeaderImage.compression == 2)) - { - NextOffset = FindNextOffsetFrom(SHP, x + 1, SHP.Header.NumImages); - if (NextOffset != 0) - { - ImageSize = NextOffset - SHP.Data[x].HeaderImage.offset; - SHP.Data[x].Databuffer = new byte[(SHP.Data[x].HeaderImage.cx * SHP.Data[x].HeaderImage.cy)]; - Databuffer = new byte[ImageSize]; - for (int i = 0; i < ImageSize; i++) - { - s.Seek(SHP.Data[x].HeaderImage.offset + i, SeekOrigin.Begin); - Databuffer[i] = s.ReadUInt8(); - } - - Decode2(Databuffer, ref SHP.Data[x].Databuffer, SHP.Data[x].HeaderImage.cx, SHP.Data[x].HeaderImage.cy, ref ImageSize); - - k = 0; - l = 0; - for (int i = 0; i < Height; i++) - { - erri = i; - for (int j = SHP.Data[x].HeaderImage.x; j < Width; j++) - { - errj = j; - errl = l; - errk = k; - arroff = i + j + l; - - if (((j + 1) > (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x)) || ((i + 1) > (SHP.Data[x].HeaderImage.cy))) - cp = 0; - else - cp = SHP.Data[x].Databuffer[i + (j - SHP.Data[x].HeaderImage.x) + l]; - - FData[i + j + k] = cp; - - if (j == (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x - 1)) - l = l + (SHP.Data[x].HeaderImage.cx - 1); - - if (j == (Width - 1)) - k = k + (Width - 1); - } - } - //FData = headers[x - 1].Image; - k = 0; - for (int i = 0; i < (Height - SHP.Data[x].HeaderImage.y); i++) - { - for (int j = 0; j < Width; j++) - { - headers[x - 1].Image[i + j + k + (Width * SHP.Data[x].HeaderImage.y)] = FData[i + j + k]; - if (j == (Width - 1)) - { - k = k + (Width - 1); - } - } - } - - // Compression 2 - } - else - { - ImageSize = 0; - ImageSize = FileSize - SHP.Data[x].HeaderImage.offset; - Databuffer = new byte[ImageSize]; - for (int i = 0; i < ImageSize; i++) - { - s.Seek(SHP.Data[x].HeaderImage.offset + i, SeekOrigin.Begin); - Databuffer[i] = s.ReadUInt8(); - } - SHP.Data[x].Databuffer = new byte[((SHP.Data[x].HeaderImage.cx * SHP.Data[x].HeaderImage.cy))]; - Decode2(Databuffer, ref SHP.Data[x].Databuffer, SHP.Data[x].HeaderImage.cx, SHP.Data[x].HeaderImage.cy, ref ImageSize); - - k = 0; - l = 0; - for (int i = 0; i < Height; i++) - { - erri = i; - for (int j = SHP.Data[x].HeaderImage.x; j < Width; j++) - { - errj = j; - errl = l; - errk = k; - arroff = i + j + l; - - if (((j + 1) > (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x)) || ((i + 1) > (SHP.Data[x].HeaderImage.cy))) - cp = 0; - else - cp = SHP.Data[x].Databuffer[i + (j - SHP.Data[x].HeaderImage.x) + l]; - - FData[i + j + k] = cp; - - if (j == (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x - 1)) - l = l + (SHP.Data[x].HeaderImage.cx - 1); - - if (j == (Width - 1)) - k = k + (Width - 1); - } - } - //FData = headers[x - 1].Image; - k = 0; - for (int i = 0; i < (Height - SHP.Data[x].HeaderImage.y); i++) - { - for (int j = 0; j < Width; j++) - { - headers[x - 1].Image[i + j + k + (Width * SHP.Data[x].HeaderImage.y)] = FData[i + j + k]; - if (j == (Width - 1)) - { - k = k + (Width - 1); - } - } - } - // Compression 2 - } - } - else - { - // Compression 1 - ImageSize = (int)(SHP.Data[x].HeaderImage.cx * SHP.Data[x].HeaderImage.cy); - Databuffer = new byte[ImageSize]; - for (int i = 0; i < ImageSize; i++) - { - s.Seek(SHP.Data[x].HeaderImage.offset + i, SeekOrigin.Begin); - Databuffer[i] = s.ReadUInt8(); - } - SHP.Data[x].Databuffer = new byte[(SHP.Data[x].HeaderImage.cx * SHP.Data[x].HeaderImage.cy)]; - SHP.Data[x].Databuffer = Databuffer; - - k = 0; - l = 0; - for (int i = 0; i < Height; i++) - { - erri = i; - for (int j = SHP.Data[x].HeaderImage.x; j < Width; j++) - { - errj = j; - errl = l; - errk = k; - arroff = i + j + l; - - if (((j + 1) > (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x)) || ((i + 1) > (SHP.Data[x].HeaderImage.cy))) - cp = 0; - else - cp = SHP.Data[x].Databuffer[i + (j - SHP.Data[x].HeaderImage.x) + l]; - - FData[i + j + k] = cp; - - if (j == (SHP.Data[x].HeaderImage.cx + SHP.Data[x].HeaderImage.x - 1)) - l = l + (SHP.Data[x].HeaderImage.cx - 1); - - if (j == (Width - 1)) - k = k + (Width - 1); - } - } - //FData = headers[x - 1].Image; - k = 0; - for (int i = 0; i < (Height - SHP.Data[x].HeaderImage.y); i++) - { - for (int j = 0; j < Width; j++) - { - headers[x - 1].Image[i + j + k + (Width * SHP.Data[x].HeaderImage.y)] = FData[i + j + k]; - if (j == (Width - 1)) - { - k = k + (Width - 1); - } - } - } - } - } - catch (Exception e) - { - Console.WriteLine(e.Message); } } - // Set the shp's databuffer to the result after decompression - } - //Width = Width2; - //Height = Height2; - } - - public HeaderImage this[int index] - { - get { return headers[index]; } - } - - public static void ReInterpretWordFromBytes(byte Byte1, byte Byte2, ref ushort FullValue) - { - FullValue = (ushort)((Byte2 * 256) + Byte1); - } - - public static void ReInterpretWordFromBytes(byte Byte1, byte Byte2, ref uint FullValue) - { - FullValue = (uint)((Byte2 * 256) + Byte1); - } - - // Compression 3: - public static void Decode3(byte[] Source, ref byte[] Dest, int cx, int cy, ref int max) - { - int SP; - int DP; - int x; - int y; - int Count; - int v; - int maxdp; - ushort Pos; - maxdp = cx * cy; - SP = 0; - DP = 0; - Pos = 0; - try - { - for (y = 1; y <= cy; y++) - { - ReInterpretWordFromBytes(Source[SP], Source[SP + 1], ref Pos); - - Count = Pos - 2; - - SP = SP + 2; - - x = 0; - while (Count > 0) - { - Count = Count - 1; - if ((SP > max) || (DP > maxdp)) - { - break; - } - else - { - // SP has reached max value, exit - v = Source[SP]; - SP++; - if (v != 0) - { - if ((SP > max) || (DP > maxdp)) - { - break; - } - else - { - x++; - Dest[DP] += (byte)v; - } - DP++; - } - else - { - Count -= 1; - v = Source[SP]; - - SP++; - if ((x + v) > cx) - { - v = cx - x; - } - x = x + v; - while (v > 0) - { - if ((SP > max) || (DP > maxdp)) - { - break; - } - else - { - v -= 1; - Dest[DP] = 0; - - } - DP++; - // SP has reached max value, exit - } - } - } - } - if ((SP >= max) || (DP >= maxdp)) - { - return; - } - // SP has reached max value, exit - } - } - - catch (Exception e) - { - Console.WriteLine(e.Message); - } - - } - - public static void Decode2(byte[] Source, ref byte[] Dest, int cx, int cy, ref int max) - { - int SP; - int DP; - int y; - int Count; - int maxdp; - ushort Pos; - maxdp = cx * cy; - SP = 0; - DP = 0; - Pos = 0; - try - { - for (y = 1; y <= cy; y++) - { - ReInterpretWordFromBytes(Source[SP], Source[SP + 1], ref Pos); - Count = Pos - 2; - SP += 2; - while (Count > 0) - { - Count -= 1; - if ((SP > max) || (DP > maxdp)) - { - return; - } - // SP has reached max value, exit - Dest[DP] = Source[SP]; - SP++; - DP++; - } - if ((SP >= max) || (DP >= maxdp)) - { - return; - } - // SP has reached max value, exit - } - } - catch (Exception e) - { - Console.WriteLine(e.Message); } } - - public IEnumerator GetEnumerator() - { - return headers.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public Size Size { get { return new Size(Width, Height); } } } -} +} \ No newline at end of file diff --git a/OpenRA.Game/Graphics/SpriteLoader.cs b/OpenRA.Game/Graphics/SpriteLoader.cs index 34343340c4..c046f4f985 100644 --- a/OpenRA.Game/Graphics/SpriteLoader.cs +++ b/OpenRA.Game/Graphics/SpriteLoader.cs @@ -44,7 +44,7 @@ namespace OpenRA.Graphics if (ImageCount == 0) { var shp = new ShpTSReader(FileSystem.OpenWithExts(filename, exts)); - return shp.Select(a => SheetBuilder.Add(a.Image, shp.Size)).ToArray(); + return shp.Frames.Select(a => SheetBuilder.Add(a.Image, a.Size, a.Offset)).ToArray(); } else {