1 /*
2  * See Licensing and Copyright notice in naev.h
3  */
4 
5 /**
6  * @file unidiff.c
7  *
8  * @brief Handles the application and removal of 'diffs' to the universe.
9  *
10  * Diffs allow changing planets, fleets, factions, etc... in the universe.
11  *  These are meant to be applied after the player triggers them, mostly
12  *  through missions.
13  */
14 
15 
16 #include "unidiff.h"
17 
18 #include "naev.h"
19 
20 #include <stdlib.h>
21 #include "nstring.h"
22 
23 #include "log.h"
24 #include "nxml.h"
25 #include "space.h"
26 #include "ndata.h"
27 #include "fleet.h"
28 #include "map_overlay.h"
29 
30 
31 #define CHUNK_SIZE      32 /**< Size of chunk to allocate. */
32 
33 
34 /**
35  * @enum UniHunkTargetType_t
36  *
37  * @brief Represents the possible hunk targets.
38  */
39 typedef enum UniHunkTargetType_ {
40    HUNK_TARGET_NONE,
41    HUNK_TARGET_SYSTEM,
42    HUNK_TARGET_ASSET,
43    HUNK_TARGET_TECH,
44    HUNK_TARGET_FACTION,
45 } UniHunkTargetType_t;
46 
47 
48 /**
49  * @struct UniHunkTarget_t
50  *
51  * @brief Represents the hunk's target.
52  */
53 typedef struct UniHunkTarget_ {
54    UniHunkTargetType_t type; /**< Type of hunk target. */
55    union {
56       char *name; /**< Name of the target. */
57    } u; /**< Union of possible target types. */
58 } UniHunkTarget_t;
59 
60 
61 /**
62  * @enum UniHunkType_t
63  *
64  * @brief Represents the different type of hunk actions.
65  */
66 typedef enum UniHunkType_ {
67    HUNK_TYPE_NONE,
68    /* Target should be system. */
69    HUNK_TYPE_ASSET_ADD,
70    HUNK_TYPE_ASSET_REMOVE,
71    HUNK_TYPE_ASSET_BLACKMARKET,
72    HUNK_TYPE_ASSET_LEGALMARKET,
73    HUNK_TYPE_JUMP_ADD,
74    HUNK_TYPE_JUMP_REMOVE,
75    /* Target should be tech. */
76    HUNK_TYPE_TECH_ADD,
77    HUNK_TYPE_TECH_REMOVE,
78    /* Target should be asset. */
79    HUNK_TYPE_ASSET_FACTION,
80    HUNK_TYPE_ASSET_FACTION_REMOVE, /* For internal usage. */
81    /* Target should be faction. */
82    HUNK_TYPE_FACTION_VISIBLE,
83    HUNK_TYPE_FACTION_INVISIBLE,
84    HUNK_TYPE_FACTION_ALLY,
85    HUNK_TYPE_FACTION_ENEMY,
86    HUNK_TYPE_FACTION_NEUTRAL,
87    HUNK_TYPE_FACTION_REALIGN, /* For internal usage. */
88 } UniHunkType_t;
89 
90 
91 /**
92  * @struct UniHunk_t
93  *
94  * @brief Represents a single hunk in the diff.
95  */
96 typedef struct UniHunk_ {
97    UniHunkTarget_t target; /**< Hunk's target. */
98 
99    UniHunkType_t type; /**< Type of hunk it is. */
100    xmlNodePtr node; /**< Parent node. */
101    union {
102       char *name;
103    } u; /**< Actual data to patch. */
104    union {
105       char *name;
106       int data;
107    } o; /** Old data to possibly replace. */
108 } UniHunk_t;
109 
110 
111 /**
112  * @struct UniDiff_t
113  *
114  * @brief Represents each Universe Diff.
115  */
116 typedef struct UniDiff_ {
117    char *name; /**< Name of the diff. */
118 
119    UniHunk_t *applied; /**< Applied hunks. */
120    int napplied; /**< Number of applied hunks. */
121    int mapplied; /**< Memory of applied hunks. */
122 
123    UniHunk_t *failed; /**< Failed hunks. */
124    int nfailed; /**< Number of failed hunks. */
125    int mfailed; /**< Memory of failed hunks. */
126 } UniDiff_t;
127 
128 
129 /*
130  * Diff stack.
131  */
132 static UniDiff_t *diff_stack = NULL; /**< Currently applied universe diffs. */
133 static int diff_nstack = 0; /**< Number of diffs in the stack. */
134 static int diff_mstack = 0; /**< Currently allocated diffs. */
135 
136 
137 /*
138  * Prototypes.
139  */
140 static UniDiff_t* diff_get( const char *name );
141 static UniDiff_t *diff_newDiff (void);
142 static int diff_removeDiff( UniDiff_t *diff );
143 static int diff_patchSystem( UniDiff_t *diff, xmlNodePtr node );
144 static int diff_patchTech( UniDiff_t *diff, xmlNodePtr node );
145 static int diff_patch( xmlNodePtr parent );
146 static int diff_patchHunk( UniHunk_t *hunk );
147 static void diff_hunkFailed( UniDiff_t *diff, UniHunk_t *hunk );
148 static void diff_hunkSuccess( UniDiff_t *diff, UniHunk_t *hunk );
149 static void diff_cleanup( UniDiff_t *diff );
150 static void diff_cleanupHunk( UniHunk_t *hunk );
151 /* Externed. */
152 int diff_save( xmlTextWriterPtr writer ); /**< Used in save.c */
153 int diff_load( xmlNodePtr parent ); /**< Used in save.c */
154 
155 
156 /**
157  * @brief Checks if a diff is currently applied.
158  *
159  *    @param name Diff to check.
160  *    @return 0 if it's not applied, 1 if it is.
161  */
diff_isApplied(const char * name)162 int diff_isApplied( const char *name )
163 {
164    if (diff_get(name) != NULL)
165       return 1;
166    return 0;
167 }
168 
169 
170 /**
171  * @brief Gets a diff by name.
172  *
173  *    @param name Name of the diff to get.
174  *    @return The diff if found or NULL if not found.
175  */
diff_get(const char * name)176 static UniDiff_t* diff_get( const char *name )
177 {
178    int i;
179    for (i=0; i<diff_nstack; i++)
180       if (strcmp(diff_stack[i].name,name)==0)
181          return &diff_stack[i];
182    return NULL;
183 }
184 
185 
186 /**
187  * @brief Applies a diff to the universe.
188  *
189  *    @param name Diff to apply.
190  *    @return 0 on success.
191  */
diff_apply(const char * name)192 int diff_apply( const char *name )
193 {
194    xmlNodePtr node;
195    xmlDocPtr doc;
196    uint32_t bufsize;
197    char *buf;
198    char *diffname;
199 
200    /* Check if already applied. */
201    if (diff_isApplied(name))
202       return 0;
203 
204    buf = ndata_read( DIFF_DATA_PATH, &bufsize );
205    doc = xmlParseMemory( buf, bufsize );
206 
207    node = doc->xmlChildrenNode;
208    if (strcmp((char*)node->name,"unidiffs")) {
209       ERR("Malformed unidiff file: missing root element 'unidiffs'");
210       return 0;
211    }
212 
213    node = node->xmlChildrenNode; /* first system node */
214    if (node == NULL) {
215       ERR("Malformed unidiff file: does not contain elements");
216       return 0;
217    }
218 
219    do {
220       if (xml_isNode(node,"unidiff")) {
221          /* Check to see if it's the diff we're looking for. */
222          xmlr_attr(node,"name",diffname);
223          if (strcmp(diffname,name)==0) {
224             /* Apply it. */
225             diff_patch( node );
226 
227             /* Clean up. */
228             free(diffname);
229             xmlFreeDoc(doc);
230             free(buf);
231 
232             economy_execQueued();
233 
234             return 0;
235          }
236          free(diffname);
237       }
238    } while (xml_nextNode(node));
239 
240    /* More clean up. */
241    xmlFreeDoc(doc);
242    free(buf);
243 
244    WARN("UniDiff '%s' not found in "DIFF_DATA_PATH".", name);
245    return -1;
246 }
247 
248 
249 /**
250  * @brief Patches a system.
251  *
252  *    @param diff Diff that is doing the patching.
253  *    @param node Node containing the system.
254  *    @return 0 on success.
255  */
diff_patchSystem(UniDiff_t * diff,xmlNodePtr node)256 static int diff_patchSystem( UniDiff_t *diff, xmlNodePtr node )
257 {
258    UniHunk_t base, hunk;
259    xmlNodePtr cur;
260    char *buf;
261 
262    /* Set the target. */
263    memset(&base, 0, sizeof(UniHunk_t));
264    base.target.type = HUNK_TARGET_SYSTEM;
265    xmlr_attr(node,"name",base.target.u.name);
266    if (base.target.u.name==NULL) {
267       WARN("Unidiff '%s' has a system node without a 'name' tag, not applying.", diff->name);
268       return -1;
269    }
270 
271    /* Now parse the possible changes. */
272    cur = node->xmlChildrenNode;
273    do {
274       xml_onlyNodes(cur);
275       if (xml_isNode(cur,"asset")) {
276          hunk.target.type = base.target.type;
277          hunk.target.u.name = strdup(base.target.u.name);
278 
279          /* Get the asset to modify. */
280          xmlr_attr(cur,"name",hunk.u.name);
281 
282          /* Get the type. */
283          buf = xml_get(cur);
284          if (buf==NULL) {
285             WARN("Unidiff '%s': Null hunk type.", diff->name);
286             continue;
287          }
288          if (strcmp(buf,"add")==0)
289             hunk.type = HUNK_TYPE_ASSET_ADD;
290          else if (strcmp(buf,"remove")==0)
291             hunk.type = HUNK_TYPE_ASSET_REMOVE;
292          else if (strcmp(buf,"blackmarket")==0)
293             hunk.type = HUNK_TYPE_ASSET_BLACKMARKET;
294          else if (strcmp(buf,"legalmarket")==0)
295             hunk.type = HUNK_TYPE_ASSET_LEGALMARKET;
296          else
297             WARN("Unidiff '%s': Unknown hunk type '%s' for asset '%s'.", diff->name, buf, hunk.u.name);
298 
299          /* Apply diff. */
300          if (diff_patchHunk( &hunk ) < 0)
301             diff_hunkFailed( diff, &hunk );
302          else
303             diff_hunkSuccess( diff, &hunk );
304          continue;
305       }
306       else if (xml_isNode(cur,"jump")) {
307          hunk.target.type = base.target.type;
308          hunk.target.u.name = strdup(base.target.u.name);
309 
310          /* Get the jump point to modify. */
311          xmlr_attr(cur,"target",hunk.u.name);
312 
313          /* Get the type. */
314          buf = xml_get(cur);
315          if (buf==NULL) {
316             WARN("Unidiff '%s': Null hunk type.", diff->name);
317             continue;
318          }
319 
320          if (strcmp(buf,"add")==0)
321             hunk.type = HUNK_TYPE_JUMP_ADD;
322          else if (strcmp(buf,"remove")==0)
323             hunk.type = HUNK_TYPE_JUMP_REMOVE;
324          else
325             WARN("Unidiff '%s': Unknown hunk type '%s' for jump '%s'.", diff->name, buf, hunk.u.name);
326 
327          hunk.node = cur;
328 
329          /* Apply diff. */
330          if (diff_patchHunk( &hunk ) < 0)
331             diff_hunkFailed( diff, &hunk );
332          else
333             diff_hunkSuccess( diff, &hunk );
334          continue;
335       }
336       WARN("Unidiff '%s' has unknown node '%s'.", diff->name, node->name);
337    } while (xml_nextNode(cur));
338 
339    /* Clean up some stuff. */
340    free(base.target.u.name);
341    base.target.u.name = NULL;
342 
343    return 0;
344 }
345 
346 
347 /**
348  * @brief Patches a tech.
349  *
350  *    @param diff Diff that is doing the patching.
351  *    @param node Node containing the tech.
352  *    @return 0 on success.
353  */
diff_patchTech(UniDiff_t * diff,xmlNodePtr node)354 static int diff_patchTech( UniDiff_t *diff, xmlNodePtr node )
355 {
356    UniHunk_t base, hunk;
357    xmlNodePtr cur;
358 
359    /* Set the target. */
360    memset(&base, 0, sizeof(UniHunk_t));
361    base.target.type = HUNK_TARGET_TECH;
362    xmlr_attr(node,"name",base.target.u.name);
363    if (base.target.u.name==NULL) {
364       WARN("Unidiff '%s' has an target node without a 'name' tag", diff->name);
365       return -1;
366    }
367 
368    /* Now parse the possible changes. */
369    cur = node->xmlChildrenNode;
370    do {
371       xml_onlyNodes(cur);
372       if (xml_isNode(cur,"add")) {
373          hunk.target.type = base.target.type;
374          hunk.target.u.name = strdup(base.target.u.name);
375 
376          /* Outfit type is constant. */
377          hunk.type = HUNK_TYPE_TECH_ADD;
378 
379          /* Get the data. */
380          hunk.u.name = xml_getStrd(cur);
381 
382          /* Apply diff. */
383          if (diff_patchHunk( &hunk ) < 0)
384             diff_hunkFailed( diff, &hunk );
385          else
386             diff_hunkSuccess( diff, &hunk );
387          continue;
388       }
389       else if (xml_isNode(cur,"remove")) {
390          hunk.target.type = base.target.type;
391          hunk.target.u.name = strdup(base.target.u.name);
392 
393          /* Outfit type is constant. */
394          hunk.type = HUNK_TYPE_TECH_REMOVE;
395 
396          /* Get the data. */
397          hunk.u.name = xml_getStrd(cur);
398 
399          /* Apply diff. */
400          if (diff_patchHunk( &hunk ) < 0)
401             diff_hunkFailed( diff, &hunk );
402          else
403             diff_hunkSuccess( diff, &hunk );
404          continue;
405       }
406       WARN("Unidiff '%s' has unknown node '%s'.", diff->name, node->name);
407    } while (xml_nextNode(cur));
408 
409    /* Clean up some stuff. */
410    free(base.target.u.name);
411    base.target.u.name = NULL;
412 
413    return 0;
414 }
415 
416 
417 /**
418  * @brief Patches a asset.
419  *
420  *    @param diff Diff that is doing the patching.
421  *    @param node Node containing the asset.
422  *    @return 0 on success.
423  */
diff_patchAsset(UniDiff_t * diff,xmlNodePtr node)424 static int diff_patchAsset( UniDiff_t *diff, xmlNodePtr node )
425 {
426    UniHunk_t base, hunk;
427    xmlNodePtr cur;
428 
429    /* Set the target. */
430    memset(&base, 0, sizeof(UniHunk_t));
431    base.target.type = HUNK_TARGET_ASSET;
432    xmlr_attr(node,"name",base.target.u.name);
433    if (base.target.u.name==NULL) {
434       WARN("Unidiff '%s' has an target node without a 'name' tag", diff->name);
435       return -1;
436    }
437 
438    /* Now parse the possible changes. */
439    cur = node->xmlChildrenNode;
440    do {
441       xml_onlyNodes(cur);
442       if (xml_isNode(cur,"faction")) {
443          hunk.target.type = base.target.type;
444          hunk.target.u.name = strdup(base.target.u.name);
445 
446          /* Outfit type is constant. */
447          hunk.type = HUNK_TYPE_ASSET_FACTION;
448 
449          /* Get the data. */
450          hunk.u.name = xml_getStrd(cur);
451 
452          /* Apply diff. */
453          if (diff_patchHunk( &hunk ) < 0)
454             diff_hunkFailed( diff, &hunk );
455          else
456             diff_hunkSuccess( diff, &hunk );
457          continue;
458       }
459       WARN("Unidiff '%s' has unknown node '%s'.", diff->name, node->name);
460    } while (xml_nextNode(cur));
461 
462    /* Clean up some stuff. */
463    free(base.target.u.name);
464    base.target.u.name = NULL;
465 
466    return 0;
467 }
468 
469 
470 /**
471  * @brief Patches a faction.
472  *
473  *    @param diff Diff that is doing the patching.
474  *    @param node Node containing the asset.
475  *    @return 0 on success.
476  */
diff_patchFaction(UniDiff_t * diff,xmlNodePtr node)477 static int diff_patchFaction( UniDiff_t *diff, xmlNodePtr node )
478 {
479    UniHunk_t base, hunk;
480    xmlNodePtr cur;
481    char *buf;
482 
483    /* Set the target. */
484    memset(&base, 0, sizeof(UniHunk_t));
485    base.target.type = HUNK_TARGET_FACTION;
486    xmlr_attr(node,"name",base.target.u.name);
487    if (base.target.u.name==NULL) {
488       WARN("Unidiff '%s' has an target node without a 'name' tag", diff->name);
489       return -1;
490    }
491 
492    /* Now parse the possible changes. */
493    cur = node->xmlChildrenNode;
494    do {
495       xml_onlyNodes(cur);
496       if (xml_isNode(cur,"visible")) {
497          hunk.target.type = base.target.type;
498          hunk.target.u.name = strdup(base.target.u.name);
499 
500          /* Faction type is constant. */
501          hunk.type = HUNK_TYPE_FACTION_VISIBLE;
502 
503          /* There is no name. */
504          hunk.u.name = NULL;
505 
506          /* Apply diff. */
507          if (diff_patchHunk( &hunk ) < 0)
508             diff_hunkFailed( diff, &hunk );
509          else
510             diff_hunkSuccess( diff, &hunk );
511          continue;
512       }
513       else if (xml_isNode(cur,"invisible")) {
514          hunk.target.type = base.target.type;
515          hunk.target.u.name = strdup(base.target.u.name);
516 
517          /* Faction type is constant. */
518          hunk.type = HUNK_TYPE_FACTION_INVISIBLE;
519 
520          /* There is no name. */
521          hunk.u.name = NULL;
522 
523          /* Apply diff. */
524          if (diff_patchHunk( &hunk ) < 0)
525             diff_hunkFailed( diff, &hunk );
526          else
527             diff_hunkSuccess( diff, &hunk );
528          continue;
529       }
530       else if (xml_isNode(cur,"faction")) {
531          hunk.target.type = base.target.type;
532          hunk.target.u.name = strdup(base.target.u.name);
533 
534          /* Get the faction to set the association of. */
535          xmlr_attr(cur,"name",hunk.u.name);
536 
537          /* Get the type. */
538          buf = xml_get(cur);
539          if (buf==NULL) {
540             WARN("Unidiff '%s': Null hunk type.", diff->name);
541             continue;
542          }
543          if (strcmp(buf,"ally")==0)
544             hunk.type = HUNK_TYPE_FACTION_ALLY;
545          else if (strcmp(buf,"enemy")==0)
546             hunk.type = HUNK_TYPE_FACTION_ENEMY;
547          else if (strcmp(buf,"neutral")==0)
548             hunk.type = HUNK_TYPE_FACTION_NEUTRAL;
549          else
550             WARN("Unidiff '%s': Unknown hunk type '%s' for faction '%s'.", diff->name, buf, hunk.u.name);
551 
552          /* Apply diff. */
553          if (diff_patchHunk( &hunk ) < 0)
554             diff_hunkFailed( diff, &hunk );
555          else
556             diff_hunkSuccess( diff, &hunk );
557          continue;
558       }
559       WARN("Unidiff '%s' has unknown node '%s'.", diff->name, node->name);
560    } while (xml_nextNode(cur));
561 
562    /* Clean up some stuff. */
563    free(base.target.u.name);
564    base.target.u.name = NULL;
565 
566    return 0;
567 }
568 
569 
570 /**
571  * @brief Actually applies a diff in XML node form.
572  *
573  *    @param parent Node containing the diff information.
574  *    @return 0 on success.
575  */
diff_patch(xmlNodePtr parent)576 static int diff_patch( xmlNodePtr parent )
577 {
578    int i, univ_update;
579    UniDiff_t *diff;
580    UniHunk_t *fail;
581    xmlNodePtr node;
582    char *target;
583 
584    /* Prepare it. */
585    diff = diff_newDiff();
586    memset(diff, 0, sizeof(UniDiff_t));
587    xmlr_attr(parent,"name",diff->name);
588 
589    /* Whether or not we need to update the universe. */
590    univ_update = 0;
591 
592    node = parent->xmlChildrenNode;
593    do {
594       xml_onlyNodes(node);
595       if (xml_isNode(node,"system")) {
596          univ_update = 1;
597          diff_patchSystem( diff, node );
598       }
599       else if (xml_isNode(node, "tech"))
600          diff_patchTech( diff, node );
601       else if (xml_isNode(node, "asset")) {
602          univ_update = 1;
603          diff_patchAsset( diff, node );
604       }
605       else if (xml_isNode(node, "faction")) {
606          univ_update = 1;
607          diff_patchFaction( diff, node );
608       }
609       else
610          WARN("Unidiff '%s' has unknown node '%s'.", diff->name, node->name);
611    } while (xml_nextNode(node));
612 
613    if (diff->nfailed > 0) {
614       WARN("Unidiff '%s' failed to apply %d hunks.", diff->name, diff->nfailed);
615       for (i=0; i<diff->nfailed; i++) {
616          fail   = &diff->failed[i];
617          target = fail->target.u.name;
618          switch (fail->type) {
619             case HUNK_TYPE_ASSET_ADD:
620                WARN("   [%s] asset add: '%s'", target, fail->u.name);
621                break;
622             case HUNK_TYPE_ASSET_REMOVE:
623                WARN("   [%s] asset remove: '%s'", target, fail->u.name);
624                break;
625             case HUNK_TYPE_ASSET_BLACKMARKET:
626                WARN("   [%s] asset blackmarket: '%s'", target, fail->u.name);
627                break;
628             case HUNK_TYPE_ASSET_LEGALMARKET:
629                WARN("   [%s] asset legalmarket: '%s'", target, fail->u.name);
630                break;
631             case HUNK_TYPE_JUMP_ADD:
632                WARN("   [%s] jump add: '%s'", target, fail->u.name);
633                break;
634             case HUNK_TYPE_JUMP_REMOVE:
635                WARN("   [%s] jump remove: '%s'", target, fail->u.name);
636                break;
637             case HUNK_TYPE_TECH_ADD:
638                WARN("   [%s] tech add: '%s'", target,
639                      fail->u.name );
640                break;
641             case HUNK_TYPE_TECH_REMOVE:
642                WARN("   [%s] tech remove: '%s'", target,
643                      fail->u.name );
644                break;
645             case HUNK_TYPE_ASSET_FACTION:
646                WARN("   [%s] asset faction: '%s'", target,
647                      fail->u.name );
648                break;
649             case HUNK_TYPE_ASSET_FACTION_REMOVE:
650                WARN("   [%s] asset faction removal: '%s'", target,
651                      fail->u.name );
652                break;
653             case HUNK_TYPE_FACTION_VISIBLE:
654                WARN("   [%s] faction visible: '%s'", target,
655                      fail->u.name );
656                break;
657             case HUNK_TYPE_FACTION_INVISIBLE:
658                WARN("   [%s] faction invisible: '%s'", target,
659                      fail->u.name );
660                break;
661             case HUNK_TYPE_FACTION_ALLY:
662                WARN("   [%s] faction set ally: '%s'", target,
663                      fail->u.name );
664                break;
665             case HUNK_TYPE_FACTION_ENEMY:
666                WARN("   [%s] faction set enemy: '%s'", target,
667                      fail->u.name );
668                break;
669             case HUNK_TYPE_FACTION_NEUTRAL:
670                WARN("   [%s] faction set neutral: '%s'", target,
671                      fail->u.name );
672                break;
673             case HUNK_TYPE_FACTION_REALIGN:
674                WARN("   [%s] faction alignment reset: '%s'", target,
675                      fail->u.name );
676                break;
677 
678             default:
679                WARN("   unknown hunk '%d'", fail->type);
680                break;
681          }
682       }
683    }
684 
685    /* Prune presences if necessary. */
686    if (univ_update)
687       space_reconstructPresences();
688 
689    /* Update overlay map just in case. */
690    ovr_refresh();
691    return 0;
692 }
693 
694 
695 /**
696  * @brief Applies a hunk and adds it to the diff.
697  *
698  *    @param diff Diff to which the hunk belongs.
699  *    @param hunk Hunk to apply.
700  *    @return 0 on success.
701  */
diff_patchHunk(UniHunk_t * hunk)702 static int diff_patchHunk( UniHunk_t *hunk )
703 {
704    Planet *p;
705    int a, b;
706 
707    switch (hunk->type) {
708 
709       /* Adding an asset. */
710       case HUNK_TYPE_ASSET_ADD:
711          planet_updateLand( planet_get(hunk->u.name) );
712          return system_addPlanet( system_get(hunk->target.u.name), hunk->u.name );
713       /* Removing an asset. */
714       case HUNK_TYPE_ASSET_REMOVE:
715          return system_rmPlanet( system_get(hunk->target.u.name), hunk->u.name );
716       /* Making an asset a black market. */
717       case HUNK_TYPE_ASSET_BLACKMARKET:
718          planet_addService( planet_get(hunk->u.name), PLANET_SERVICE_BLACKMARKET );
719          return 0;
720       /* Making an asset a legal market. */
721       case HUNK_TYPE_ASSET_LEGALMARKET:
722          planet_rmService( planet_get(hunk->u.name), PLANET_SERVICE_BLACKMARKET );
723          return 0;
724 
725       /* Adding a Jump. */
726       case HUNK_TYPE_JUMP_ADD:
727          return system_addJumpDiff( system_get(hunk->target.u.name), hunk->node );
728       /* Removing a jump. */
729       case HUNK_TYPE_JUMP_REMOVE:
730          return system_rmJump( system_get(hunk->target.u.name), hunk->u.name );
731 
732       /* Adding a tech. */
733       case HUNK_TYPE_TECH_ADD:
734          return tech_addItem( hunk->target.u.name, hunk->u.name );
735       /* Removing a tech. */
736       case HUNK_TYPE_TECH_REMOVE:
737          return tech_rmItem( hunk->target.u.name, hunk->u.name );
738 
739       /* Changing asset faction. */
740       case HUNK_TYPE_ASSET_FACTION:
741          p = planet_get( hunk->target.u.name );
742          if (p==NULL)
743             return -1;
744          hunk->o.name = faction_name( p->faction );
745          return planet_setFaction( p, faction_get(hunk->u.name) );
746       case HUNK_TYPE_ASSET_FACTION_REMOVE:
747          return planet_setFaction( planet_get(hunk->target.u.name), faction_get(hunk->o.name) );
748 
749       /* Making a faction visible. */
750       case HUNK_TYPE_FACTION_VISIBLE:
751          return faction_setInvisible( faction_get(hunk->target.u.name), 0 );
752       /* Making a faction invisible. */
753       case HUNK_TYPE_FACTION_INVISIBLE:
754          return faction_setInvisible( faction_get(hunk->target.u.name), 1 );
755       /* Making two factions allies. */
756       case HUNK_TYPE_FACTION_ALLY:
757          a = faction_get( hunk->target.u.name );
758          b = faction_get( hunk->u.name );
759          if (areAllies(a, b))
760             hunk->o.data = 'A';
761          else if (areEnemies(a, b))
762             hunk->o.data = 'E';
763          else
764             hunk->o.data = 0;
765          faction_addAlly( a, b );
766          faction_addAlly( b, a );
767          return 0;
768       /* Making two factions enemies. */
769       case HUNK_TYPE_FACTION_ENEMY:
770          a = faction_get( hunk->target.u.name );
771          b = faction_get( hunk->u.name );
772          if (areAllies(a, b))
773             hunk->o.data = 'A';
774          else if (areEnemies(a, b))
775             hunk->o.data = 'E';
776          else
777             hunk->o.data = 0;
778          faction_addEnemy( a, b );
779          faction_addEnemy( b, a );
780          return 0;
781       /* Making two factions neutral (removing enemy/ally statuses). */
782       case HUNK_TYPE_FACTION_NEUTRAL:
783          a = faction_get( hunk->target.u.name );
784          b = faction_get( hunk->u.name );
785          if (areAllies(a, b))
786             hunk->o.data = 'A';
787          else if (areEnemies(a, b))
788             hunk->o.data = 'E';
789          else
790             hunk->o.data = 0;
791          faction_rmAlly( a, b );
792          faction_rmAlly( b, a );
793          faction_rmEnemy( a, b );
794          faction_rmEnemy( b, a );
795          return 0;
796       /* Resetting the alignment state of two factions. */
797       case HUNK_TYPE_FACTION_REALIGN:
798          a = faction_get( hunk->target.u.name );
799          b = faction_get( hunk->u.name );
800          if (hunk->o.data == 'A') {
801             faction_rmEnemy(a, b);
802             faction_rmEnemy(b, a);
803             faction_addAlly(a, b);
804             faction_addAlly(b, a);
805          }
806          else if (hunk->o.data == 'E') {
807             faction_rmAlly(a, b);
808             faction_rmAlly(b, a);
809             faction_addEnemy(a, b);
810             faction_addAlly(b, a);
811          }
812          else {
813             faction_rmAlly( a, b );
814             faction_rmAlly( b, a );
815             faction_rmEnemy( a, b );
816             faction_rmEnemy( b, a );
817          }
818          return 0;
819 
820       default:
821          WARN("Unknown hunk type '%d'.", hunk->type);
822          break;
823    }
824 
825    return -1;
826 }
827 
828 
829 /**
830  * @brief Adds a hunk to the failed list.
831  *
832  *    @param diff Diff to add hunk to.
833  *    @param hunk Hunk that failed to apply.
834  */
diff_hunkFailed(UniDiff_t * diff,UniHunk_t * hunk)835 static void diff_hunkFailed( UniDiff_t *diff, UniHunk_t *hunk )
836 {
837    if (diff == NULL)
838       return;
839 
840    diff->nfailed++;
841    if (diff->nfailed > diff->mfailed) {
842       diff->mfailed += CHUNK_SIZE;
843       diff->failed = realloc(diff->failed, sizeof(UniHunk_t) * diff->mfailed);
844    }
845    diff->failed[diff->nfailed-1] = *hunk;
846 }
847 
848 
849 /**
850  * @brief Adds a hunk to the applied list.
851  *
852  *    @param diff Diff to add hunk to.
853  *    @param hunk Hunk that applied correctly.
854  */
diff_hunkSuccess(UniDiff_t * diff,UniHunk_t * hunk)855 static void diff_hunkSuccess( UniDiff_t *diff, UniHunk_t *hunk )
856 {
857    if (diff == NULL)
858       return;
859 
860    diff->napplied++;
861    if (diff->napplied > diff->mapplied) {
862       diff->mapplied += CHUNK_SIZE;
863       diff->applied = realloc(diff->applied, sizeof(UniHunk_t) * diff->mapplied);
864    }
865    diff->applied[diff->napplied-1] = *hunk;
866 }
867 
868 
869 /**
870  * @brief Removes a diff from the universe.
871  *
872  *    @param name Diff to remove.
873  */
diff_remove(const char * name)874 void diff_remove( const char *name )
875 {
876    UniDiff_t *diff;
877 
878    /* Check if already applied. */
879    diff = diff_get(name);
880    if (diff == NULL)
881       return;
882 
883    diff_removeDiff(diff);
884 
885    economy_execQueued();
886 }
887 
888 
889 /**
890  * @brief Removes all active diffs.
891  */
diff_clear(void)892 void diff_clear (void)
893 {
894    while (diff_nstack > 0)
895       diff_removeDiff(&diff_stack[diff_nstack-1]);
896 
897    economy_execQueued();
898 }
899 
900 
901 /**
902  * @brief Creates a new UniDiff_t for usage.
903  *
904  *    @return A newly created UniDiff_t.
905  */
diff_newDiff(void)906 static UniDiff_t *diff_newDiff (void)
907 {
908    /* Check if needs initialization. */
909    if (diff_stack == NULL) {
910       diff_mstack = CHUNK_SIZE;
911       diff_stack = malloc(diff_mstack * sizeof(UniDiff_t));
912       diff_nstack = 1;
913       return &diff_stack[0];
914    }
915 
916    diff_nstack++;
917    /* Check if need to grow. */
918    if (diff_nstack > diff_mstack) {
919       diff_mstack += CHUNK_SIZE;
920       diff_stack = realloc(diff_stack, diff_mstack * sizeof(UniDiff_t));
921    }
922 
923    return &diff_stack[diff_nstack-1];
924 }
925 
926 
927 /**
928  * @brief Removes a diff.
929  *
930  *    @param diff Diff to remove.
931  *    @return 0 on success.
932  */
diff_removeDiff(UniDiff_t * diff)933 static int diff_removeDiff( UniDiff_t *diff )
934 {
935    int i;
936    UniHunk_t hunk;
937 
938    for (i=0; i<diff->napplied; i++) {
939       hunk = diff->applied[i];
940       /* Invert the type for reverting. */
941       switch (hunk.type) {
942          case HUNK_TYPE_ASSET_ADD:
943             hunk.type = HUNK_TYPE_ASSET_REMOVE;
944             break;
945          case HUNK_TYPE_ASSET_REMOVE:
946             hunk.type = HUNK_TYPE_ASSET_ADD;
947             break;
948 
949          case HUNK_TYPE_ASSET_BLACKMARKET:
950             hunk.type = HUNK_TYPE_ASSET_LEGALMARKET;
951             break;
952          case HUNK_TYPE_ASSET_LEGALMARKET:
953             hunk.type = HUNK_TYPE_ASSET_BLACKMARKET;
954             break;
955 
956          case HUNK_TYPE_JUMP_ADD:
957             hunk.type = HUNK_TYPE_JUMP_REMOVE;
958             break;
959          case HUNK_TYPE_JUMP_REMOVE:
960             hunk.type = HUNK_TYPE_JUMP_ADD;
961             break;
962 
963          case HUNK_TYPE_TECH_ADD:
964             hunk.type = HUNK_TYPE_TECH_REMOVE;
965             break;
966          case HUNK_TYPE_TECH_REMOVE:
967             hunk.type = HUNK_TYPE_TECH_ADD;
968             break;
969 
970          case HUNK_TYPE_ASSET_FACTION:
971             hunk.type = HUNK_TYPE_ASSET_FACTION_REMOVE;
972             break;
973 
974          case HUNK_TYPE_FACTION_VISIBLE:
975             hunk.type = HUNK_TYPE_FACTION_INVISIBLE;
976             break;
977          case HUNK_TYPE_FACTION_INVISIBLE:
978             hunk.type = HUNK_TYPE_FACTION_VISIBLE;
979             break;
980 
981          case HUNK_TYPE_FACTION_ALLY:
982             hunk.type = HUNK_TYPE_FACTION_REALIGN;
983             break;
984          case HUNK_TYPE_FACTION_ENEMY:
985             hunk.type = HUNK_TYPE_FACTION_REALIGN;
986             break;
987          case HUNK_TYPE_FACTION_NEUTRAL:
988             hunk.type = HUNK_TYPE_FACTION_REALIGN;
989             break;
990 
991          default:
992             WARN("Unknown Hunk type '%d'.", hunk.type);
993             continue;
994       }
995 
996       if (diff_patchHunk(&hunk))
997          WARN("Failed to remove hunk type '%d'.", hunk.type);
998    }
999 
1000    diff_cleanup(diff);
1001    diff_nstack--;
1002    i = diff - diff_stack;
1003    memmove(&diff_stack[i], &diff_stack[i+1], sizeof(UniDiff_t) * (diff_nstack-i));
1004 
1005    return 0;
1006 }
1007 
1008 
1009 /**
1010  * @brief Cleans up a diff.
1011  *
1012  *    @param diff Diff to clean up.
1013  */
diff_cleanup(UniDiff_t * diff)1014 static void diff_cleanup( UniDiff_t *diff )
1015 {
1016    int i;
1017 
1018    free(diff->name);
1019    for (i=0; i<diff->napplied; i++)
1020       diff_cleanupHunk(&diff->applied[i]);
1021    if (diff->applied != NULL)
1022       free(diff->applied);
1023    for (i=0; i<diff->nfailed; i++)
1024       diff_cleanupHunk(&diff->failed[i]);
1025    if (diff->failed != NULL)
1026       free(diff->failed);
1027    memset(diff, 0, sizeof(UniDiff_t));
1028 }
1029 
1030 
1031 /**
1032  * @brief Cleans up a hunk.
1033  *
1034  *    @param hunk Hunk to clean up.
1035  */
diff_cleanupHunk(UniHunk_t * hunk)1036 static void diff_cleanupHunk( UniHunk_t *hunk )
1037 {
1038    if (hunk->target.u.name != NULL)
1039       free(hunk->target.u.name);
1040 
1041    switch (hunk->type) {
1042       case HUNK_TYPE_ASSET_ADD:
1043       case HUNK_TYPE_ASSET_REMOVE:
1044       case HUNK_TYPE_ASSET_BLACKMARKET:
1045       case HUNK_TYPE_ASSET_LEGALMARKET:
1046       case HUNK_TYPE_JUMP_ADD:
1047       case HUNK_TYPE_JUMP_REMOVE:
1048       case HUNK_TYPE_TECH_ADD:
1049       case HUNK_TYPE_TECH_REMOVE:
1050       case HUNK_TYPE_ASSET_FACTION:
1051       case HUNK_TYPE_ASSET_FACTION_REMOVE:
1052       case HUNK_TYPE_FACTION_VISIBLE:
1053       case HUNK_TYPE_FACTION_INVISIBLE:
1054       case HUNK_TYPE_FACTION_ALLY:
1055       case HUNK_TYPE_FACTION_ENEMY:
1056       case HUNK_TYPE_FACTION_NEUTRAL:
1057       case HUNK_TYPE_FACTION_REALIGN:
1058          if (hunk->u.name != NULL)
1059             free(hunk->u.name);
1060          hunk->u.name = NULL;
1061          break;
1062 
1063       default:
1064          break;
1065    }
1066    memset( hunk, 0, sizeof(UniHunk_t) );
1067 }
1068 
1069 
1070 /**
1071  * @brief Saves the active diffs.
1072  *
1073  *    @param writer XML Writer to use.
1074  *    @return 0 on success.
1075  */
diff_save(xmlTextWriterPtr writer)1076 int diff_save( xmlTextWriterPtr writer )
1077 {
1078    int i;
1079    UniDiff_t *diff;
1080 
1081    xmlw_startElem(writer,"diffs");
1082    for (i=0; i<diff_nstack; i++) {
1083       diff = &diff_stack[i];
1084 
1085       xmlw_elem(writer, "diff", "%s", diff->name);
1086    }
1087    xmlw_endElem(writer); /* "diffs" */
1088 
1089    return 0;
1090 
1091 }
1092 
1093 /**
1094  * @brief Loads the diffs.
1095  *
1096  *    @param parent Parent node containing diffs.
1097  *    @return 0 on success.
1098  */
diff_load(xmlNodePtr parent)1099 int diff_load( xmlNodePtr parent )
1100 {
1101    xmlNodePtr node, cur;
1102 
1103    diff_clear();
1104 
1105    node = parent->xmlChildrenNode;
1106    do {
1107       if (xml_isNode(node,"diffs")) {
1108          cur = node->xmlChildrenNode;
1109          do {
1110             if (xml_isNode(cur,"diff"))
1111                diff_apply( xml_get(cur) );
1112          } while (xml_nextNode(cur));
1113       }
1114    } while (xml_nextNode(node));
1115 
1116    return 0;
1117 
1118 }
1119 
1120 
1121