Merge pull request #6673 from pchote/unhardcode-sprites

Move sprite parsers into mod code
This commit is contained in:
obrakmann
2014-10-07 20:45:50 +02:00
36 changed files with 695 additions and 682 deletions

View File

@@ -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

View File

@@ -160,6 +160,10 @@
<Project>{0DFB103F-2962-400F-8C6D-E2C28CCBA633}</Project>
<Name>OpenRA.Game</Name>
</ProjectReference>
<ProjectReference Include="..\OpenRA.Mods.Common\OpenRA.Mods.Common.csproj">
<Project>{FE6C8CC0-2F07-442A-B29F-17617B3B7FC6}</Project>
<Name>OpenRA.Mods.Common</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="OpenRA.Editor.Icon.ico" />

View File

@@ -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<ILegacyEditorRenderInfo>().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);

View File

@@ -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);

View File

@@ -22,7 +22,7 @@ namespace OpenRA.Editor
{
public readonly int TileSize;
public TileSet TileSet;
Dictionary<ushort, List<byte[]>> templates;
Dictionary<ushort, byte[][]> templates;
// Extract a square tile that the editor can render
byte[] ExtractSquareTile(ISpriteFrame frame)
@@ -44,42 +44,19 @@ namespace OpenRA.Editor
return data;
}
List<byte[]> LoadTemplate(string filename, string[] exts, Dictionary<string, ISpriteSource> 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<byte[]>();
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<ushort, List<byte[]>>();
var sourceCache = new Dictionary<string, ISpriteSource>();
foreach (var t in TileSet.Templates)
templates.Add(t.Key, LoadTemplate(t.Value.Image, tileset.Extensions, sourceCache, t.Value.Frames));
templates = new Dictionary<ushort, byte[][]>();
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<byte[]> Data(ushort id)
public byte[][] Data(ushort id)
{
return templates[id];
}

View File

@@ -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<ISpriteFrame> 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);
}
}
}
}

View File

@@ -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<ISpriteFrame> 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);
}
}
}
}

View File

@@ -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<ISpriteFrame> 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<TmpTSTile>();
for (var i = 0; i < offsets.Length; i++)
{
s.Position = offsets[i];
tiles.Add(new TmpTSTile(s, size));
}
Frames = tiles.ToArray().AsReadOnly();
}
}
}

View File

@@ -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();

View File

@@ -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)

View File

@@ -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<string, Sprite[]> sprites;
readonly Cache<string, ISpriteFrame[]> 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<string, Sprite[]>(CacheSpriteFrames);
sprites = new Cache<string, Sprite[]>(CacheSprites);
frames = new Cache<string, ISpriteFrame[]>(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]; }
}
}

View File

@@ -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<ISpriteFrame> 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");
}
}
}
}

View File

@@ -22,33 +22,6 @@ namespace OpenRA.Graphics
Dictionary<ushort, Sprite[]> templates;
Sprite missingTile;
Sprite[] LoadTemplate(string filename, string[] exts, Dictionary<string, ISpriteSource> 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<Sprite>();
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<string, ISpriteSource>();
templates = new Dictionary<ushort, Sprite[]>();
sheetBuilder = new SheetBuilder(SheetType.Indexed, allocate);
templates = new Dictionary<ushort, Sprite[]>();
// 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));

View File

@@ -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<string[]>("SpriteFormats", yaml["SpriteFormats"].Value);
}
static string[] YamlList(Dictionary<string, MiniYaml> yaml, string key)

View File

@@ -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)

View File

@@ -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<ISpriteLoader>();
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)

View File

@@ -277,13 +277,6 @@
<Compile Include="FileFormats\XccLocalDatabase.cs" />
<Compile Include="FileFormats\HvaReader.cs" />
<Compile Include="FileFormats\PngLoader.cs" />
<Compile Include="FileFormats\R8Reader.cs" />
<Compile Include="FileFormats\ShpD2Reader.cs" />
<Compile Include="FileFormats\ShpReader.cs" />
<Compile Include="FileFormats\ShpTSReader.cs" />
<Compile Include="FileFormats\TmpRAReader.cs" />
<Compile Include="FileFormats\TmpTDReader.cs" />
<Compile Include="FileFormats\TmpTSReader.cs" />
<Compile Include="FileFormats\VqaReader.cs" />
<Compile Include="FileFormats\VxlReader.cs" />
<Compile Include="Primitives\ActionQueue.cs" />
@@ -320,7 +313,6 @@
<Compile Include="Map\ActorReference.cs" />
<Compile Include="Support\Evaluator.cs" />
<Compile Include="Settings.cs" />
<Compile Include="Graphics\SpriteSource.cs" />
<Compile Include="Graphics\PlayerColorRemap.cs" />
<Compile Include="Graphics\Palette.cs" />
<Compile Include="FileSystem\GlobalFileSystem.cs" />

View File

@@ -87,7 +87,6 @@
<Compile Include="UtilityCommands\ImportLegacyMapCommand.cs" />
<Compile Include="UtilityCommands\LegacyMapImporter.cs" />
<Compile Include="UtilityCommands\RemapShpCommand.cs" />
<Compile Include="UtilityCommands\TransposeShpCommand.cs" />
<Compile Include="UtilityCommands\UpgradeMapCommand.cs" />
<Compile Include="UtilityCommands\UpgradeModCommand.cs" />
<Compile Include="UtilityCommands\UpgradeRules.cs" />
@@ -114,6 +113,11 @@
<Compile Include="World\ResourceClaim.cs" />
<Compile Include="World\ResourceClaimLayer.cs" />
<Compile Include="World\SmudgeLayer.cs" />
<Compile Include="SpriteLoaders\ShpTDLoader.cs" />
<Compile Include="SpriteLoaders\ShpTSLoader.cs" />
<Compile Include="SpriteLoaders\TmpRALoader.cs" />
<Compile Include="SpriteLoaders\TmpTDLoader.cs" />
<Compile Include="SpriteLoaders\ShpD2Loader.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>

View File

@@ -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<ISpriteFrame> 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;
}
}
}

View File

@@ -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<ISpriteFrame> 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<byte[]> frames)
{
var compressedFrames = frames.Select(f => Format80.Encode(f)).ToArray();

View File

@@ -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<ISpriteFrame> 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;
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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.");
}

View File

@@ -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++)

View File

@@ -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)

View File

@@ -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));
}
}
}

View File

@@ -84,6 +84,7 @@
<Compile Include="Widgets\SlidingContainerWidget.cs" />
<Compile Include="Widgets\Logic\IngameChromeLogic.cs" />
<Compile Include="ChooseBuildTabOnSelect.cs" />
<Compile Include="R8Loader.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>

View File

@@ -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<ISpriteFrame> 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<R8Image>();
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<R8Frame>();
while (s.Position < s.Length)
tmp.Add(new R8Frame(s));
s.Position = start;
frames = tmp.ToArray();
return true;
}
}
}

View File

@@ -56,6 +56,7 @@
<Compile Include="TiberianSunRefinery.cs" />
<Compile Include="Render\WithVoxelWalkerBody.cs" />
<Compile Include="Render\WithVoxelUnloadBody.cs" />
<Compile Include="SpriteLoaders\TmpTSLoader.cs" />
</ItemGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -0,0 +1,121 @@
#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.TS.SpriteLoaders
{
public class TmpTSLoader : ISpriteLoader
{
class TmpTSFrame : 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 TmpTSFrame(Stream s, Size size)
{
if (s.Position != 0)
{
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
}
else
Data = new byte[0];
}
}
bool IsTmpTS(Stream s)
{
var start = s.Position;
s.Position += 8;
var sx = s.ReadUInt32();
var sy = s.ReadUInt32();
// Find the first non-empty frame
var offset = s.ReadUInt32();
while (offset == 0)
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;
}
TmpTSFrame[] ParseFrames(Stream s)
{
var start = s.Position;
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 TmpTSFrame[offsets.Length];
for (var i = 0; i < offsets.Length; i++)
{
s.Position = offsets[i];
tiles[i] = new TmpTSFrame(s, size);
}
s.Position = start;
return tiles;
}
public bool TryParseSprite(Stream s, out ISpriteFrame[] frames)
{
if (!IsTmpTS(s))
{
frames = null;
return false;
}
frames = ParseFrames(s);
return true;
}
}
}

View File

@@ -210,3 +210,5 @@ Missions:
mods/cnc/missions.yaml
SupportsMapsFrom: cnc
SpriteFormats: ShpTD, TmpTD, ShpTS, TmpRA

View File

@@ -188,3 +188,5 @@ LuaScripts:
mods/common/lua/facing.lua
SupportsMapsFrom: d2k
SpriteFormats: R8, ShpTD, TmpRA

View File

@@ -49,4 +49,6 @@ Fonts:
Font:FreeSansBold.ttf
Size:10
LobbyDefaults:
LobbyDefaults:
SpriteFormats: ShpTD

View File

@@ -207,3 +207,5 @@ Missions:
mods/ra/missions.yaml
SupportsMapsFrom: ra
SpriteFormats: ShpTD, TmpRA, TmpTD, ShpTS

View File

@@ -229,3 +229,5 @@ LuaScripts:
mods/common/lua/facing.lua
SupportsMapsFrom: ts
SpriteFormats: ShpTS, TmpTS, ShpTD