diff --git a/Makefile b/Makefile
index 706f87adba..08505885fd 100644
--- a/Makefile
+++ b/Makefile
@@ -166,7 +166,7 @@ mod_ts: $(mod_ts_TARGET)
editor_SRCS := $(shell find OpenRA.Editor/ -iname '*.cs')
editor_TARGET = OpenRA.Editor.exe
editor_KIND = winexe
-editor_DEPS = $(game_TARGET)
+editor_DEPS = $(game_TARGET) $(mod_common_TARGET)
editor_LIBS = System.Windows.Forms.dll System.Data.dll System.Drawing.dll $(editor_DEPS) thirdparty/Eluant.dll
editor_EXTRA = -resource:OpenRA.Editor.Form1.resources -resource:OpenRA.Editor.MapSelect.resources
editor_FLAGS = -win32icon:OpenRA.Editor/OpenRA.Editor.Icon.ico
diff --git a/OpenRA.Editor/OpenRA.Editor.csproj b/OpenRA.Editor/OpenRA.Editor.csproj
index d4ddf115d0..8c6687a4a5 100644
--- a/OpenRA.Editor/OpenRA.Editor.csproj
+++ b/OpenRA.Editor/OpenRA.Editor.csproj
@@ -160,6 +160,10 @@
{0DFB103F-2962-400F-8C6D-E2C28CCBA633}
OpenRA.Game
+
+ {FE6C8CC0-2F07-442A-B29F-17617B3B7FC6}
+ OpenRA.Mods.Common
+
diff --git a/OpenRA.Editor/RenderUtils.cs b/OpenRA.Editor/RenderUtils.cs
index a3497a882f..ca8bb84eb7 100644
--- a/OpenRA.Editor/RenderUtils.cs
+++ b/OpenRA.Editor/RenderUtils.cs
@@ -14,13 +14,14 @@ using System.Linq;
using OpenRA.FileFormats;
using OpenRA.FileSystem;
using OpenRA.Graphics;
+using OpenRA.Mods.Common.SpriteLoaders;
using OpenRA.Traits;
namespace OpenRA.Editor
{
static class RenderUtils
{
- static Bitmap RenderShp(ISpriteSource shp, IPalette p)
+ static Bitmap RenderShp(ShpTDSprite shp, IPalette p)
{
var frame = shp.Frames.First();
@@ -50,14 +51,14 @@ namespace OpenRA.Editor
var image = info.Traits.Get().EditorImage(info);
using (var s = GlobalFileSystem.OpenWithExts(image, tileset.Extensions))
{
- var shp = new ShpReader(s);
+ var shp = new ShpTDSprite(s);
var bitmap = RenderShp(shp, p);
try
{
using (var s2 = GlobalFileSystem.OpenWithExts(image + "2", tileset.Extensions))
{
- var shp2 = new ShpReader(s2);
+ var shp2 = new ShpTDSprite(s2);
var roofBitmap = RenderShp(shp2, p);
using (var g = System.Drawing.Graphics.FromImage(bitmap))
@@ -81,7 +82,7 @@ namespace OpenRA.Editor
using (var s = GlobalFileSystem.OpenWithExts(image, exts))
{
// TODO: Do this properly
- var shp = new ShpReader(s) as ISpriteSource;
+ var shp = new ShpTDSprite(s);
var frame = shp.Frames.Last();
var bitmap = new Bitmap(frame.Size.Width, frame.Size.Height, PixelFormat.Format8bppIndexed);
diff --git a/OpenRA.Editor/Surface.cs b/OpenRA.Editor/Surface.cs
index 20b317ee2b..e801a283aa 100644
--- a/OpenRA.Editor/Surface.cs
+++ b/OpenRA.Editor/Surface.cs
@@ -271,18 +271,19 @@ namespace OpenRA.Editor
for (var i = 0; i < ChunkSize; i++)
for (var j = 0; j < ChunkSize; j++)
{
- var cell = new CPos(u * ChunkSize + i, v * ChunkSize + j);
- var tr = Map.MapTiles.Value[cell];
+ var ui = u * ChunkSize + i;
+ var vj = v * ChunkSize + j;
+ var tr = Map.MapTiles.Value[ui, vj];
var tile = TileSetRenderer.Data(tr.Type);
- var index = (tr.Index < tile.Count) ? tr.Index : (byte)0;
+ var index = (tr.Index < tile.Length) ? tr.Index : (byte)0;
var rawImage = tile[index];
for (var x = 0; x < TileSetRenderer.TileSize; x++)
for (var y = 0; y < TileSetRenderer.TileSize; y++)
p[(j * TileSetRenderer.TileSize + y) * stride + i * TileSetRenderer.TileSize + x] = Palette.GetColor(rawImage[x + TileSetRenderer.TileSize * y]).ToArgb();
- if (Map.MapResources.Value[cell].Type != 0)
+ if (Map.MapResources.Value[ui, vj].Type != 0)
{
- var resourceImage = ResourceTemplates[Map.MapResources.Value[cell].Type].Bitmap;
+ var resourceImage = ResourceTemplates[Map.MapResources.Value[ui, vj].Type].Bitmap;
var srcdata = resourceImage.LockBits(resourceImage.Bounds(),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
diff --git a/OpenRA.Editor/TileSetRenderer.cs b/OpenRA.Editor/TileSetRenderer.cs
index c1bcef7c46..c39b9b5cf4 100644
--- a/OpenRA.Editor/TileSetRenderer.cs
+++ b/OpenRA.Editor/TileSetRenderer.cs
@@ -22,7 +22,7 @@ namespace OpenRA.Editor
{
public readonly int TileSize;
public TileSet TileSet;
- Dictionary> templates;
+ Dictionary templates;
// Extract a square tile that the editor can render
byte[] ExtractSquareTile(ISpriteFrame frame)
@@ -44,42 +44,19 @@ namespace OpenRA.Editor
return data;
}
- List LoadTemplate(string filename, string[] exts, Dictionary sourceCache, int[] frames)
- {
- ISpriteSource source;
- if (!sourceCache.ContainsKey(filename))
- {
- using (var s = GlobalFileSystem.OpenWithExts(filename, exts))
- source = SpriteSource.LoadSpriteSource(s, filename);
-
- if (source.CacheWhenLoadingTileset)
- sourceCache.Add(filename, source);
- }
- else
- source = sourceCache[filename];
-
- if (frames != null)
- {
- var ret = new List();
- var srcFrames = source.Frames;
- foreach (var i in frames)
- ret.Add(ExtractSquareTile(srcFrames[i]));
-
- return ret;
- }
-
- return source.Frames.Select(f => ExtractSquareTile(f)).ToList();
- }
-
public TileSetRenderer(TileSet tileset, Size tileSize)
{
this.TileSet = tileset;
this.TileSize = Math.Min(tileSize.Width, tileSize.Height);
- templates = new Dictionary>();
- var sourceCache = new Dictionary();
- foreach (var t in TileSet.Templates)
- templates.Add(t.Key, LoadTemplate(t.Value.Image, tileset.Extensions, sourceCache, t.Value.Frames));
+ templates = new Dictionary();
+ var spriteLoader = new SpriteLoader(Game.modData.SpriteLoaders, tileset.Extensions, null);
+ foreach (var t in tileset.Templates)
+ {
+ var allFrames = spriteLoader.LoadAllFrames(t.Value.Image);
+ var frames = t.Value.Frames != null ? t.Value.Frames.Select(f => allFrames[f]).ToArray() : allFrames;
+ templates.Add(t.Value.Id, frames.Select(f => ExtractSquareTile(f)).ToArray());
+ }
}
public Bitmap RenderTemplate(ushort id, IPalette p)
@@ -125,7 +102,7 @@ namespace OpenRA.Editor
return bitmap;
}
- public List Data(ushort id)
+ public byte[][] Data(ushort id)
{
return templates[id];
}
diff --git a/OpenRA.Game/FileFormats/TmpRAReader.cs b/OpenRA.Game/FileFormats/TmpRAReader.cs
deleted file mode 100644
index 5b8f4b520e..0000000000
--- a/OpenRA.Game/FileFormats/TmpRAReader.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-#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.Drawing;
-using System.IO;
-using OpenRA.Graphics;
-
-namespace OpenRA.FileFormats
-{
- public class TmpRAReader : ISpriteSource
- {
- public IReadOnlyList Frames { get; private set; }
- public bool CacheWhenLoadingTileset { get { return false; } }
-
- public TmpRAReader(Stream s)
- {
- var width = s.ReadUInt16();
- var height = s.ReadUInt16();
- var size = new Size(width, height);
-
- s.Position += 12;
- var imgStart = s.ReadUInt32();
- s.Position += 8;
- var indexEnd = s.ReadInt32();
- s.Position += 4;
- var indexStart = s.ReadInt32();
-
- s.Position = indexStart;
- var count = indexEnd - indexStart;
- var tiles = new TmpTile[count];
- Frames = tiles.AsReadOnly();
- var tilesIndex = 0;
- foreach (var b in s.ReadBytes(count))
- {
- if (b != 255)
- {
- s.Position = imgStart + b * width * height;
- tiles[tilesIndex++] = new TmpTile(s.ReadBytes(width * height), size);
- }
- else
- tiles[tilesIndex++] = new TmpTile(null, size);
- }
- }
- }
-}
diff --git a/OpenRA.Game/FileFormats/TmpTDReader.cs b/OpenRA.Game/FileFormats/TmpTDReader.cs
deleted file mode 100644
index d0521460d2..0000000000
--- a/OpenRA.Game/FileFormats/TmpTDReader.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-#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.Drawing;
-using System.IO;
-using OpenRA.Graphics;
-
-namespace OpenRA.FileFormats
-{
- public class TmpTile : ISpriteFrame
- {
- public Size Size { get; private set; }
- public Size FrameSize { get; private set; }
- public float2 Offset { get { return float2.Zero; } }
- public byte[] Data { get; set; }
-
- public TmpTile(byte[] data, Size size)
- {
- FrameSize = size;
- Data = data;
-
- if (data == null)
- Data = new byte[0];
- else
- Size = size;
- }
- }
-
- public class TmpTDReader : ISpriteSource
- {
- public IReadOnlyList Frames { get; private set; }
- public bool CacheWhenLoadingTileset { get { return false; } }
-
- public TmpTDReader(Stream s)
- {
- var width = s.ReadUInt16();
- var height = s.ReadUInt16();
- var size = new Size(width, height);
-
- s.Position += 8;
- var imgStart = s.ReadUInt32();
- s.Position += 8;
- var indexEnd = s.ReadInt32();
- var indexStart = s.ReadInt32();
-
- s.Position = indexStart;
- var count = indexEnd - indexStart;
- var tiles = new TmpTile[count];
- Frames = tiles.AsReadOnly();
- var tilesIndex = 0;
- foreach (var b in s.ReadBytes(count))
- {
- if (b != 255)
- {
- s.Position = imgStart + b * width * height;
- tiles[tilesIndex++] = new TmpTile(s.ReadBytes(width * height), size);
- }
- else
- tiles[tilesIndex++] = new TmpTile(null, size);
- }
- }
- }
-}
diff --git a/OpenRA.Game/FileFormats/TmpTSReader.cs b/OpenRA.Game/FileFormats/TmpTSReader.cs
deleted file mode 100644
index d405e2d01d..0000000000
--- a/OpenRA.Game/FileFormats/TmpTSReader.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-#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.Collections.Generic;
-using System.Drawing;
-using System.IO;
-using OpenRA.Graphics;
-
-namespace OpenRA.FileFormats
-{
- public class TmpTSTile : 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 TmpTSTile(Stream s, Size size)
- {
- Size = size;
-
- // Ignore tile header for now
- s.Position += 52;
-
- Data = new byte[size.Width * size.Height];
-
- // Unpack tile data
- var width = 4;
- for (var i = 0; i < size.Height; i++)
- {
- var start = i * size.Width + (size.Width - width) / 2;
- for (var j = 0; j < width; j++)
- Data[start + j] = s.ReadUInt8();
-
- width += (i < size.Height / 2 - 1 ? 1 : -1) * 4;
- }
-
- // Ignore Z-data for now
- // Ignore extra data for now
- }
- }
-
- public class TmpTSReader : ISpriteSource
- {
- public IReadOnlyList Frames { get; private set; }
- public bool CacheWhenLoadingTileset { get { return false; } }
-
- public TmpTSReader(Stream s)
- {
- var templateWidth = s.ReadUInt32();
- var templateHeight = s.ReadUInt32();
- var tileWidth = s.ReadInt32();
- var tileHeight = s.ReadInt32();
- var size = new Size(tileWidth, tileHeight);
- var offsets = new uint[templateWidth * templateHeight];
- for (var i = 0; i < offsets.Length; i++)
- offsets[i] = s.ReadUInt32();
-
- var tiles = new List();
- for (var i = 0; i < offsets.Length; i++)
- {
- s.Position = offsets[i];
- tiles.Add(new TmpTSTile(s, size));
- }
-
- Frames = tiles.ToArray().AsReadOnly();
- }
- }
-}
diff --git a/OpenRA.Game/Graphics/CursorProvider.cs b/OpenRA.Game/Graphics/CursorProvider.cs
index 042e3164d2..28701e2fd4 100644
--- a/OpenRA.Game/Graphics/CursorProvider.cs
+++ b/OpenRA.Game/Graphics/CursorProvider.cs
@@ -45,7 +45,7 @@ namespace OpenRA.Graphics
foreach (var p in nodesDict["Palettes"].Nodes)
palette.AddPalette(p.Key, new ImmutablePalette(GlobalFileSystem.Open(p.Value.Value), shadowIndex), false);
- var spriteLoader = new SpriteLoader(new string[0], new SheetBuilder(SheetType.Indexed));
+ var spriteLoader = new SpriteLoader(modData.SpriteLoaders, new string[0], new SheetBuilder(SheetType.Indexed));
foreach (var s in nodesDict["Cursors"].Nodes)
LoadSequencesForCursor(spriteLoader, s.Key, s.Value);
spriteLoader.SheetBuilder.Current.ReleaseBuffer();
diff --git a/OpenRA.Game/Graphics/SequenceProvider.cs b/OpenRA.Game/Graphics/SequenceProvider.cs
index 2f3947e137..bed18baac0 100644
--- a/OpenRA.Game/Graphics/SequenceProvider.cs
+++ b/OpenRA.Game/Graphics/SequenceProvider.cs
@@ -79,7 +79,7 @@ namespace OpenRA.Graphics
{
this.modData = modData;
- spriteLoader = Exts.Lazy(() => new SpriteLoader(tileSet.Extensions, new SheetBuilder(SheetType.Indexed)));
+ spriteLoader = Exts.Lazy(() => new SpriteLoader(modData.SpriteLoaders, tileSet.Extensions, new SheetBuilder(SheetType.Indexed)));
}
public Sequences LoadSequences(Map map)
diff --git a/OpenRA.Game/Graphics/SpriteLoader.cs b/OpenRA.Game/Graphics/SpriteLoader.cs
index fe7c8781b5..d542a1ea89 100644
--- a/OpenRA.Game/Graphics/SpriteLoader.cs
+++ b/OpenRA.Game/Graphics/SpriteLoader.cs
@@ -8,35 +8,67 @@
*/
#endregion
+using System.Drawing;
+using System.IO;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Primitives;
namespace OpenRA.Graphics
{
+ public interface ISpriteLoader
+ {
+ bool TryParseSprite(Stream s, out ISpriteFrame[] frames);
+ }
+
+ public interface ISpriteFrame
+ {
+ Size Size { get; }
+ Size FrameSize { get; }
+ float2 Offset { get; }
+ byte[] Data { get; }
+ bool DisableExportPadding { get; }
+ }
+
public class SpriteLoader
{
public readonly SheetBuilder SheetBuilder;
+ readonly ISpriteLoader[] loaders;
readonly Cache sprites;
+ readonly Cache frames;
readonly string[] exts;
- public SpriteLoader(string[] exts, SheetBuilder sheetBuilder)
+ public SpriteLoader(ISpriteLoader[] loaders, string[] exts, SheetBuilder sheetBuilder)
{
+ this.loaders = loaders;
SheetBuilder = sheetBuilder;
// Include extension-less version
this.exts = exts.Append("").ToArray();
- sprites = new Cache(CacheSpriteFrames);
+ sprites = new Cache(CacheSprites);
+ frames = new Cache(CacheFrames);
}
- Sprite[] CacheSpriteFrames(string filename)
+ Sprite[] CacheSprites(string filename)
+ {
+ return frames[filename].Select(a => SheetBuilder.Add(a))
+ .ToArray();
+ }
+
+ ISpriteFrame[] CacheFrames(string filename)
{
using (var stream = GlobalFileSystem.OpenWithExts(filename, exts))
- return SpriteSource.LoadSpriteSource(stream, filename).Frames
- .Select(a => SheetBuilder.Add(a))
- .ToArray();
+ {
+ ISpriteFrame[] frames;
+ foreach (var loader in loaders)
+ if (loader.TryParseSprite(stream, out frames))
+ return frames;
+
+ throw new InvalidDataException(filename + " is not a valid sprite file");
+ }
}
public Sprite[] LoadAllSprites(string filename) { return sprites[filename]; }
+ public ISpriteFrame[] LoadAllFrames(string filename) { return frames[filename]; }
}
}
diff --git a/OpenRA.Game/Graphics/SpriteSource.cs b/OpenRA.Game/Graphics/SpriteSource.cs
deleted file mode 100644
index ae23aa5501..0000000000
--- a/OpenRA.Game/Graphics/SpriteSource.cs
+++ /dev/null
@@ -1,264 +0,0 @@
-#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.Drawing;
-using System.IO;
-using OpenRA.FileFormats;
-
-namespace OpenRA.Graphics
-{
- public interface ISpriteFrame
- {
- Size Size { get; }
- Size FrameSize { get; }
- float2 Offset { get; }
- byte[] Data { get; }
- }
-
- public interface ISpriteSource
- {
- IReadOnlyList Frames { get; }
- bool CacheWhenLoadingTileset { get; }
- }
-
- // TODO: Most of this should be moved into the format parsers themselves.
- public enum SpriteType { Unknown, ShpTD, ShpTS, ShpD2, TmpTD, TmpRA, TmpTS, R8 }
- public static class SpriteSource
- {
- static bool IsTmpRA(Stream s)
- {
- var start = s.Position;
-
- s.Position += 20;
- var a = s.ReadUInt32();
- s.Position += 2;
- var b = s.ReadUInt16();
-
- s.Position = start;
- return a == 0 && b == 0x2c73;
- }
-
- static bool IsTmpTD(Stream s)
- {
- var start = s.Position;
-
- s.Position += 16;
- var a = s.ReadUInt32();
- var b = s.ReadUInt32();
-
- s.Position = start;
- return a == 0 && b == 0x0D1AFFFF;
- }
-
- static bool IsTmpTS(Stream s)
- {
- var start = s.Position;
- s.Position += 8;
- var sx = s.ReadUInt32();
- var sy = s.ReadUInt32();
-
- // Find the first frame
- var offset = s.ReadUInt32();
-
- if (offset > s.Length - 52)
- {
- s.Position = start;
- return false;
- }
-
- s.Position = offset + 12;
- var test = s.ReadUInt32();
-
- s.Position = start;
- 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 IsShpTD(Stream s)
- {
- var start = s.Position;
-
- // First word is the image count
- var imageCount = s.ReadUInt16();
- if (imageCount == 0)
- {
- s.Position = start;
- return false;
- }
-
- // Last offset should point to the end of file
- var finalOffset = start + 14 + 8 * imageCount;
- if (finalOffset > s.Length)
- {
- s.Position = start;
- return false;
- }
-
- s.Position = finalOffset;
- var eof = s.ReadUInt32();
- if (eof != s.Length)
- {
- s.Position = start;
- return false;
- }
-
- // Check the format flag on the first frame
- s.Position = start + 17;
- var b = s.ReadUInt8();
-
- s.Position = start;
- return b == 0x20 || b == 0x40 || b == 0x80;
- }
-
- static 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;
- }
-
- static bool IsR8(Stream s)
- {
- var start = s.Position;
-
- // First byte is nonzero
- if (s.ReadUInt8() == 0)
- {
- s.Position = start;
- return false;
- }
-
- // Check the format of the first frame
- s.Position = start + 25;
- var d = s.ReadUInt8();
-
- s.Position = start;
- return d == 8;
- }
-
- public static SpriteType DetectSpriteType(Stream s)
- {
- if (IsShpTD(s))
- return SpriteType.ShpTD;
-
- if (IsShpTS(s))
- return SpriteType.ShpTS;
-
- if (IsR8(s))
- return SpriteType.R8;
-
- if (IsTmpRA(s))
- return SpriteType.TmpRA;
-
- if (IsTmpTD(s))
- return SpriteType.TmpTD;
-
- if (IsTmpTS(s))
- return SpriteType.TmpTS;
-
- if (IsShpD2(s))
- return SpriteType.ShpD2;
-
- return SpriteType.Unknown;
- }
-
- public static ISpriteSource LoadSpriteSource(Stream s, string filename)
- {
- var type = DetectSpriteType(s);
- switch (type)
- {
- case SpriteType.ShpTD:
- return new ShpReader(s);
- case SpriteType.ShpTS:
- return new ShpTSReader(s);
- case SpriteType.R8:
- return new R8Reader(s);
- case SpriteType.TmpRA:
- return new TmpRAReader(s);
- case SpriteType.TmpTD:
- return new TmpTDReader(s);
- case SpriteType.TmpTS:
- return new TmpTSReader(s);
- case SpriteType.ShpD2:
- return new ShpD2Reader(s);
- case SpriteType.Unknown:
- default:
- throw new InvalidDataException(filename + " is not a valid sprite file");
- }
- }
- }
-}
diff --git a/OpenRA.Game/Graphics/Theater.cs b/OpenRA.Game/Graphics/Theater.cs
index e3b962160e..110b27703a 100644
--- a/OpenRA.Game/Graphics/Theater.cs
+++ b/OpenRA.Game/Graphics/Theater.cs
@@ -22,33 +22,6 @@ namespace OpenRA.Graphics
Dictionary templates;
Sprite missingTile;
- Sprite[] LoadTemplate(string filename, string[] exts, Dictionary sourceCache, int[] frames)
- {
- ISpriteSource source;
- if (!sourceCache.ContainsKey(filename))
- {
- using (var s = GlobalFileSystem.OpenWithExts(filename, exts))
- source = SpriteSource.LoadSpriteSource(s, filename);
-
- if (source.CacheWhenLoadingTileset)
- sourceCache.Add(filename, source);
- }
- else
- source = sourceCache[filename];
-
- if (frames != null)
- {
- var ret = new List();
- var srcFrames = source.Frames.ToArray();
- foreach (var i in frames)
- ret.Add(sheetBuilder.Add(srcFrames[i]));
-
- return ret.ToArray();
- }
-
- return source.Frames.Select(f => sheetBuilder.Add(f)).ToArray();
- }
-
public Theater(TileSet tileset)
{
var allocated = false;
@@ -61,11 +34,17 @@ namespace OpenRA.Graphics
return new Sheet(new Size(tileset.SheetSize, tileset.SheetSize), true);
};
- var sourceCache = new Dictionary();
- templates = new Dictionary();
sheetBuilder = new SheetBuilder(SheetType.Indexed, allocate);
+ templates = new Dictionary();
+
+ // We manage the SheetBuilder ourselves, to avoid loading all of the tileset images
+ var spriteLoader = new SpriteLoader(Game.modData.SpriteLoaders, tileset.Extensions, null);
foreach (var t in tileset.Templates)
- templates.Add(t.Value.Id, LoadTemplate(t.Value.Image, tileset.Extensions, sourceCache, t.Value.Frames));
+ {
+ var allFrames = spriteLoader.LoadAllFrames(t.Value.Image);
+ var frames = t.Value.Frames != null ? t.Value.Frames.Select(f => allFrames[f]).ToArray() : allFrames;
+ templates.Add(t.Value.Id, frames.Select(f => sheetBuilder.Add(f)).ToArray());
+ }
// 1x1px transparent tile
missingTile = sheetBuilder.Add(new byte[1], new Size(1, 1));
diff --git a/OpenRA.Game/Manifest.cs b/OpenRA.Game/Manifest.cs
index 282ae968d1..12fc57a47f 100644
--- a/OpenRA.Game/Manifest.cs
+++ b/OpenRA.Game/Manifest.cs
@@ -37,6 +37,8 @@ namespace OpenRA
public readonly Size TileSize = new Size(24, 24);
public readonly TileShape TileShape = TileShape.Rectangle;
+ public readonly string[] SpriteFormats = { };
+
[Desc("(x,y,z) offset of the full cell and each sub-cell", "X & Y should be between -512 ... 512 and Z >= 0")]
public readonly WVec[] SubCellOffsets =
{
@@ -128,6 +130,9 @@ namespace OpenRA
compat.Add(c.Trim());
MapCompatibility = compat.ToArray();
+
+ if (yaml.ContainsKey("SpriteFormats"))
+ SpriteFormats = FieldLoader.GetValue("SpriteFormats", yaml["SpriteFormats"].Value);
}
static string[] YamlList(Dictionary yaml, string key)
diff --git a/OpenRA.Game/Map/Map.cs b/OpenRA.Game/Map/Map.cs
index 311b4ffc02..30b8751024 100644
--- a/OpenRA.Game/Map/Map.cs
+++ b/OpenRA.Game/Map/Map.cs
@@ -457,7 +457,7 @@ namespace OpenRA
for (var i = 0; i < MapSize.X; i++)
for (var j = 0; j < MapSize.Y; j++)
{
- var tile = MapTiles.Value[new CPos(i, j)];
+ var tile = MapTiles.Value[i, j];
writer.Write(tile.Type);
writer.Write(tile.Index);
}
@@ -467,7 +467,7 @@ namespace OpenRA
{
for (var j = 0; j < MapSize.Y; j++)
{
- var tile = MapResources.Value[new CPos(i, j)];
+ var tile = MapResources.Value[i, j];
writer.Write(tile.Type);
writer.Write(tile.Index);
}
@@ -654,9 +654,8 @@ namespace OpenRA
{
for (var i = Bounds.Left; i < Bounds.Right; i++)
{
- var cell = new CPos(i, j);
- var type = MapTiles.Value[cell].Type;
- var index = MapTiles.Value[cell].Index;
+ var type = MapTiles.Value[i, j].Type;
+ var index = MapTiles.Value[i, j].Index;
if (!tileset.Templates.ContainsKey(type))
{
Console.WriteLine("Unknown Tile ID {0}".F(type));
@@ -668,7 +667,7 @@ namespace OpenRA
continue;
index = (byte)r.Next(0, template.TilesCount);
- MapTiles.Value[cell] = new TerrainTile(type, index);
+ MapTiles.Value[i, j] = new TerrainTile(type, index);
}
}
}
@@ -730,7 +729,7 @@ namespace OpenRA
for (var j = -max; j <= max; j++)
for (var i = -max; i <= max; i++)
if (max * max >= i * i + j * j)
- ts [Exts.ISqrt(i * i + j * j, Exts.ISqrtRoundMode.Ceiling)].Add(new CVec(i, j));
+ ts[Exts.ISqrt(i * i + j * j, Exts.ISqrtRoundMode.Ceiling)].Add(new CVec(i, j));
// Sort each integer-distance group by the actual distance
foreach (var list in ts)
diff --git a/OpenRA.Game/ModData.cs b/OpenRA.Game/ModData.cs
index c5b5fc3a0c..288d9ac836 100644
--- a/OpenRA.Game/ModData.cs
+++ b/OpenRA.Game/ModData.cs
@@ -24,6 +24,7 @@ namespace OpenRA
public readonly ObjectCreator ObjectCreator;
public readonly WidgetLoader WidgetLoader;
public readonly MapCache MapCache;
+ public readonly ISpriteLoader[] SpriteLoaders;
public ILoadScreen LoadScreen = null;
public VoxelLoader VoxelLoader;
public readonly RulesetCache RulesetCache;
@@ -45,6 +46,18 @@ namespace OpenRA
RulesetCache.LoadingProgress += HandleLoadingProgress;
MapCache = new MapCache(this);
+ var loaders = new List();
+ foreach (var format in Manifest.SpriteFormats)
+ {
+ var loader = ObjectCreator.FindType(format + "Loader");
+ if (loader == null || !loader.GetInterfaces().Contains(typeof(ISpriteLoader)))
+ throw new InvalidOperationException("Unable to find a sprite loader for type '{0}'.".F(format));
+
+ loaders.Add((ISpriteLoader)ObjectCreator.CreateBasic(loader));
+ }
+
+ SpriteLoaders = loaders.ToArray();
+
// HACK: Mount only local folders so we have a half-working environment for the asset installer
GlobalFileSystem.UnmountAll();
foreach (var dir in Manifest.Folders)
diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj
index e675c54864..6eb94aca68 100644
--- a/OpenRA.Game/OpenRA.Game.csproj
+++ b/OpenRA.Game/OpenRA.Game.csproj
@@ -277,13 +277,6 @@
-
-
-
-
-
-
-
@@ -320,7 +313,6 @@
-
diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
index 25a741cc5d..68c487a53d 100644
--- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
+++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
@@ -87,7 +87,6 @@
-
@@ -114,6 +113,11 @@
+
+
+
+
+
diff --git a/OpenRA.Game/FileFormats/ShpD2Reader.cs b/OpenRA.Mods.Common/SpriteLoaders/ShpD2Loader.cs
similarity index 61%
rename from OpenRA.Game/FileFormats/ShpD2Reader.cs
rename to OpenRA.Mods.Common/SpriteLoaders/ShpD2Loader.cs
index dca7a888d6..c5567cefec 100644
--- a/OpenRA.Game/FileFormats/ShpD2Reader.cs
+++ b/OpenRA.Mods.Common/SpriteLoaders/ShpD2Loader.cs
@@ -9,13 +9,16 @@
#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 ShpD2Reader : ISpriteSource
+ public class ShpD2Loader : ISpriteLoader
{
[Flags] enum FormatFlags : int
{
@@ -24,14 +27,15 @@ namespace OpenRA.FileFormats
VariableLengthTable = 4
}
- class Frame : ISpriteFrame
+ 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 Frame(Stream s)
+ public ShpD2Frame(Stream s)
{
var flags = (FormatFlags)s.ReadUInt16();
s.Position += 1;
@@ -83,11 +87,48 @@ namespace OpenRA.FileFormats
}
}
- public IReadOnlyList Frames { get; private set; }
- public bool CacheWhenLoadingTileset { get { return false; } }
-
- public ShpD2Reader(Stream s)
+ 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.
@@ -101,13 +142,27 @@ namespace OpenRA.FileFormats
for (var i = 0; i < imageCount + 1; i++)
offsets[i] = (twoByteOffset ? s.ReadUInt16() : s.ReadUInt32()) + 2;
- var frames = new Frame[imageCount];
- Frames = frames.AsReadOnly();
+ var frames = new ShpD2Frame[imageCount];
for (var i = 0; i < frames.Length; i++)
{
s.Position = offsets[i];
- frames[i] = new Frame(s);
+ 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;
}
}
}
diff --git a/OpenRA.Game/FileFormats/ShpReader.cs b/OpenRA.Mods.Common/SpriteLoaders/ShpTDLoader.cs
similarity index 79%
rename from OpenRA.Game/FileFormats/ShpReader.cs
rename to OpenRA.Mods.Common/SpriteLoaders/ShpTDLoader.cs
index 0d7004cca5..055375889a 100644
--- a/OpenRA.Game/FileFormats/ShpReader.cs
+++ b/OpenRA.Mods.Common/SpriteLoaders/ShpTDLoader.cs
@@ -13,11 +13,63 @@ 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 ShpReader : ISpriteSource
+ public class ShpTDLoader : ISpriteLoader
+ {
+ static bool IsShpTD(Stream s)
+ {
+ var start = s.Position;
+
+ // First word is the image count
+ var imageCount = s.ReadUInt16();
+ if (imageCount == 0)
+ {
+ s.Position = start;
+ return false;
+ }
+
+ // Last offset should point to the end of file
+ var finalOffset = start + 14 + 8 * imageCount;
+ if (finalOffset > s.Length)
+ {
+ s.Position = start;
+ return false;
+ }
+
+ s.Position = finalOffset;
+ var eof = s.ReadUInt32();
+ if (eof != s.Length)
+ {
+ s.Position = start;
+ return false;
+ }
+
+ // Check the format flag on the first frame
+ s.Position = start + 17;
+ var b = s.ReadUInt8();
+
+ s.Position = start;
+ return b == 0x20 || b == 0x40 || b == 0x80;
+ }
+
+ public bool TryParseSprite(Stream s, out ISpriteFrame[] frames)
+ {
+ if (!IsShpTD(s))
+ {
+ frames = null;
+ return false;
+ }
+
+ frames = new ShpTDSprite(s).Frames.ToArray();
+ return true;
+ }
+ }
+
+ public class ShpTDSprite
{
enum Format { Format20 = 0x20, Format40 = 0x40, Format80 = 0x80 }
@@ -27,6 +79,7 @@ namespace OpenRA.FileFormats
public Size FrameSize { get { return reader.Size; } }
public float2 Offset { get { return float2.Zero; } }
public byte[] Data { get; set; }
+ public bool DisableExportPadding { get { return false; } }
public uint FileOffset;
public Format Format;
@@ -35,12 +88,12 @@ namespace OpenRA.FileFormats
public Format RefFormat;
public ImageHeader RefImage;
- ShpReader reader;
+ ShpTDSprite reader;
// Used by ShpWriter
public ImageHeader() { }
- public ImageHeader(Stream stream, ShpReader reader)
+ public ImageHeader(Stream stream, ShpTDSprite reader)
{
this.reader = reader;
var data = stream.ReadUInt32();
@@ -60,7 +113,6 @@ namespace OpenRA.FileFormats
}
public IReadOnlyList Frames { get; private set; }
- public bool CacheWhenLoadingTileset { get { return false; } }
public readonly Size Size;
int recurseDepth = 0;
@@ -69,7 +121,7 @@ namespace OpenRA.FileFormats
readonly long shpBytesFileOffset;
readonly byte[] shpBytes;
- public ShpReader(Stream stream)
+ public ShpTDSprite(Stream stream)
{
imageCount = stream.ReadUInt16();
stream.Position += 4;
@@ -149,12 +201,6 @@ namespace OpenRA.FileFormats
return imageData;
}
- public static ShpReader Load(string filename)
- {
- using (var s = File.OpenRead(filename))
- return new ShpReader(s);
- }
-
public static void Write(Stream s, Size size, IEnumerable frames)
{
var compressedFrames = frames.Select(f => Format80.Encode(f)).ToArray();
diff --git a/OpenRA.Game/FileFormats/ShpTSReader.cs b/OpenRA.Mods.Common/SpriteLoaders/ShpTSLoader.cs
similarity index 51%
rename from OpenRA.Game/FileFormats/ShpTSReader.cs
rename to OpenRA.Mods.Common/SpriteLoaders/ShpTSLoader.cs
index 661a2790f4..6471be0aab 100644
--- a/OpenRA.Game/FileFormats/ShpTSReader.cs
+++ b/OpenRA.Mods.Common/SpriteLoaders/ShpTSLoader.cs
@@ -8,25 +8,30 @@
*/
#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; }
public float2 Offset { get; private set; }
public byte[] Data { get; set; }
+ public bool DisableExportPadding { get { return false; } }
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();
@@ -44,21 +49,56 @@ namespace OpenRA.FileFormats
}
}
- public IReadOnlyList Frames { get; private set; }
- public bool CacheWhenLoadingTileset { get { return false; } }
-
- 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/OpenRA.Mods.Common/SpriteLoaders/TmpRALoader.cs b/OpenRA.Mods.Common/SpriteLoaders/TmpRALoader.cs
new file mode 100644
index 0000000000..900c3fdd0c
--- /dev/null
+++ b/OpenRA.Mods.Common/SpriteLoaders/TmpRALoader.cs
@@ -0,0 +1,102 @@
+#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 TmpRALoader : ISpriteLoader
+ {
+ class TmpRAFrame : ISpriteFrame
+ {
+ public Size Size { get; private set; }
+ public Size FrameSize { get; private set; }
+ public float2 Offset { get { return float2.Zero; } }
+ public byte[] Data { get; set; }
+ public bool DisableExportPadding { get { return false; } }
+
+ public TmpRAFrame(byte[] data, Size size)
+ {
+ FrameSize = size;
+ Data = data;
+
+ if (data == null)
+ Data = new byte[0];
+ else
+ Size = size;
+ }
+ }
+
+ bool IsTmpRA(Stream s)
+ {
+ var start = s.Position;
+
+ s.Position += 20;
+ var a = s.ReadUInt32();
+ s.Position += 2;
+ var b = s.ReadUInt16();
+
+ s.Position = start;
+ return a == 0 && b == 0x2c73;
+ }
+
+ TmpRAFrame[] ParseFrames(Stream s)
+ {
+ var start = s.Position;
+ var width = s.ReadUInt16();
+ var height = s.ReadUInt16();
+ var size = new Size(width, height);
+
+ s.Position += 12;
+ var imgStart = s.ReadUInt32();
+ s.Position += 8;
+ var indexEnd = s.ReadInt32();
+ s.Position += 4;
+ var indexStart = s.ReadInt32();
+
+ s.Position = indexStart;
+ var count = indexEnd - indexStart;
+ var tiles = new TmpRAFrame[count];
+
+ var tilesIndex = 0;
+ foreach (var b in s.ReadBytes(count))
+ {
+ if (b != 255)
+ {
+ s.Position = imgStart + b * width * height;
+ tiles[tilesIndex++] = new TmpRAFrame(s.ReadBytes(width * height), size);
+ }
+ else
+ tiles[tilesIndex++] = new TmpRAFrame(null, size);
+ }
+
+ s.Position = start;
+ return tiles;
+ }
+
+ public bool TryParseSprite(Stream s, out ISpriteFrame[] frames)
+ {
+ if (!IsTmpRA(s))
+ {
+ frames = null;
+ return false;
+ }
+
+ frames = ParseFrames(s);
+ return true;
+ }
+ }
+}
diff --git a/OpenRA.Mods.Common/SpriteLoaders/TmpTDLoader.cs b/OpenRA.Mods.Common/SpriteLoaders/TmpTDLoader.cs
new file mode 100644
index 0000000000..22617faecf
--- /dev/null
+++ b/OpenRA.Mods.Common/SpriteLoaders/TmpTDLoader.cs
@@ -0,0 +1,99 @@
+#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 TmpTDLoader : ISpriteLoader
+ {
+ class TmpTDFrame : ISpriteFrame
+ {
+ public Size Size { get; private set; }
+ public Size FrameSize { get; private set; }
+ public float2 Offset { get { return float2.Zero; } }
+ public byte[] Data { get; set; }
+ public bool DisableExportPadding { get { return false; } }
+
+ public TmpTDFrame(byte[] data, Size size)
+ {
+ FrameSize = size;
+ Data = data;
+
+ if (data == null)
+ Data = new byte[0];
+ else
+ Size = size;
+ }
+ }
+
+ bool IsTmpTD(Stream s)
+ {
+ var start = s.Position;
+
+ s.Position += 16;
+ var a = s.ReadUInt32();
+ var b = s.ReadUInt32();
+
+ s.Position = start;
+ return a == 0 && b == 0x0D1AFFFF;
+ }
+
+ TmpTDFrame[] ParseFrames(Stream s)
+ {
+ var start = s.Position;
+ var width = s.ReadUInt16();
+ var height = s.ReadUInt16();
+ var size = new Size(width, height);
+
+ s.Position += 8;
+ var imgStart = s.ReadUInt32();
+ s.Position += 8;
+ var indexEnd = s.ReadInt32();
+ var indexStart = s.ReadInt32();
+
+ s.Position = indexStart;
+ var count = indexEnd - indexStart;
+ var tiles = new TmpTDFrame[count];
+ var tilesIndex = 0;
+ foreach (var b in s.ReadBytes(count))
+ {
+ if (b != 255)
+ {
+ s.Position = imgStart + b * width * height;
+ tiles[tilesIndex++] = new TmpTDFrame(s.ReadBytes(width * height), size);
+ }
+ else
+ tiles[tilesIndex++] = new TmpTDFrame(null, size);
+ }
+
+ s.Position = start;
+ return tiles;
+ }
+
+ public bool TryParseSprite(Stream s, out ISpriteFrame[] frames)
+ {
+ if (!IsTmpTD(s))
+ {
+ frames = null;
+ return false;
+ }
+
+ frames = ParseFrames(s);
+ return true;
+ }
+ }
+}
diff --git a/OpenRA.Mods.Common/UtilityCommands/ConvertPngToShpCommand.cs b/OpenRA.Mods.Common/UtilityCommands/ConvertPngToShpCommand.cs
index 4cef69cec1..d52b7fee69 100644
--- a/OpenRA.Mods.Common/UtilityCommands/ConvertPngToShpCommand.cs
+++ b/OpenRA.Mods.Common/UtilityCommands/ConvertPngToShpCommand.cs
@@ -17,6 +17,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using OpenRA.FileFormats;
+using OpenRA.Mods.Common.SpriteLoaders;
namespace OpenRA.Mods.Common.UtilityCommands
{
@@ -36,7 +37,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
throw new InvalidOperationException("All frames must be the same size");
using (var destStream = File.Create(dest))
- ShpReader.Write(destStream, size, frames.Select(f => ToBytes(f)));
+ ShpTDSprite.Write(destStream, size, frames.Select(f => ToBytes(f)));
Console.WriteLine(dest + " saved.");
}
diff --git a/OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs b/OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs
index 16fac878aa..f70338b257 100644
--- a/OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs
+++ b/OpenRA.Mods.Common/UtilityCommands/ConvertSpriteToPngCommand.cs
@@ -41,19 +41,17 @@ namespace OpenRA.Mods.Common.UtilityCommands
var palette = new ImmutablePalette(args[2], shadowIndex);
- ISpriteSource source;
- using (var stream = File.OpenRead(src))
- source = SpriteSource.LoadSpriteSource(stream, src);
+ var frames = new SpriteLoader(modData.SpriteLoaders, new string[0], null)
+ .LoadAllFrames(src);
- // The r8 padding requires external information that we can't access here.
- var usePadding = !(args.Contains("--nopadding") || source is R8Reader);
+ var usePadding = !args.Contains("--nopadding");
var count = 0;
var prefix = Path.GetFileNameWithoutExtension(src);
- foreach (var frame in source.Frames)
+ foreach (var frame in frames)
{
- var frameSize = usePadding ? frame.FrameSize : frame.Size;
- var offset = usePadding ? (frame.Offset - 0.5f * new float2(frame.Size - frame.FrameSize)).ToInt2() : int2.Zero;
+ var frameSize = usePadding && !frame.DisableExportPadding ? frame.FrameSize : frame.Size;
+ var offset = usePadding && !frame.DisableExportPadding ? (frame.Offset - 0.5f * new float2(frame.Size - frame.FrameSize)).ToInt2() : int2.Zero;
// shp(ts) may define empty frames
if (frameSize.Width == 0 && frameSize.Height == 0)
@@ -69,7 +67,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
// Clear the frame
- if (usePadding)
+ if (usePadding && !frame.DisableExportPadding)
{
var clearRow = new byte[data.Stride];
for (var i = 0; i < frameSize.Height; i++)
diff --git a/OpenRA.Mods.Common/UtilityCommands/RemapShpCommand.cs b/OpenRA.Mods.Common/UtilityCommands/RemapShpCommand.cs
index 59ee1885b6..1bffa57ff2 100644
--- a/OpenRA.Mods.Common/UtilityCommands/RemapShpCommand.cs
+++ b/OpenRA.Mods.Common/UtilityCommands/RemapShpCommand.cs
@@ -18,6 +18,7 @@ using OpenRA.Traits;
using OpenRA.Graphics;
using OpenRA.FileFormats;
using OpenRA.FileSystem;
+using OpenRA.Mods.Common.SpriteLoaders;
namespace OpenRA.Mods.Common.UtilityCommands
{
@@ -65,11 +66,13 @@ namespace OpenRA.Mods.Common.UtilityCommands
.Where(a => !remap.ContainsValue(a))
.MinBy(a => ColorDistance(destPalette[a], srcPalette[i]));
- var srcImage = ShpReader.Load(args[3]);
-
+ using (var s = File.OpenRead(args[3]))
using (var destStream = File.Create(args[4]))
- ShpReader.Write(destStream, srcImage.Size,
+ {
+ var srcImage = new ShpTDSprite(s);
+ ShpTDSprite.Write(destStream, srcImage.Size,
srcImage.Frames.Select(im => im.Data.Select(px => (byte)remap[px]).ToArray()));
+ }
}
static int ColorDistance(uint a, uint b)
diff --git a/OpenRA.Mods.Common/UtilityCommands/TransposeShpCommand.cs b/OpenRA.Mods.Common/UtilityCommands/TransposeShpCommand.cs
deleted file mode 100644
index 0f5f5be222..0000000000
--- a/OpenRA.Mods.Common/UtilityCommands/TransposeShpCommand.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-#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.IO;
-using System.Linq;
-using System.Text;
-using OpenRA.FileFormats;
-
-namespace OpenRA.Mods.Common.UtilityCommands
-{
- class TransposeShpCommand : IUtilityCommand
- {
- public string Name { get { return "--transpose"; } }
-
- [Desc("SRCSHP DESTSHP START N M [START N M ...]",
- "Transpose the N*M block of frames starting at START.")]
- public void Run(ModData modData, string[] args)
- {
- var srcImage = ShpReader.Load(args[1]);
-
- var srcFrames = srcImage.Frames;
- var destFrames = srcImage.Frames.ToArray();
-
- for (var z = 3; z < args.Length - 2; z += 3)
- {
- var start = Exts.ParseIntegerInvariant(args[z]);
- var m = Exts.ParseIntegerInvariant(args[z + 1]);
- var n = Exts.ParseIntegerInvariant(args[z + 2]);
-
- for (var i = 0; i < m; i++)
- for (var j = 0; j < n; j++)
- destFrames[start + i * n + j] = srcFrames[start + j * m + i];
- }
-
- using (var destStream = File.Create(args[2]))
- ShpReader.Write(destStream, srcImage.Size, destFrames.Select(f => f.Data));
- }
- }
-}
diff --git a/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj b/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj
index e547daf283..9dc1baf04c 100644
--- a/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj
+++ b/OpenRA.Mods.D2k/OpenRA.Mods.D2k.csproj
@@ -84,6 +84,7 @@
+
diff --git a/OpenRA.Game/FileFormats/R8Reader.cs b/OpenRA.Mods.D2k/R8Loader.cs
similarity index 64%
rename from OpenRA.Game/FileFormats/R8Reader.cs
rename to OpenRA.Mods.D2k/R8Loader.cs
index 0db3ff57ff..89ac184489 100644
--- a/OpenRA.Game/FileFormats/R8Reader.cs
+++ b/OpenRA.Mods.D2k/R8Loader.cs
@@ -1,30 +1,33 @@
#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
+ * 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.Graphics;
-namespace OpenRA.FileFormats
+namespace OpenRA.Mods.D2k.SpriteLoaders
{
- public class R8Reader : ISpriteSource
+ public class R8Loader : ISpriteLoader
{
- class R8Image : ISpriteFrame
+ class R8Frame : ISpriteFrame
{
public Size Size { get; private set; }
public Size FrameSize { get; private set; }
public float2 Offset { get; private set; }
public byte[] Data { get; set; }
+ public bool DisableExportPadding { get { return true; } }
- public R8Image(Stream s)
+ public R8Frame(Stream s)
{
// Scan forward until we find some data
var type = s.ReadUInt8();
@@ -61,20 +64,41 @@ namespace OpenRA.FileFormats
}
}
- public IReadOnlyList Frames { get; private set; }
- public bool CacheWhenLoadingTileset { get { return true; } }
-
- public readonly int ImageCount;
- public R8Reader(Stream stream)
+ bool IsR8(Stream s)
{
- var frames = new List();
- while (stream.Position < stream.Length)
+ var start = s.Position;
+
+ // First byte is nonzero
+ if (s.ReadUInt8() == 0)
{
- frames.Add(new R8Image(stream));
- ImageCount++;
+ s.Position = start;
+ return false;
}
- Frames = frames.ToArray().AsReadOnly();
+ // Check the format of the first frame
+ s.Position = start + 25;
+ var d = s.ReadUInt8();
+
+ s.Position = start;
+ return d == 8;
+ }
+
+ public bool TryParseSprite(Stream s, out ISpriteFrame[] frames)
+ {
+ if (!IsR8(s))
+ {
+ frames = null;
+ return false;
+ }
+
+ var start = s.Position;
+ var tmp = new List();
+ while (s.Position < s.Length)
+ tmp.Add(new R8Frame(s));
+ s.Position = start;
+
+ frames = tmp.ToArray();
+ return true;
}
}
}
diff --git a/OpenRA.Mods.TS/OpenRA.Mods.TS.csproj b/OpenRA.Mods.TS/OpenRA.Mods.TS.csproj
index 9650418c9d..4dc1e5f23c 100644
--- a/OpenRA.Mods.TS/OpenRA.Mods.TS.csproj
+++ b/OpenRA.Mods.TS/OpenRA.Mods.TS.csproj
@@ -56,6 +56,7 @@
+