1 /* 2 * Copyright (C) 2015-2017, by Laszlo Szeremi under the Boost license. 3 * 4 * Pixel Perfect Engine, map module 5 */ 6 module PixelPerfectEngine.map.mapload; 7 8 import std.xml; 9 import std.stdio; 10 import std.file; 11 import std.algorithm.mutation; 12 //import std.array; 13 import std.conv; 14 import PixelPerfectEngine.extbmp.extbmp; 15 16 //public import map.mapdata; 17 import PixelPerfectEngine.graphics.layers; 18 import PixelPerfectEngine.graphics.bitmap; 19 import PixelPerfectEngine.system.file; 20 import PixelPerfectEngine.system.exc; 21 import PixelPerfectEngine.system.etc; 22 23 /** 24 * Stores, loads, and saves a level data from an XML and multiple MAP files. 25 */ 26 27 public class ExtendibleMap{ 28 private void[] rawData, rawData0; 29 private int headerLenght; 30 private uint flags; 31 private Element[] tileSource, objectSource; 32 private TileLayerData[] tld; 33 private SpriteLayerData[] sld; 34 public string[string] metaData; 35 public string filename; 36 /// Load from datastream 37 this(void[] data){ 38 rawData = data; 39 headerLoad(); 40 } 41 /// Load from file 42 this(string filename){ 43 this.filename = filename; 44 loadFile(); 45 } 46 ///Create new from scratch 47 this(){ 48 49 } 50 /// Loads the bitmaps for the Tilelayer from the XMP files 51 Bitmap16Bit[wchar] loadTileSet(int num){ 52 Bitmap16Bit[wchar] result; 53 54 foreach(Element e1; tileSource[num].elements){ 55 if(e1.tag.name == "File"){ 56 ExtendibleBitmap xmp = new ExtendibleBitmap(e1.tag.attr["source"]); 57 foreach(Element e2; e1.elements){ 58 result[to!wchar(parseHex(e2.tag.attr["wcharID"]))] = loadBitmapFromXMP(xmp, e2.tag.attr["source"]); 59 60 } 61 } 62 } 63 return result; 64 } 65 Bitmap32Bit[wchar] load32BitTileSet(int num){ 66 Bitmap32Bit[wchar] result; 67 foreach(Element e1; tileSource[num].elements){ 68 if(e1.tag.name == "File"){ 69 ExtendibleBitmap xmp = new ExtendibleBitmap(e1.tag.attr["source"]); 70 foreach(Element e2; e1.elements){ 71 result[to!wchar(parseHex(e2.tag.attr["wcharID"]))] = load32BitBitmapFromXMP(xmp, e2.tag.attr["source"]); 72 } 73 } 74 } 75 return result; 76 } 77 void addFileToTileSource(int num, string file){ 78 Element e = new Element(new Tag("File")); 79 e.tag.attr["source"] = file; 80 tileSource[num] ~= e; 81 } 82 void addTileToTileSource(int num, wchar ID, string name, string source, string file){ 83 foreach(Element e; tileSource[num].elements){ 84 if(e.tag.attr["source"] == file){ 85 Element e0 = new Element("TileSource",name); 86 e0.tag.attr["wcharID"] = intToHex(ID, 4); 87 e0.tag.attr["source"] = source; 88 e ~= e0; 89 return; 90 } 91 } 92 } 93 void addTileLayer(TileLayerData t){ 94 tld ~= t; 95 //create placeholder element 96 Element e = new Element("TileLayer"); 97 tileSource ~= e; 98 } 99 TileLayerData getTileLayer(int num){ 100 return tld[num]; 101 } 102 int getNumOfLayers(){ 103 return tld.length + sld.length; 104 } 105 void removeTileLayer(int num){ 106 tld = remove(tld, num); 107 tileSource = remove(tileSource, num); 108 } 109 110 void loadFile(){ 111 //writeln(filename); 112 try{ 113 rawData = std.file.read(filename); 114 flags = *cast(uint*)rawData.ptr; 115 headerLenght = *cast(int*)(rawData.ptr + 4); 116 117 headerLoad(); 118 119 if(rawData.length > 8 + headerLenght){ 120 rawData0 = rawData[8 + headerLenght..rawData.length]; 121 } 122 rawData.length = 0; 123 124 }catch(Exception e){ 125 writeln(e.toString); 126 } 127 } 128 //private void removeElement/( 129 private void headerLoad(){ 130 string header = cast(string)rawData[8..8 + headerLenght]; 131 Document d = new Document(header); 132 foreach(Element e1; d.elements){ 133 switch(e1.tag.name){ 134 case "MetaData": 135 //writeln("MetaData found"); 136 foreach(Element e2; e1.elements){ 137 metaData[e2.tag.name] = e2.text; 138 } 139 break; 140 case "TileLayer": 141 int from = 8 + headerLenght + to!int(e1.tag.attr["dataOffset"]), dataLength = to!int(e1.tag.attr["dataLength"]); 142 tld ~= new TileLayerData(to!int(e1.tag.attr["tX"]), to!int(e1.tag.attr["tY"]), 143 to!int(e1.tag.attr["mX"]), to!int(e1.tag.attr["mY"]), to!double(e1.tag.attr["sX"]), to!double(e1.tag.attr["sY"]), 144 to!int(e1.tag.attr["priority"]), cast(wchar[])rawData[from..(from+dataLength)], e1.tag.attr["name"], e1.tag.attr.get("subType","")); 145 tileSource ~= e1; 146 break; 147 case "SpriteLayer": 148 auto ea = new Element("SpriteLayer"); 149 SpriteLayerData s = new SpriteLayerData(e1.tag.attr["name"], to!double(e1.tag.attr["sX"]), to!double(e1.tag.attr["sY"]), 150 to!int(e1.tag.attr["priority"]), e1.tag.attr.get("subType","")); 151 foreach(Element e2; e1.elements){ 152 if(e2.tag.name == "Object"){ 153 ObjectPlacement o = new ObjectPlacement(to!int(e2.tag.attr["x"]), to!int(e2.tag.attr["y"]), to!int(e2.tag.attr["num"]),e2.tag.attr["ID"]); 154 o.addAuxData(e2.elements); 155 156 }else{ 157 ea ~= e2; 158 } 159 } 160 objectSource ~= ea; 161 sld ~= s; 162 break; 163 default: break; 164 } 165 } 166 } 167 void saveFile(string filename){ 168 this.filename = filename; 169 } 170 void saveFile(){ 171 auto doc = new Document(new Tag("HEADER")); 172 auto e0 = new Element("MetaData"); 173 foreach(string s; metaData.byKey()){ 174 e0 ~= new Element(s, metaData[s]); 175 } 176 doc ~= e0; 177 178 for(int i; i < tileSource.length; i++){ 179 Element e1 = tileSource[i]; 180 e1.tag.attr["name"] = tld[i].name; 181 e1.tag.attr["tX"] = to!string(tld[i].tX); 182 e1.tag.attr["tY"] = to!string(tld[i].tY); 183 e1.tag.attr["mX"] = to!string(tld[i].mX); 184 e1.tag.attr["mY"] = to!string(tld[i].mY); 185 e1.tag.attr["sX"] = to!string(tld[i].sX); 186 e1.tag.attr["sY"] = to!string(tld[i].sY); 187 e1.tag.attr["subtype"] = tld[i].subtype; 188 e1.tag.attr["priority"] = to!string(tld[i].priority); 189 e1.tag.attr["dataOffset"] = to!string(rawData0.length); 190 rawData0 ~= cast(void[])tld[i].mapping; 191 e1.tag.attr["dataLength"] = to!string(tld[i].mapping.length * wchar.sizeof); 192 doc ~= e1; 193 } 194 195 for(int i; i < objectSource.length; i++){ 196 Element e1 = objectSource[i]; 197 e1.tag.attr["name"] = sld[i].name; 198 e1.tag.attr["sX"] = to!string(sld[i].sX); 199 e1.tag.attr["sY"] = to!string(sld[i].sY); 200 e1.tag.attr["subtype"] = sld[i].subtype; 201 e1.tag.attr["priority"] = to!string(sld[i].priority); 202 /*foreach(ObjectPlacement o;sld[i].placement){ 203 auto e2 = o.getAuxData(); 204 e2.tag.attr["x"] = to!string(o.x); 205 e2.tag.attr["y"] = to!string(o.y); 206 e2.tag.attr["num"] = to!string(o.num); 207 e2.tag.attr["ID"] = o.ID; 208 e1 ~= e2; 209 }*/ 210 doc ~= e1; 211 } 212 string header = doc.toString(); 213 rawData.length = 8; 214 *cast(uint*)rawData.ptr = flags; 215 *cast(int*)(rawData.ptr+4) = header.length; 216 rawData ~= cast(void[])header; 217 rawData ~= rawData0; 218 std.file.write(filename, rawData); 219 rawData0.length = 0; 220 } 221 } 222 223 /*public enum ExtMapFlags : uint{ 224 CompressionMethodNull = 1, 225 CompressionMethodZLIB = 2, 226 LongHeader = 16, 227 LongFile = 32 228 }*/ 229 230 public class TileLayerData{ 231 wchar[] mapping; 232 string name, subtype; 233 int tX, tY, mX, mY, priority; 234 double sX, sY; 235 public this(int tX, int tY, int mX, int mY, double sX, double sY, int priority, wchar[] mapping, string name, string subtype = ""){ 236 this.tX = tX; 237 this.tY = tY; 238 this.mX = mX; 239 this.mY = mY; 240 this.sX = sX; 241 this.sY = sY; 242 this.priority = priority; 243 this.mapping = mapping; 244 this.name = name; 245 this.subtype = subtype; 246 } 247 public this(int tX, int tY, int mX, int mY, double sX, double sY, int priority, string name, string subtype = ""){ 248 this.tX = tX; 249 this.tY = tY; 250 this.mX = mX; 251 this.mY = mY; 252 this.sX = sX; 253 this.sY = sY; 254 this.priority = priority; 255 //this.mapping = mapping; 256 this.name = name; 257 this.subtype = subtype; 258 259 wchar[] initMapping; 260 initMapping.length = mX*mY; 261 this.mapping = initMapping; 262 } 263 public void writeMapping(int x, int y, wchar tile){ 264 mapping[x + (mX * y)] = tile; 265 } 266 public wchar readMapping(int x, int y){ 267 return mapping[x + (mX * y)]; 268 } 269 } 270 271 public class SpriteLayerData{ 272 string name, subtype; 273 double sX, sY; 274 int priority; 275 ObjectPlacement[] placement; 276 this(string name, double sX, double sY, int priority, string subtype = ""){ 277 this.name = name; 278 this.subtype = subtype; 279 this.sX = sX; 280 this.sY = sY; 281 this.priority = priority; 282 } 283 } 284 285 public class ObjectPlacement{ 286 protected Element[] auxObjectData; 287 int x, y, num; 288 string ID; 289 this(int x, int y, int num, string ID){ 290 this.x = x; 291 this.y = y; 292 this.num = num; 293 this.ID = ID; 294 } 295 296 void addAuxData(Element[] auxData){ 297 auxObjectData = auxData; 298 } 299 300 Element[] getAuxData(){ 301 return auxObjectData; 302 } 303 }