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 }