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