示例#1
0
        public bool TryParseSprite(Stream s, out ISpriteFrame[] frames, out TypeDictionary metadata)
        {
            frames   = new ISpriteFrame[1];
            metadata = new TypeDictionary();

            return(true);
        }
示例#2
0
            public WsaTile(Stream s, Size size, ISpriteFrame prev)
            {
                Size = size;
                var dataLen = s.Length - s.Position;

                Console.WriteLine("dataLen = {0}", dataLen);
                var tempData = StreamExts.ReadBytes(s, (int)dataLen);

                byte[] srcData = new byte[size.Width * size.Height];

                // format80 decompression
                LCWCompression.DecodeInto(tempData, srcData);

                // and format40 decmporession
                Data = new byte[size.Width * size.Height];
                if (prev == null)
                {
                    Array.Clear(Data, 0, Data.Length);
                }
                else
                {
                    Array.Copy(prev.Data, Data, Data.Length);
                }
                XORDeltaCompression.DecodeInto(srcData, Data, 0);
            }
示例#3
0
        public void Read(Stream stream, out TypeDictionary metadata)
        {
            ISpriteFrame[] videoFrames;
            ISpriteFrame   prev      = null;
            WsaLoader      wsaLoader = new WsaLoader();

            metadata = null;

            if (frames != null)
            {
                prev = frames[frames.Length - 1];
            }

            wsaLoader.TryParseSpriteWithPrevFrame(stream, prev, out videoFrames, out metadata);

            if (frames == null)
            {
                frames = videoFrames;

                if (Length > 0)
                {
                    var frame = frames[0];
                    width  = frame.Size.Width;
                    height = frame.Size.Height;
                }
            }
            else
            {
                frames = frames.Concat(videoFrames).ToArray();
            }
        }
示例#4
0
        ISpriteFrame[] 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();
            }

            // Depth information are stored as a second set of frames (like split shadows)
            var stride = offsets.Length;
            var tiles  = new ISpriteFrame[stride * 2];

            for (var j = 0; j < templateHeight; j++)
            {
                for (var i = 0; i < templateWidth; i++)
                {
                    var k = j * templateWidth + i;
                    s.Position = offsets[k];

                    var frame = new TmpTSFrame(s, size, i, j);
                    tiles[k]          = frame;
                    tiles[k + stride] = new TmpTSDepthFrame(frame);
                }
            }

            s.Position = start;
            return(tiles);
        }
示例#5
0
        public bool TryParseSprite(Stream s, out ISpriteFrame[] frames, out TypeDictionary metadata)
        {
            metadata = null;
            frames   = null;
            if (!Png.Verify(s))
            {
                return(false);
            }

            var png = new Png(s);

            List <Rectangle> frameRegions;
            List <float2>    frameOffsets;

            // Prefer manual defined regions over auto sliced regions.
            if (png.EmbeddedData.Any(meta => meta.Key.StartsWith("Frame[")))
            {
                RegionsFromFrames(png, out frameRegions, out frameOffsets);
            }
            else
            {
                RegionsFromSlices(png, out frameRegions, out frameOffsets);
            }

            frames = new ISpriteFrame[frameRegions.Count];

            for (var i = 0; i < frames.Length; i++)
            {
                var frameStart  = frameRegions[i].X + frameRegions[i].Y * png.Width;
                var frameSize   = new Size(frameRegions[i].Width, frameRegions[i].Height);
                var pixelLength = png.Palette == null ? 4 : 1;

                frames[i] = new PngSheetFrame()
                {
                    Size      = frameSize,
                    FrameSize = frameSize,
                    Offset    = frameOffsets[i],
                    Data      = new byte[frameRegions[i].Width * frameRegions[i].Height * pixelLength],
                    Type      = png.Palette == null ? SpriteFrameType.BGRA : SpriteFrameType.Indexed
                };

                for (var y = 0; y < frames[i].Size.Height; y++)
                {
                    Array.Copy(png.Data, (frameStart + y * png.Width) * pixelLength, frames[i].Data, y * frames[i].Size.Width * pixelLength, frames[i].Size.Width * pixelLength);
                }
            }

            metadata = new TypeDictionary
            {
                new PngSheetMetadata(png.EmbeddedData),
            };

            if (png.Palette != null)
            {
                metadata.Add(new EmbeddedSpritePalette(png.Palette.Select(x => (uint)x.ToArgb()).ToArray()));
            }

            return(true);
        }
示例#6
0
		public bool TryParseSprite(Stream s, out ISpriteFrame[] frames)
		{
			if (!IsShpTD(s))
			{
				frames = null;
				return false;
			}

			frames = new ShpTDSprite(s).Frames.ToArray();
			return true;
		}
示例#7
0
 bool IFrameList.Contains(ISpriteFrame item)
 {
     if (item is T)
     {
         return(mFrames.Contains((T)item));
     }
     else
     {
         return(false);
     }
 }
示例#8
0
		public bool TryParseSprite(Stream s, out ISpriteFrame[] frames)
		{
			if (!IsTmpRA(s))
			{
				frames = null;
				return false;
			}

			frames = ParseFrames(s);
			return true;
		}
示例#9
0
 int IFrameList.IndexOf(ISpriteFrame item)
 {
     if (item is T)
     {
         return(mFrames.IndexOf((T)item));
     }
     else
     {
         return(-1);
     }
 }
示例#10
0
        public bool TryParseSpriteWithPrevFrame(Stream s, ISpriteFrame prev, out ISpriteFrame[] frames, out TypeDictionary metadata)
        {
            metadata = null;
            if (!IsWsa(s))
            {
                frames = null;
                return(false);
            }

            frames = ParseFrames(s, prev);
            return(true);
        }
示例#11
0
        public bool TryParseSprite(Stream s, out ISpriteFrame[] frames)
        {
            if (!IsCpsD2(s))
            {
                frames = null;
                return false;
            }

            s.Position = 0;
            frames = ParseFrames(s);
            return true;
        }
示例#12
0
        IHardwareCursor CreateCursor(ISpriteFrame f, ImmutablePalette palette, string name, CursorSequence sequence)
        {
            var hotspot = sequence.Hotspot - f.Offset.ToInt2() + new int2(f.Size) / 2;

            // Expand the frame if required to include the hotspot
            var frameWidth = f.Size.Width;
            var dataWidth  = f.Size.Width;
            var dataX      = 0;

            if (hotspot.X < 0)
            {
                dataX      = -hotspot.X;
                dataWidth += dataX;
                hotspot    = hotspot.WithX(0);
            }
            else if (hotspot.X >= frameWidth)
            {
                dataWidth = hotspot.X + 1;
            }

            var frameHeight = f.Size.Height;
            var dataHeight  = f.Size.Height;
            var dataY       = 0;

            if (hotspot.Y < 0)
            {
                dataY       = -hotspot.Y;
                dataHeight += dataY;
                hotspot     = hotspot.WithY(0);
            }
            else if (hotspot.Y >= frameHeight)
            {
                dataHeight = hotspot.Y + 1;
            }

            var data = new byte[4 * dataWidth * dataHeight];

            for (var j = 0; j < frameHeight; j++)
            {
                for (var i = 0; i < frameWidth; i++)
                {
                    var bytes = BitConverter.GetBytes(palette[f.Data[j * frameWidth + i]]);
                    var start = 4 * ((j + dataY) * dataWidth + dataX + i);
                    for (var k = 0; k < 4; k++)
                    {
                        data[start + k] = bytes[k];
                    }
                }
            }

            return(Game.Renderer.Window.CreateHardwareCursor(name, new Size(dataWidth, dataHeight), data, hotspot));
        }
示例#13
0
        WsaTile[] ParseFrames(Stream s, ISpriteFrame prev)
        {
            var start = s.Position;

            var tiles = new WsaTile[numTiles];

            for (var i = 0; i < numTiles; i++)
            {
                s.Position = offsets[i];
                tiles[i]   = new WsaTile(s, new Size(tileWidth, tileHeight), (i == 0) ? prev : tiles[i - 1]);
            }

            s.Position = start;
            return(tiles);
        }
示例#14
0
        public override bool TryParseSprite(Stream s, string filename, out ISpriteFrame[] frames, out TypeDictionary metadata)
        {
            metadata = null;

            if (!IsDdf(s))
            {
                frames = null;
                return(false);
            }

            var aniStream = s as DdfPackageLoader.AniSegmentStream;

            aniStream.Position = aniStream.AniPosition;
            frames             = new ISpriteFrame[aniStream.ReadUInt32()];

            for (var i = 0; i < frames.Length; i++)
            {
                var metaName = aniStream.ReadASCII(32).Replace("\0", string.Empty);

                aniStream.Position += 4 * 2;
                var numScripts = aniStream.ReadUInt32();
                aniStream.Position += 32 * numScripts;
                var returnPosition = aniStream.Position;

                long metaPosition;
                if (aniStream.MetaIndex.TryGetValue(metaName, out metaPosition))
                {
                    var metaStream = new SegmentStream(aniStream, 0, aniStream.Length);
                    metaStream.Position = metaPosition;

                    var ddfName = metaStream.ReadASCII(32).Replace("\0", string.Empty);
                    metaStream.Position += 4 * 3;
                    var offset = new float2(metaStream.ReadInt32(), metaStream.ReadInt32());

                    long ddfPosition;
                    if (aniStream.DdfIndex.TryGetValue(ddfName, out ddfPosition))
                    {
                        var ddfStream = aniStream.DdfStream;
                        ddfStream.Position = ddfPosition;
                        frames[i]          = new DdfSpriteFrame(ddfStream, offset);
                    }
                }

                aniStream.Position = returnPosition;
            }

            return(true);
        }
示例#15
0
		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;
		}
示例#16
0
		// Extract a square tile that the editor can render
		byte[] ExtractSquareTile(ISpriteFrame frame)
		{
			var data = new byte[TileSize * TileSize];

			// Invalid tile size: return blank tile
			if (frame.Size.Width < TileSize || frame.Size.Height < TileSize)
				return new byte[0];

			var frameData = frame.Data;
			var xOffset = (frame.Size.Width - TileSize) / 2;
			var yOffset = (frame.Size.Height - TileSize) / 2;

			for (var y = 0; y < TileSize; y++)
				for (var x = 0; x < TileSize; x++)
					data[y * TileSize + x] = frameData[(yOffset + y) * frame.Size.Width + x + xOffset];

			return data;
		}
示例#17
0
		IHardwareCursor CreateCursor(ISpriteFrame f, ImmutablePalette palette, string name, CursorSequence sequence)
		{
			var hotspot = sequence.Hotspot - f.Offset.ToInt2() + new int2(f.Size) / 2;

			// Expand the frame if required to include the hotspot
			var frameWidth = f.Size.Width;
			var dataWidth = f.Size.Width;
			var dataX = 0;
			if (hotspot.X < 0)
			{
				dataX = -hotspot.X;
				dataWidth += dataX;
				hotspot = hotspot.WithX(0);
			}
			else if (hotspot.X >= frameWidth)
				dataWidth = hotspot.X + 1;

			var frameHeight = f.Size.Height;
			var dataHeight = f.Size.Height;
			var dataY = 0;
			if (hotspot.Y < 0)
			{
				dataY = -hotspot.Y;
				dataHeight += dataY;
				hotspot = hotspot.WithY(0);
			}
			else if (hotspot.Y >= frameHeight)
				dataHeight = hotspot.Y + 1;

			var data = new byte[4 * dataWidth * dataHeight];
			for (var j = 0; j < frameHeight; j++)
			{
				for (var i = 0; i < frameWidth; i++)
				{
					var bytes = BitConverter.GetBytes(palette[f.Data[j * frameWidth + i]]);
					var start = 4 * ((j + dataY) * dataWidth + dataX + i);
					for (var k = 0; k < 4; k++)
						data[start + k] = bytes[k];
				}
			}

			return Game.Renderer.Device.CreateHardwareCursor(name, new Size(dataWidth, dataHeight), data, hotspot);
		}
示例#18
0
        static Sheet SpriteFrameToSheet(ISpriteFrame frame, PaletteReference p)
        {
            var          size         = Exts.NextPowerOf2(Math.Max(frame.FrameSize.Width, frame.FrameSize.Height));
            SheetBuilder sheetBuilder = new SheetBuilder(SheetType.BGRA, size);

            byte[] data;
            if (frame.Type == SpriteFrameType.Indexed)
            {
                data = IndexedSpriteFrameToData(frame, p);
            }
            else
            {
                data = frame.Data;
            }

            var sprite = sheetBuilder.Add(data, frame.FrameSize);

            return(sprite.Sheet);
        }
示例#19
0
        public override bool TryParseSprite(Stream s, out ISpriteFrame[] frames, out TypeDictionary metadata)
        {
            PngFrame isf = new PngFrame();
            Png      p   = new Png(s);

            isf.Data      = p.Data;
            isf.Size      = new Size(p.Width, p.Height);
            isf.FrameSize = new Size(p.Width, p.Height);
            frames        = new ISpriteFrame[1];
            frames[0]     = isf;

            metadata = new TypeDictionary
            {
                new PngSheetMetadata(p.EmbeddedData),
                new EmbeddedSpritePalette(p.Palette.Select(x => (uint)x.ToArgb()).ToArray())
            };

            //metadata = null;
            return(true);
        }
示例#20
0
        IHardwareCursor CreateCursor(string name, ISpriteFrame frame, ImmutablePalette palette, int2 paddingTL, int2 paddingBR, int2 hotspot)
        {
            // Pad the cursor and convert to RBGA
            var newWidth  = paddingTL.X + frame.Size.Width + paddingBR.X;
            var newHeight = paddingTL.Y + frame.Size.Height + paddingBR.Y;
            var rgbaData  = new byte[4 * newWidth * newHeight];

            for (var j = 0; j < frame.Size.Height; j++)
            {
                for (var i = 0; i < frame.Size.Width; i++)
                {
                    var bytes = BitConverter.GetBytes(palette[frame.Data[j * frame.Size.Width + i]]);
                    var o     = 4 * ((j + paddingTL.Y) * newWidth + i + paddingTL.X);
                    for (var k = 0; k < 4; k++)
                    {
                        rgbaData[o + k] = bytes[k];
                    }
                }
            }

            return(Game.Renderer.Window.CreateHardwareCursor(name, new Size(newWidth, newHeight), rgbaData, hotspot));
        }
示例#21
0
        public static byte[] FrameToBGRA(string name, ISpriteFrame frame, ImmutablePalette palette)
        {
            // Data is already in BGRA format
            if (frame.Type == SpriteFrameType.BGRA)
            {
                return(frame.Data);
            }

            // Cursors may be either native BGRA or Indexed.
            // Indexed sprites are converted to BGRA using the referenced palette.
            // All palettes must be explicitly referenced, even if they are embedded in the sprite.
            if (frame.Type == SpriteFrameType.Indexed && palette == null)
            {
                throw new InvalidOperationException("Cursor sequence `{0}` attempted to load an indexed sprite but does not define Palette".F(name));
            }

            var width  = frame.Size.Width;
            var height = frame.Size.Height;
            var data   = new byte[4 * width * height];

            for (var j = 0; j < height; j++)
            {
                for (var i = 0; i < width; i++)
                {
                    var bytes = BitConverter.GetBytes(palette[frame.Data[j * width + i]]);
                    var c     = palette[frame.Data[j * width + i]];
                    var k     = 4 * (j * width + i);

                    // Convert RGBA to BGRA
                    data[k]     = bytes[2];
                    data[k + 1] = bytes[1];
                    data[k + 2] = bytes[0];
                    data[k + 3] = bytes[3];
                }
            }

            return(data);
        }
示例#22
0
        static byte[] IndexedSpriteFrameToData(ISpriteFrame frame, PaletteReference p)
        {
            byte[] data = new byte[4 * frame.FrameSize.Width * frame.FrameSize.Height];

            IPalette palette = p.Palette;
            int      k       = 0;
            int      offset  = 0;

            byte[] frameData = frame.Data;
            for (int y = 0; y < frame.FrameSize.Height; y++)
            {
                for (int x = 0; x < frame.FrameSize.Width; x++)
                {
                    Color color = Color.FromArgb(palette[frameData[k++]]);
                    data[offset++] = color.R;
                    data[offset++] = color.G;
                    data[offset++] = color.B;
                    data[offset++] = color.A;
                }
            }

            return(data);
        }
示例#23
0
        WsaTile[] ParseFrames(Stream s, ISpriteFrame prev)
        {
            var start = s.Position;

            var tiles = new WsaTile[numTiles];

            for (var i = 0; i < numTiles; i++)
            {
                s.Position = offsets[i];
                if ((prevwsafilename.Contains("7B") || prevwsafilename.Contains("8B") || prevwsafilename.Contains("8C")) && prevwsaframe != null)
                {
                    tiles[i]     = new WsaTile(s, new Size(tileWidth, tileHeight), prevwsaframe);
                    prevwsaframe = null;
                }
                else
                {
                    tiles[i] = new WsaTile(s, new Size(tileWidth, tileHeight), (i == 0) ? prev : tiles[i - 1]);
                }
            }

            s.Position = start;
            return(tiles);
        }
示例#24
0
        public bool TryParseSprite(Stream stream, out ISpriteFrame[] frames)
        {
            var start      = stream.Position;
            var identifier = stream.ReadASCII(10);

            if (identifier != "MIX FILE  ")
            {
                stream.Position = start;
                frames          = new ISpriteFrame[0];
                return(false);
            }

            var dataSize = stream.ReadInt32();

            frames = new ISpriteFrame[stream.ReadInt32()];
            var imagesOffset = stream.ReadInt32();

            /*var numPalettes = */ stream.ReadInt32();
            /*var firstPaletteId = */ stream.ReadInt32();
            /*var paletteOffset = */ stream.ReadInt32();

            /*var entry = */ stream.ReadASCII(5); // "ENTRY"

            for (var i = 0; i < frames.Length; i++)
            {
                var imageOffset = stream.ReadInt32();
                frames[i] = new MixSpriteFrame(SegmentStream.CreateWithoutOwningStream(stream, imagesOffset + imageOffset, dataSize - imageOffset));
            }

            // /*var pal = */stream.ReadASCII(5); // " PAL "

            // for (var i = 0; i < numPalettes; i++) {
            //     Palette palette;
            // }

            return(true);
        }
示例#25
0
        // Extract a square tile that the editor can render
        byte[] ExtractSquareTile(ISpriteFrame frame)
        {
            var data = new byte[TileSize * TileSize];

            // Invalid tile size: return blank tile
            if (frame.Size.Width < TileSize || frame.Size.Height < TileSize)
            {
                return(new byte[0]);
            }

            var frameData = frame.Data;
            var xOffset   = (frame.Size.Width - TileSize) / 2;
            var yOffset   = (frame.Size.Height - TileSize) / 2;

            for (var y = 0; y < TileSize; y++)
            {
                for (var x = 0; x < TileSize; x++)
                {
                    data[y * TileSize + x] = frameData[(yOffset + y) * frame.Size.Width + x + xOffset];
                }
            }

            return(data);
        }
示例#26
0
        public static byte[] ConvertIndexedToBgra(string name, ISpriteFrame frame, ImmutablePalette palette)
        {
            if (frame.Type != SpriteFrameType.Indexed8)
            {
                throw new ArgumentException("ConvertIndexedToBgra requires input frames to be indexed.", nameof(frame));
            }

            // All palettes must be explicitly referenced, even if they are embedded in the sprite.
            if (palette == null)
            {
                throw new InvalidOperationException($"Cursor sequence `{name}` attempted to load an indexed sprite but does not define Palette");
            }

            var width  = frame.Size.Width;
            var height = frame.Size.Height;
            var data   = new byte[4 * width * height];

            unsafe
            {
                // Cast the data to an int array so we can copy the src data directly
                fixed(byte *bd = &data[0])
                {
                    var rgba = (uint *)bd;

                    for (var j = 0; j < height; j++)
                    {
                        for (var i = 0; i < width; i++)
                        {
                            rgba[j * width + i] = palette[frame.Data[j * width + i]];
                        }
                    }
                }
            }

            return(data);
        }
示例#27
0
        public virtual bool TryParseSprite(Stream s, string filename, out ISpriteFrame[] frames, out TypeDictionary metadata)
        {
            metadata = null;

            if (!IsDdf(s))
            {
                frames = null;
                return(false);
            }

            var ddfStream = s as DdfPackageLoader.DdfSegmentStream;

            ddfStream.Position = ddfStream.DdfPosition;

            if (ddfStream.IsTile)
            {
                var fullTile = new DdfSpriteFrame(ddfStream, new float2(0, 0));
                frames = new ISpriteFrame[]
                {
                    new QuarterDdfTile(fullTile, 0, 0),
                    new QuarterDdfTile(fullTile, 1, 0),
                    new QuarterDdfTile(fullTile, 0, 1),
                    new QuarterDdfTile(fullTile, 1, 1)
                };
            }
            else
            {
                frames = new ISpriteFrame[]
                {
                    new DdfSpriteFrame(ddfStream, new float2(0, 0))
                }
            };

            return(true);
        }
    }
示例#28
0
            public WsaD2Tile(Stream s, Size size, ISpriteFrame prev)
            {
                Size = size;
                var dataLen = s.Length - s.Position;
                Console.WriteLine("dataLen = {0}", dataLen);
                var tempData = StreamExts.ReadBytes(s, (int)dataLen);
                byte[] srcData = new byte[size.Width * size.Height];

                // format80 decompression
                LCWCompression.DecodeInto(tempData, srcData);

                // and format40 decmporession
                Data = new byte[size.Width * size.Height];
                if (prev == null)
                    Array.Clear(Data, 0, Data.Length);
                else
                    Array.Copy(prev.Data, Data, Data.Length);
                XORDeltaCompression.DecodeInto(srcData, Data, 0);
            }
示例#29
0
 public Sprite Add(ISpriteFrame frame)
 {
     return(Add(frame.Data, frame.Size, frame.Offset));
 }
示例#30
0
        public ShpRemasteredSprite(Stream stream)
        {
            var container = new ZipFile(stream);

            string framePrefix = null;
            var    frameCount  = 0;

            foreach (ZipEntry entry in container)
            {
                var match = FilenameRegex.Match(entry.Name);
                if (!match.Success)
                {
                    continue;
                }

                var prefix = match.Groups["prefix"].Value;
                if (framePrefix == null)
                {
                    framePrefix = prefix;
                }

                if (prefix != framePrefix)
                {
                    throw new InvalidDataException($"Frame prefix mismatch: `{prefix}` != `{framePrefix}`");
                }

                frameCount = Math.Max(frameCount, int.Parse(match.Groups["frame"].Value) + 1);
            }

            var frames = new ISpriteFrame[frameCount];

            for (var i = 0; i < frames.Length; i++)
            {
                var tgaEntry = container.GetEntry($"{framePrefix}{i:D4}.tga");

                // Blank frame
                if (tgaEntry == null)
                {
                    frames[i] = new TgaSprite.TgaFrame();
                    continue;
                }

                var metaEntry = container.GetEntry($"{framePrefix}{i:D4}.meta");
                using (var tgaStream = container.GetInputStream(tgaEntry))
                {
                    var metaStream = metaEntry != null?container.GetInputStream(metaEntry) : null;

                    if (metaStream != null)
                    {
                        var meta = MetaRegex.Match(metaStream.ReadAllText());
                        var crop = Rectangle.FromLTRB(
                            ParseGroup(meta, "left"), ParseGroup(meta, "top"),
                            ParseGroup(meta, "right"), ParseGroup(meta, "bottom"));

                        var frameSize = new Size(ParseGroup(meta, "width"), ParseGroup(meta, "height"));
                        frames[i] = new TgaSprite.TgaFrame(tgaStream, frameSize, crop);
                        metaStream.Dispose();
                    }
                    else
                    {
                        frames[i] = new TgaSprite.TgaFrame(tgaStream);
                    }
                }
            }

            Frames = frames;
        }
示例#31
0
		public Sprite Add(ISpriteFrame frame) { return Add(frame.Data, frame.Size, frame.Offset); }
示例#32
0
		ISpriteFrame[] 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();

			// Depth information are stored as a second set of frames (like split shadows)
			var stride = offsets.Length;
			var tiles = new ISpriteFrame[stride * 2];

			for (var j = 0; j < templateHeight; j++)
			{
				for (var i = 0; i < templateWidth; i++)
				{
					var k = j * templateWidth + i;
					s.Position = offsets[k];

					var frame = new TmpTSFrame(s, size, i, j);
					tiles[k] = frame;
					tiles[k + stride] = new TmpTSDepthFrame(frame);
				}
			}

			s.Position = start;
			return tiles;
		}