1 /* 2 * Copyright (C) 2015-2019, by Laszlo Szeremi under the Boost license. 3 * 4 * Pixel Perfect Engine, tiledata module 5 */ 6 module PixelPerfectEngine.map.tiledata; 7 8 import PixelPerfectEngine.system.etc; 9 10 /** 11 * Represents TileInfo that can be embedded into TGA and PNG files, also can be stored in other files. 12 */ 13 public class TileInfo { 14 static enum char[4] IDENTIFIER_PNG = "tILE"; ///Identifier for PNG ancillary chunk 15 static enum ushort IDENTIFIER_TGA = 0xFF00; ///Identifier for TGA developer extension 16 /** 17 * Header used for shared information. 18 */ 19 public struct Header { 20 align(1): 21 public ubyte tileWidth; ///Width of each tile 22 public ubyte tileHeight; ///Height of each tile 23 } 24 /** 25 * Index used for individual info for each tile in the file. 26 */ 27 align(1) public struct IndexF { 28 align(1): 29 public wchar id; ///Identifier for each tile. 16bit value used for some support for unicode text. 30 public ubyte nameLength; ///Length of the namefield. 31 } 32 /** 33 * Index used for individual info for each tile in memory. 34 */ 35 public struct IndexM { 36 public wchar id; ///Identifier for each tile. 16bit value used for some support for unicode text. 37 private string _name; ///Name with protection from overflow to more than 255 chars 38 /** 39 * Returns the name of the tile. 40 */ 41 public @property @safe @nogc pure nothrow string name() { 42 return _name; 43 } 44 /** 45 * Sets the name of the tile if val is shorter than 255 characters, then returns the new name. 46 * Returns the old name if not. 47 */ 48 public @property @safe pure nothrow string name(string val) { 49 if(val.length <= ubyte.max) 50 _name = val; 51 return _name; 52 } 53 } 54 Header header; ///Header that stores all common data. 55 IndexM[] indexes; ///Index for each tile in the file, even for "unused" tiles. 56 /** 57 * Automatically generates itself from a bytestream. 58 */ 59 public this(ubyte[] source, uint totalWidth, uint totalHeight) @safe pure { 60 header = reinterpretGet!Header(source[0..Header.sizeof]); 61 source = source[Header.sizeof..$]; 62 indexes.length = (totalWidth / header.tileWidth) * (totalHeight / header.tileHeight); 63 for(int i; i < indexes.length; i++) { 64 const IndexF f = reinterpretGet!IndexF(source[0..IndexF.sizeof]); 65 source = source[IndexF.sizeof..$]; 66 indexes[i].id = f.id; 67 if(f.nameLength){ 68 indexes[i].name = (reinterpretCast!char(source[0..f.nameLength])).idup; 69 source = source[f.nameLength..$]; 70 } 71 } 72 } 73 /** 74 * Creates a new instance from scratch. 75 */ 76 public this(ubyte tileWidth, ubyte tileHeight, uint totalWidth, uint totalHeight) @safe pure { 77 header = Header(tileWidth, tileHeight); 78 indexes.length = (totalWidth / header.tileWidth) * (totalHeight / header.tileHeight); 79 } 80 /** 81 * Serializes itself into a bytestream. 82 */ 83 public ubyte[] serialize() @safe pure { 84 ubyte[] result; 85 result ~= toStream(header); 86 for(int i; i < indexes.length; i++) { 87 string name = indexes[i].name; 88 IndexF f = IndexF(indexes[i].id, cast(ubyte)name.length); 89 result ~= toStream(f); 90 if(name.length) 91 result ~= reinterpretCast!ubyte(name.dup); 92 } 93 return result; 94 } 95 }