View Javadoc

1   /*
2    *   Firemox is a turn based strategy simulator
3    *   Copyright (C) 2003-2007 Fabrice Daugan
4    *
5    *   This program is free software; you can redistribute it and/or modify it 
6    * under the terms of the GNU General Public License as published by the Free 
7    * Software Foundation; either version 2 of the License, or (at your option) any
8    * later version.
9    *
10   *   This program is distributed in the hope that it will be useful, but WITHOUT 
11   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12   * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
13   * details.
14   *
15   *   You should have received a copy of the GNU General Public License along  
16   * with this program; if not, write to the Free Software Foundation, Inc., 
17   * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  package net.sf.firemox.xml.tbs;
20  
21  import java.io.FileOutputStream;
22  import java.io.IOException;
23  import java.io.OutputStream;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  import java.util.Stack;
32  
33  import net.sf.firemox.modifier.ModifierType;
34  import net.sf.firemox.operation.IdOperation;
35  import net.sf.firemox.token.IdPropertyType;
36  import net.sf.firemox.token.IdTokens;
37  import net.sf.firemox.token.IdZones;
38  import net.sf.firemox.tools.MToolKit;
39  import net.sf.firemox.tools.PairStringInt;
40  import net.sf.firemox.ui.component.CardPropertiesPanel;
41  import net.sf.firemox.xml.XmlAction;
42  import net.sf.firemox.xml.XmlConfiguration;
43  import net.sf.firemox.xml.XmlModifier;
44  import net.sf.firemox.xml.XmlParser;
45  import net.sf.firemox.xml.XmlTbs;
46  import net.sf.firemox.xml.XmlTest;
47  import net.sf.firemox.xml.XmlToMDB;
48  import net.sf.firemox.xml.XmlTools;
49  import net.sf.firemox.xml.XmlParser.Node;
50  
51  import org.apache.commons.io.FilenameUtils;
52  
53  /***
54   * This class represents an implementation of the XmlToMDB interface that
55   * converts a XML tbs node to its binary form to the given OutputSource.
56   * 
57   * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan</a>
58   * @since 0.82
59   * @since 0.83 damage types are exported
60   */
61  public class Tbs implements XmlToMDB {
62  
63  	/***
64  	 * Converts the given tbs XML node to its binary form writing in the given
65  	 * OutputStream.
66  	 * 
67  	 * @see net.sf.firemox.token.IdTokens#PLAYER_REGISTER_SIZE
68  	 * @see net.sf.firemox.stack.phasetype.PhaseType
69  	 * @see net.sf.firemox.clickable.ability.SystemAbility
70  	 * @see net.sf.firemox.tools.StatePicture
71  	 * @param node
72  	 *          the XML card structure
73  	 * @param out
74  	 *          output stream where the card structure will be saved
75  	 * @return the amount of written action in the output.
76  	 * @throws IOException
77  	 *           error during the writing.
78  	 * @see net.sf.firemox.deckbuilder.MdbLoader#loadMDB(String, int)
79  	 */
80  	public final int buildMdb(Node node, OutputStream out) throws IOException {
81  		final FileOutputStream fileOut = (FileOutputStream) out;
82  		referencedTest = new HashMap<String, Node>();
83  		referencedAbilities = new HashMap<String, Node>();
84  		referencedActions = new HashMap<String, List<Node>>();
85  		referencedAttachments = new HashMap<String, Node>();
86  		referencedNonMacroActions = new HashSet<String>();
87  		referencedNonMacroAttachments = new HashSet<String>();
88  		macroActions = new Stack<List<Node>>();
89  		resolveReferences = false;
90  
91  		MToolKit.writeString(out, node.getAttribute("name"));
92  		System.out.println("Building " + node.getAttribute("name") + " rules...");
93  		MToolKit.writeString(out, node.getAttribute("version"));
94  		MToolKit.writeString(out, node.getAttribute("author"));
95  		MToolKit.writeString(out, node.getAttribute("comment"));
96  
97  		// Write database configuration
98  		final Node database = node.get("database-properties");
99  		Map<String, IdPropertyType> properties = new HashMap<String, IdPropertyType>();
100 		for (java.lang.Object obj : database) {
101 			if (obj instanceof Node) {
102 				Node nodePriv = (Node) obj;
103 				String propertyType = nodePriv.getAttribute("type");
104 				IdPropertyType propertyId;
105 				if (propertyType == null || propertyType.equals(String.class.getName())) {
106 					if ("true".equalsIgnoreCase(nodePriv.getAttribute("translate"))) {
107 						propertyId = IdPropertyType.SIMPLE_TRANSLATABLE_PROPERTY;
108 					} else {
109 						propertyId = IdPropertyType.SIMPLE_PROPERTY;
110 					}
111 				} else if ("java.util.List".equals(propertyType)) {
112 					if ("true".equalsIgnoreCase(nodePriv.getAttribute("translate"))) {
113 						propertyId = IdPropertyType.COLLECTION_TRANSLATABLE_PROPERTY;
114 					} else {
115 						propertyId = IdPropertyType.COLLECTION_PROPERTY;
116 					}
117 				} else {
118 					throw new InternalError("Unknow property type '" + propertyType + "'");
119 				}
120 				properties.put(nodePriv.getAttribute("name"), propertyId);
121 			}
122 		}
123 		out.write(properties.size());
124 		for (Map.Entry<String, IdPropertyType> entry : properties.entrySet()) {
125 			entry.getValue().serialize(out);
126 			MToolKit.writeString(out, entry.getKey());
127 		}
128 
129 		MToolKit.writeString(out, node.getAttribute("art-url"));
130 		MToolKit.writeString(out, node.getAttribute("back-picture"));
131 		MToolKit.writeString(out, node.getAttribute("damage-picture"));
132 		MToolKit.writeString(out, (String) node.get("licence").get(0));
133 
134 		// Manas
135 		final Node manas = node.get("mana-symbols");
136 
137 		// Colored manas
138 		final Node colored = manas.get("colored");
139 		MToolKit.writeString(out, colored.getAttribute("url"));
140 		MToolKit.writeString(out, colored.getAttribute("big-url"));
141 		for (java.lang.Object obj : colored) {
142 			if (obj instanceof Node) {
143 				Node nodePriv = (Node) obj;
144 				out.write(XmlTools.getValue(nodePriv.getAttribute("name")));
145 				MToolKit.writeString(out, nodePriv.getAttribute("picture"));
146 				MToolKit.writeString(out, nodePriv.getAttribute("big-picture"));
147 			}
148 		}
149 
150 		// Colorless manas
151 		final Node colorless = manas.get("colorless");
152 		MToolKit.writeString(out, colorless.getAttribute("url"));
153 		MToolKit.writeString(out, colorless.getAttribute("big-url"));
154 		MToolKit.writeString(out, colorless.getAttribute("unknown"));
155 		out.write(colorless.getNbNodes());
156 		for (java.lang.Object obj : colorless) {
157 			if (obj instanceof Node) {
158 				final Node nodePriv = (Node) obj;
159 				out.write(Integer.parseInt(nodePriv.getAttribute("amount")));
160 				MToolKit.writeString(out, nodePriv.getAttribute("picture"));
161 			}
162 		}
163 
164 		// Prepare the shortcut to card's bytes offset
165 		final long shortcutCardBytes = fileOut.getChannel().position();
166 		MToolKit.writeInt24(out, 0);
167 
168 		// Write the user defined deck constraints
169 		final Set<String> definedConstraints = new HashSet<String>();
170 		final Node deckConstraintRoot = XmlTools.getExternalizableNode(node,
171 				"deck-constraints");
172 		final List<Node> deckConstraints = deckConstraintRoot
173 				.getNodes("deck-constraint");
174 		XmlTools.writeAttrOptions(deckConstraintRoot, "deckbuilder-min-property",
175 				out);
176 		XmlTools.writeAttrOptions(deckConstraintRoot, "deckbuilder-max-property",
177 				out);
178 		XmlTools.writeAttrOptions(deckConstraintRoot, "master", out);
179 		out.write(deckConstraints.size());
180 		for (Node deckConstraint : deckConstraints) {
181 			// Write the constraint key name
182 			String deckName = deckConstraint.getAttribute("name");
183 			MToolKit.writeString(out, deckName);
184 
185 			// Write extend
186 			String extend = deckConstraint.getAttribute("extends");
187 			MToolKit.writeString(out, extend);
188 			if (extend != null && extend.length() > 0
189 					&& !definedConstraints.contains(extend)) {
190 				throw new RuntimeException(
191 						"'"
192 								+ deckName
193 								+ "' is supposed extending '"
194 								+ extend
195 								+ "' but has not been found. Note that declaration order is important.");
196 			}
197 
198 			// Write the constraint
199 			XmlTools.defaultOnMeTag = false;
200 			XmlTest.getTest("test").buildMdb(deckConstraint, out);
201 			definedConstraints.add(deckName);
202 		}
203 		// END OF HEADER
204 
205 		// additional zones
206 		final List<XmlParser.Node> additionalZones = node.get("layouts").get(
207 				"zones").get("additional-zones").getNodes("additional-zone");
208 		out.write(additionalZones.size());
209 		int zoneId = IdZones.FIRST_ADDITIONAL_ZONE;
210 		for (XmlParser.Node additionalZone : additionalZones) {
211 			final String zoneName = additionalZone.getAttribute("name");
212 			if (zoneId > IdZones.LAST_ADDITIONAL_ZONE) {
213 				throw new RuntimeException("Cannot add more additional-zone ("
214 						+ zoneName + "), increase core limitation");
215 			}
216 			XmlTools.zones.put(zoneName, zoneId++);
217 			MToolKit.writeString(out, zoneName);
218 			MToolKit.writeString(out, additionalZone.getAttribute("layout-class"));
219 			MToolKit.writeString(out, additionalZone.getAttribute("constraint-you"));
220 			MToolKit.writeString(out, additionalZone
221 					.getAttribute("constraint-opponent"));
222 		}
223 
224 		// Initial zone id
225 		out.write(XmlTools.getZone(node.get("layouts").get("zones").getAttribute(
226 				"default-zone")));
227 
228 		// the references
229 		final Node references = XmlTools.getExternalizableNode(node, "references");
230 
231 		// the tests references
232 		System.out.println("\tshared tests...");
233 		XmlTools.defaultOnMeTag = true;
234 		final Node tests = references.get("tests");
235 		if (tests == null) {
236 			out.write(0);
237 		} else {
238 			out.write(tests.getNbNodes());
239 			for (java.lang.Object obj : tests) {
240 				if (obj instanceof Node) {
241 					String ref = ((Node) obj).getAttribute("reference-name").toString();
242 					referencedTest.put(ref, (Node) obj);
243 					MToolKit.writeString(out, ref);
244 					for (java.lang.Object objTest : (Node) obj) {
245 						if (objTest instanceof Node) {
246 							final Node node1 = (Node) objTest;
247 							try {
248 								XmlTest.getTest(node1.getTag()).buildMdb(node1, out);
249 							} catch (Throwable ie) {
250 								XmlConfiguration.error("In referenced test '" + ref + "' : "
251 										+ ie.getMessage() + ". Context=" + objTest);
252 							}
253 							break;
254 						}
255 					}
256 				}
257 			}
258 		}
259 
260 		// the action references (not included into MDB)
261 		final Node actions = references.get("actions");
262 		System.out.print("\tactions : references (inlined in MDB)");
263 		if (actions != null) {
264 			for (java.lang.Object obj : actions) {
265 				if (obj instanceof Node) {
266 					final String ref = ((Node) obj).getAttribute("reference-name");
267 					final String globalName = ((Node) obj).getAttribute("name");
268 					// do not accept macro?
269 					if ("false".equals(((Node) obj).getAttribute("macro"))) {
270 						referencedNonMacroActions.add(ref);
271 					}
272 					final List<Node> actionList = new ArrayList<Node>();
273 					boolean firstIsDone = false;
274 					for (java.lang.Object actionI : (Node) obj) {
275 						if (actionI instanceof Node) {
276 							final Node action = (Node) actionI;
277 							// add action to the action list and replace the name of
278 							// sub-actions
279 							if (action.getAttribute("name") == null) {
280 								if (globalName != null) {
281 									if (firstIsDone) {
282 										action.addAttribute(new XmlParser.Attribute("name", "%"
283 												+ globalName));
284 									} else {
285 										action.addAttribute(new XmlParser.Attribute("name",
286 												globalName));
287 										firstIsDone = true;
288 									}
289 								} else if (firstIsDone) {
290 									action
291 											.addAttribute(new XmlParser.Attribute("name", "%" + ref));
292 								} else {
293 									action.addAttribute(new XmlParser.Attribute("name", ref));
294 									firstIsDone = true;
295 								}
296 							} else if (!firstIsDone) {
297 								firstIsDone = true;
298 							}
299 							actionList.add((Node) actionI);
300 						}
301 					}
302 					// add this reference
303 					referencedActions.put(ref, actionList);
304 
305 				}
306 			}
307 		}
308 
309 		// the attachment references (not included into MDB)
310 		final Node attachments = references.get("attachments");
311 		System.out.print(", attachments");
312 		if (attachments != null) {
313 			for (java.lang.Object obj : attachments) {
314 				if (obj instanceof Node) {
315 					final String ref = ((Node) obj).getAttribute("reference-name");
316 					// do not accept macro?
317 					if ("false".equals(((Node) obj).getAttribute("macro"))) {
318 						referencedNonMacroAttachments.add(ref);
319 					}
320 					// add this reference
321 					referencedAttachments.put(ref, (Node) obj);
322 				}
323 			}
324 		}
325 
326 		// action pictures
327 		final Node actionsPictures = node.get("action-pictures");
328 		System.out.print(", pictures");
329 		if (actionsPictures == null) {
330 			out.write(0);
331 			out.write('\0');
332 		} else {
333 			MToolKit.writeString(out, actionsPictures.getAttribute("url"));
334 			out.write(actionsPictures.getNbNodes());
335 			for (java.lang.Object obj : actionsPictures) {
336 				if (obj instanceof Node) {
337 					final Node actionPicture = (Node) obj;
338 					MToolKit.writeString(out, actionPicture.getAttribute("name"));
339 					MToolKit.writeString(out, actionPicture.getAttribute("picture"));
340 				}
341 			}
342 		}
343 
344 		// constraints on abilities linked to action
345 		final Node constraints = node.get("action-constraints");
346 		System.out.println(", constraints");
347 		if (constraints == null) {
348 			out.write(0);
349 		} else {
350 			out.write(constraints.getNbNodes());
351 			for (java.lang.Object obj : constraints) {
352 				if (obj instanceof Node) {
353 					final Node constraint = (Node) obj;
354 					for (java.lang.Object objA : constraint.get("actions")) {
355 						if (objA instanceof Node) {
356 							XmlAction.getAction(((Node) objA).getTag()).buildMdb((Node) objA,
357 									out);
358 							break;
359 						}
360 					}
361 					if ("or".equals(constraint.getAttribute("operation"))) {
362 						IdOperation.OR.serialize(out);
363 					} else {
364 						IdOperation.AND.serialize(out);
365 					}
366 					XmlTest.getTest("test").buildMdb(constraint.get("test"), out);
367 				}
368 			}
369 		}
370 
371 		// objects defined in this TBS
372 		System.out.println("\tobjects...");
373 		final List<Node> objects = node.get("objects").getNodes("object");
374 		out.write(objects.size());
375 		for (Node object : objects) {
376 			// write object
377 			final XmlParser.Attribute objectName = new XmlParser.Attribute("name",
378 					object.getAttribute("name"));
379 			boolean onePresent = false;
380 			for (java.lang.Object modifierTmp : object) {
381 				if (modifierTmp instanceof Node) {
382 					// write the 'has-next' flag
383 					if (onePresent) {
384 						out.write(1);
385 					}
386 
387 					// write the object-modifier
388 					final Node modifier = (Node) modifierTmp;
389 					modifier.addAttribute(objectName);
390 					XmlModifier.getModifier(modifier.getTag()).buildMdb(modifier, out);
391 
392 					// Write the 'paint' flag
393 					if (onePresent) {
394 						out.write(0);
395 					} else {
396 						out.write(1);
397 					}
398 					onePresent = true;
399 				}
400 			}
401 			if (!onePresent) {
402 				// no modifier associated to this object, we write a virtual ONE
403 				ModifierType.REGISTER_MODIFIER.serialize(out);
404 				XmlModifier.buildMdbModifier(object, out);
405 
406 				// Write the modified register index && value
407 				XmlTools.writeConstant(out, 0);
408 				XmlTools.writeConstant(out, 0);
409 				IdOperation.ADD.serialize(out);
410 
411 				// Write the 'paint' flag
412 				out.write(1);
413 			}
414 
415 			// Write the 'has-next' flag
416 			out.write(0);
417 		}
418 
419 		// the abilities references
420 		System.out.println("\tshared abilities...");
421 		Node abilities = references.get("abilities");
422 		out.write(abilities.getNbNodes());
423 		macroActions.push(null);
424 		for (Node ability : abilities.getNodes("ability")) {
425 			String ref = ability.getAttribute("reference-name").toString();
426 			referencedAbilities.put(ref, ability);
427 			MToolKit.writeString(out, ref);
428 			try {
429 				Node node1 = (Node) ability.get(1);
430 				XmlTbs.getTbsComponent(node1.getTag()).buildMdb(node1, out);
431 			} catch (Throwable ie) {
432 				XmlConfiguration.error("Error In referenced ability '" + ref + "' : "
433 						+ ie.getMessage() + "," + ie + ". Context=" + ability);
434 			}
435 		}
436 		macroActions.pop();
437 
438 		// damage types name
439 		System.out.println("\tdamage types...");
440 		Collections.sort(XmlConfiguration.EXPORTED_DAMAGE_TYPES);
441 		int count = XmlConfiguration.EXPORTED_DAMAGE_TYPES.size();
442 		out.write(count);
443 		for (int i = 0; i < count; i++) {
444 			final PairStringInt pair = XmlConfiguration.EXPORTED_DAMAGE_TYPES.get(i);
445 			MToolKit.writeString(out, pair.text);
446 			MToolKit.writeInt16(out, pair.value);
447 		}
448 
449 		/***
450 		 * <ul>
451 		 * CARD'S PART :
452 		 * <li>state pictures
453 		 * <li>tooltip filters
454 		 * <li>exported types name
455 		 * <li>exported sorted properties name
456 		 * <li>implemented cards
457 		 * </ul>
458 		 */
459 
460 		// state pictures
461 		System.out.println("\tstates");
462 		final Node states = node.get("state-pictures");
463 		if (states == null) {
464 			out.write(0);
465 		} else {
466 			out.write(states.getNbNodes());
467 			for (java.lang.Object obj : states) {
468 				if (obj instanceof Node) {
469 					final Node ns = (Node) obj;
470 					MToolKit.writeString(out, ns.getAttribute("picture"));
471 					MToolKit.writeString(out, ns.getAttribute("name"));
472 					MToolKit.writeInt16(out, XmlTools.getInt(ns.getAttribute("state")));
473 					out.write(XmlTools.getInt(ns.getAttribute("index")));
474 					final int x = XmlTools.getInt(ns.getAttribute("x"));
475 					final int y = XmlTools.getInt(ns.getAttribute("y"));
476 					if (MToolKit.getConstant(x) == -1 && MToolKit.getConstant(y) != -1
477 							|| MToolKit.getConstant(x) != -1 && MToolKit.getConstant(y) == -1) {
478 						XmlConfiguration
479 								.error("In state-picture '-1' value is allowed if and only if x AND y have this value.");
480 					}
481 					MToolKit.writeInt16(out, XmlTools.getInt(ns.getAttribute("x")));
482 					MToolKit.writeInt16(out, XmlTools.getInt(ns.getAttribute("y")));
483 					MToolKit.writeInt16(out, XmlTools.getInt(ns.getAttribute("width")));
484 					MToolKit.writeInt16(out, XmlTools.getInt(ns.getAttribute("height")));
485 					XmlTest.getTest("test").buildMdb(node.get("display-test"), out);
486 				}
487 			}
488 		}
489 
490 		// tooltip filters
491 		System.out.println("\ttooltip filters...");
492 		final Node ttFilters = node.get("tooltip-filters");
493 		XmlTools.defaultOnMeTag = false;
494 		if (ttFilters == null) {
495 			out.write(0);
496 		} else {
497 			out.write(ttFilters.getNbNodes());
498 			for (java.lang.Object obj : ttFilters) {
499 				if (obj instanceof Node) {
500 					buildMdbTooltipFilter((Node) obj, out);
501 				}
502 			}
503 		}
504 
505 		// exported types name
506 		System.out.println("\texported type...");
507 		Collections.sort(XmlConfiguration.EXPORTED_TYPES);
508 		int nbTypes = XmlConfiguration.EXPORTED_TYPES.size();
509 		out.write(nbTypes);
510 		for (int i = 0; i < nbTypes; i++) {
511 			final PairStringInt pair = XmlConfiguration.EXPORTED_TYPES.get(i);
512 			MToolKit.writeString(out, pair.text);
513 			MToolKit.writeInt16(out, pair.value);
514 		}
515 
516 		// exported sorted properties name and associated pictures
517 		System.out.println("\tproperties...");
518 		Collections.sort(XmlConfiguration.EXPORTED_PROPERTIES);
519 		final int nbProperties = XmlConfiguration.EXPORTED_PROPERTIES.size();
520 		MToolKit.writeInt16(out, nbProperties);
521 		for (int i = 0; i < nbProperties; i++) {
522 			final PairStringInt pair = XmlConfiguration.EXPORTED_PROPERTIES.get(i);
523 			MToolKit.writeInt16(out, pair.value);
524 			MToolKit.writeString(out, pair.text);
525 		}
526 		MToolKit.writeInt16(out, XmlConfiguration.PROPERTY_PICTURES.size());
527 		for (Map.Entry<Integer, String> entry : XmlConfiguration.PROPERTY_PICTURES
528 				.entrySet()) {
529 			MToolKit.writeInt16(out, entry.getKey());
530 			MToolKit.writeString(out, entry.getValue());
531 		}
532 
533 		/***
534 		 * <ul>
535 		 * STACK MANAGER'S PART :
536 		 * <li>first player registers
537 		 * <li>second player registers
538 		 * <li>abortion zone
539 		 * </ul>
540 		 */
541 		// player registers
542 		System.out.println("\tinitial registers...");
543 		Node registers = node.get("registers-first-player");
544 		final byte[] registersBytes = new byte[IdTokens.PLAYER_REGISTER_SIZE];
545 		if (registers != null) {
546 			final List<Node> list = registers.getNodes("register");
547 			for (Node register : list) {
548 				registersBytes[XmlTools.getInt(register.getAttribute("index"))] = (byte) XmlTools
549 						.getInt(register.getAttribute("value"));
550 			}
551 		}
552 		out.write(registersBytes);
553 		registers = node.get("registers-second-player");
554 		final byte[] registersBytes2 = new byte[IdTokens.PLAYER_REGISTER_SIZE];
555 		if (registers != null) {
556 			final List<Node> list = registers.getNodes("register");
557 			for (Node register : list) {
558 				registersBytes2[XmlTools.getInt(register.getAttribute("index"))] = (byte) XmlTools
559 						.getInt(register.getAttribute("value"));
560 			}
561 		}
562 		out.write(registersBytes2);
563 
564 		/*
565 		 * TODO abortion zone
566 		 */
567 		final Node refAbilities = node.get("abilities");
568 		out.write(XmlTools.getZone(refAbilities.getAttribute("abortionzone")));
569 
570 		// additional costs
571 		System.out.println("\tadditional-costs...");
572 		final List<Node> additionalCosts = node.get("additional-costs").getNodes(
573 				"additional-cost");
574 		out.write(additionalCosts.size());
575 		for (Node additionalCost : additionalCosts) {
576 			XmlTest.getTest("test").buildMdb(additionalCost.get("test"), out);
577 			XmlTbs.writeActionList(additionalCost.get("cost"), out);
578 		}
579 
580 		/***
581 		 * <ul>
582 		 * EVENT MANAGER'S PART :
583 		 * <li>phases
584 		 * <li>turn-structure
585 		 * <li>first phase index
586 		 * </ul>
587 		 */
588 		// phases
589 		System.out.println("\tphases...");
590 		final Node phases = node.get("phases");
591 		out.write(phases.getNbNodes());
592 		for (java.lang.Object obj : phases) {
593 			if (obj instanceof Node) {
594 				buildMdbPhaseType((Node) obj, out);
595 			}
596 		}
597 
598 		// turn structure
599 		final String list = phases.getAttribute("turn-structure");
600 		System.out.println("\tturn structure...");
601 		final String[] arrayid = list.split(" ");
602 		out.write(arrayid.length);
603 		for (String id : arrayid) {
604 			out.write(XmlTools.getAliasValue(id));
605 		}
606 
607 		// first phase index for first turn
608 		out.write(Integer.parseInt(phases.getAttribute("start")));
609 
610 		// state based abilities
611 		System.out.println("\trule abilities...");
612 		out.write(refAbilities.getNbNodes());
613 		for (java.lang.Object obj : refAbilities) {
614 			if (obj instanceof Node) {
615 				try {
616 					XmlTbs.getTbsComponent(((Node) obj).getTag()).buildMdb((Node) obj,
617 							out);
618 				} catch (Throwable ie) {
619 					XmlConfiguration.error("Error in system ability '"
620 							+ ((Node) obj).getAttribute("name") + "' : " + ie.getMessage()
621 							+ ". Context=" + obj);
622 					break;
623 				}
624 			}
625 		}
626 
627 		// static-modifiers
628 		System.out.println("\tstatic-modifiers...");
629 		final Node modifiers = node.get("static-modifiers");
630 		if (modifiers == null) {
631 			out.write(0);
632 		} else {
633 			out.write(modifiers.getNbNodes());
634 			for (java.lang.Object obj : modifiers) {
635 				if (obj instanceof Node) {
636 					XmlModifier.getModifier("static-modifier").buildMdb((Node) obj, out);
637 				}
638 			}
639 		}
640 
641 		// layouts
642 		System.out.println("\tlayouts...");
643 		final Node layouts = node.get("layouts");
644 
645 		// initialize task pane layout
646 		writeTaskPaneElement(layouts.get("common-panel").get("card-details").get(
647 				"properties"), out);
648 
649 		// Sector of play zone for layouts
650 		List<Node> sectors = layouts.get("zones").get("play").getNodes("sector");
651 		out.write(sectors.size());
652 		for (XmlParser.Node sector : sectors) {
653 			XmlTest.getTest("test").buildMdb(sector, out);
654 			MToolKit.writeString(out, sector.getAttribute("constraint"));
655 		}
656 
657 		// Update the shortcut to the first bytes of cards
658 		final long cardBytesPosition = fileOut.getChannel().position();
659 		fileOut.getChannel().position(shortcutCardBytes);
660 		MToolKit.writeInt24(out, (int) cardBytesPosition);
661 		fileOut.getChannel().position(cardBytesPosition);
662 
663 		// cards bytes + names
664 		resolveReferences = true;
665 		System.out.println("Processing cards...");
666 		String xmlFile = node.getAttribute("xmlFile");
667 		try {
668 			final String baseName = FilenameUtils.getBaseName(xmlFile);
669 			XmlTbs.updateMdb(baseName, out);
670 		} catch (Throwable e2) {
671 			XmlConfiguration.error("Error found in cards parsing of rule '" + xmlFile
672 					+ "' : " + e2.getMessage());
673 		}
674 
675 		System.out.println(node.getAttribute("name") + " rules finished");
676 		return 0;
677 	}
678 
679 	private void writeTaskPaneElement(Node node, OutputStream out)
680 			throws IOException {
681 		final List<Node> elements = node.getNodes("menu-element");
682 		final List<Node> attributes = node.getNodes("menu-attribute");
683 		out.write((elements == null ? 0 : elements.size())
684 				+ (attributes == null ? 0 : attributes.size()));
685 		for (java.lang.Object nestedNode : node) {
686 			if (nestedNode instanceof Node) {
687 				final Node task = (Node) nestedNode;
688 				if ("menu-element".equals(task.getTag())) {
689 					out.write(CardPropertiesPanel.NESTED_ELEMENT);
690 					MToolKit.writeString(out, task.getAttribute("name"));
691 					writeTaskPaneElement(task, out);
692 				} else if ("menu-attribute".equals(task.getTag())) {
693 					out.write(CardPropertiesPanel.ATTRIBUTE);
694 					MToolKit.writeString(out, task.getAttribute("type").toLowerCase());
695 					MToolKit.writeString(out, task.getAttribute("name"));
696 					MToolKit.writeString(out, task.getAttribute("value"));
697 				} else {
698 					XmlConfiguration
699 							.warning("non supported task type : " + task.getTag());
700 				}
701 			}
702 		}
703 
704 	}
705 
706 	/***
707 	 * <ul>
708 	 * Structure of stream : Data[size]
709 	 * <li>phase name + '\0' [...]</li>
710 	 * <li>phase identifier [1]</li>
711 	 * <li>empty stack playable, idCards for current player [2]</li>
712 	 * <li>empty stack playable, idCards for non-current player [2]</li>
713 	 * <li>middle resolution, playable idCards for current player [2]</li>
714 	 * <li>middle resolution, playable idCards for non-current player [2]</li>
715 	 * </ul>
716 	 * 
717 	 * @param node
718 	 *          the XML phase type structure
719 	 * @param out
720 	 *          output stream where the card structure will be saved
721 	 * @throws IOException
722 	 *           error during the
723 	 * @see net.sf.firemox.stack.phasetype.PhaseType
724 	 */
725 	public static final void buildMdbPhaseType(Node node, OutputStream out)
726 			throws IOException {
727 		MToolKit.writeString(out, node.getAttribute("name"));
728 		out.write(XmlTools.getAliasValue(node.getAttribute("name")));
729 		MToolKit.writeInt16(out, XmlTools.getIdCards(node
730 				.getAttribute("playable-empty-stack-you")));
731 		MToolKit.writeInt16(out, XmlTools.getIdCards(node
732 				.getAttribute("playable-empty-stack-opponent")));
733 		MToolKit.writeInt16(out, XmlTools.getIdCards(node
734 				.getAttribute("playable-middle-resolution-you")));
735 		MToolKit.writeInt16(out, XmlTools.getIdCards(node
736 				.getAttribute("playable-middle-resolution-opponent")));
737 	}
738 
739 	/***
740 	 * <ul>
741 	 * Structure of stream : Data[size]
742 	 * <li>display powerANDtoughness yes=1,no=0 [1]</li>
743 	 * <li>display states yes=1,no=0 [1]</li>
744 	 * <li>display types yes=1,no=0 [1]</li>
745 	 * <li>display colors yes=1,no=0 [1]</li>
746 	 * <li>display properties yes=1,no=0 [1]</li>
747 	 * <li>display damage yes=1,no=0 [1]</li>
748 	 * <li>filter [...]</li>
749 	 * </ul>
750 	 * 
751 	 * @param node
752 	 *          the XML structure
753 	 * @param out
754 	 *          output stream where the card structure will be saved
755 	 * @throws IOException
756 	 *           error during the writing.
757 	 */
758 	public static final void buildMdbTooltipFilter(Node node, OutputStream out)
759 			throws IOException {
760 
761 		out.write("true".equals(node.getAttribute("powerANDtoughness")) ? 1 : 0);
762 		out.write("true".equals(node.getAttribute("states")) ? 1 : 0);
763 		out.write("true".equals(node.getAttribute("types")) ? 1 : 0);
764 		out.write("true".equals(node.getAttribute("colors")) ? 1 : 0);
765 		out.write("true".equals(node.getAttribute("properties")) ? 1 : 0);
766 		out.write("true".equals(node.getAttribute("damage")) ? 1 : 0);
767 		XmlTest.getTest("test").buildMdb(node.get("filter"), out);
768 	}
769 
770 	/***
771 	 * Push an action (accepting <code>null</code>) for macro.
772 	 * 
773 	 * @param node
774 	 *          the node to give to the next macro.
775 	 */
776 	public static void pushMacroAction(List<Node> node) {
777 		macroActions.push(node);
778 	}
779 
780 	/***
781 	 * Return the last given action to a macro.
782 	 * 
783 	 * @return the last given action to a macro.
784 	 */
785 	public static List<Node> peekMacroAction() {
786 		return macroActions.peek();
787 	}
788 
789 	/***
790 	 * Remove the last given action to a macro.
791 	 */
792 	public static void popMacroAction() {
793 		macroActions.pop();
794 	}
795 
796 	/***
797 	 * Available ability references of this TBS
798 	 */
799 	public static Map<String, Node> referencedAbilities = null;
800 
801 	/***
802 	 * Available test references of this TBS
803 	 */
804 	public static Map<String, Node> referencedTest = null;
805 
806 	/***
807 	 * Available actions references of this TBS
808 	 */
809 	public static Map<String, List<Node>> referencedActions = null;
810 
811 	/***
812 	 * Available actions references of this TBS do not accepting macro
813 	 */
814 	public static Set<String> referencedNonMacroActions = null;
815 
816 	/***
817 	 * Available attachments references of this TBS do not accepting macro
818 	 */
819 	public static Set<String> referencedNonMacroAttachments = null;
820 
821 	/***
822 	 * Available attachments references of this TBS.
823 	 */
824 	public static Map<String, Node> referencedAttachments = null;
825 
826 	/***
827 	 * Available node for action of macro.
828 	 */
829 	public static Stack<List<Node>> macroActions = null;
830 
831 	/***
832 	 * Are the references must be resolved.
833 	 */
834 	public static boolean resolveReferences;
835 }