Crossfire Server, Trunk  R22047
assets.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 2020 the Crossfire Development Team
5  *
6  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
7  * welcome to redistribute it under certain conditions. For details, please
8  * see COPYING and LICENSE.
9  *
10  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
11  */
12 
18 #ifndef WIN32 /* ---win32 exclude headers */
19 #include <dirent.h>
20 #include <sys/stat.h>
21 #include "autoconf.h"
22 #endif
23 
24 extern "C" {
25 #include "global.h"
26 #include "compat.h"
27 }
28 #include "assets.h"
29 #include "AssetsManager.h"
30 #include "AssetCollector.h"
31 #include "TreasureLoader.h"
32 #include "ArchetypeLoader.h"
33 #include "PngLoader.h"
34 #include "FacesetLoader.h"
35 #include "FaceLoader.h"
36 #include "WrapperLoader.h"
37 #include "MessageLoader.h"
38 #include "Faces.h"
39 #include <string.h>
40 
41 #include <vector>
42 #include <string>
43 #include <set>
44 #include <algorithm>
45 #include <map>
46 #include <memory>
47 
48 #include "AssetWriter.h"
49 #include "TreasureWriter.h"
50 #include "FaceWriter.h"
51 #include "AnimationWriter.h"
52 #include "ArchetypeWriter.h"
53 #include "MessageWriter.h"
54 #include "image.h"
55 #include "FacesetWriter.h"
56 #include "ArtifactWriter.h"
57 #include "FormulaeWriter.h"
58 
59 #include "microtar.h"
60 #include "TarLoader.h"
61 
62 static AssetsManager *manager = nullptr;
63 
67 void assets_init() {
68  manager = new AssetsManager();
69 }
70 
74 void assets_free() {
75  delete manager;
76  manager = nullptr;
77 }
78 
82 size_t nroftreasures = 0;
83 
95 static void check_treasurelist(treasure *t, const treasurelist *tl) {
96  if (t->item == NULL && t->name == NULL)
97  LOG(llevError, "Treasurelist %s has element with no name or archetype\n", tl->name);
98  if (t->chance >= 100 && t->next_yes && (t->next || t->next_no))
99  LOG(llevError, "Treasurelist %s has element that has 100%% generation, next_yes field as well as next or next_no\n", tl->name);
100  if (t->name && strcmp(t->name, "NONE"))
102  if (t->next)
103  check_treasurelist(t->next, tl);
104  if (t->next_yes)
106  if (t->next_no)
107  check_treasurelist(t->next_no, tl);
108 }
109 
114 void assets_collect(const char* datadir) {
115  LOG(llevInfo, "Starting to collect assets from %s\n", datadir);
116 
117  AssetCollector collector;
118  collector.addLoader(new TreasureLoader(manager->treasures(), manager->archetypes()));
119  auto al = new ArchetypeLoader(manager->archetypes());
122  }
123  collector.addLoader(al);
124  collector.addLoader(new PngLoader(manager->faces()));
125  collector.addLoader(new FacesetLoader());
126  collector.addLoader(new FaceLoader(manager->faces(), manager->animations()));
127  collector.addLoader(new MessageLoader(manager->messages()));
128  collector.addLoader(new WrapperLoader("/artifacts", init_artifacts));
129  collector.addLoader(new WrapperLoader("/formulae", init_formulae));
130  collector.addLoader(new WrapperLoader("/attackmess", init_attackmess));
131  for (uint8_t hook = 0; hook < settings.hooks_count; hook++) {
132  collector.addLoader(new WrapperLoader(settings.hooks_filename[hook], settings.hooks[hook]));
133  }
134  collector.addLoader(new TarLoader(&collector));
135  collector.collect(datadir);
136 
137  LOG(llevInfo, "Finished collecting assets from %s\n", datadir);
138 }
139 
143 static void check_generators(void) {
144  int abort = 0;
145 
146  manager->archetypes()->each([&abort] (const auto& arch) {
147  if (!QUERY_FLAG(&arch->clone, FLAG_GENERATOR))
148  return;
149 
150  if (!QUERY_FLAG(&arch->clone, FLAG_CONTENT_ON_GEN) && arch->clone.other_arch == NULL) {
151  LOG(llevError, "Fatal: %s is generator without content_on_gen but lacks other_arch.\n", arch->name);
152  abort = 1;
153  return;
154  }
155  if (QUERY_FLAG(&arch->clone, FLAG_CONTENT_ON_GEN) && arch->clone.inv == NULL) {
156  LOG(llevError, "Fatal: %s is generator with content_on_gen but lacks inventory.\n", arch->name);
157  abort = 1;
158  return;
159  }
160  });
161 
162  if (abort)
164 }
165 
170 void check_summoned(void) {
171  manager->archetypes()->each([] (const auto& arch) {
172  if (arch->clone.type == SPELL && arch->clone.subtype == SP_SUMMON_GOLEM && arch->clone.other_arch) {
173  if (arch->clone.other_arch->clone.move_type == 0) {
174  LOG(llevError, "Summonable archetype %s [%s] has no move_type defined!\n", arch->clone.other_arch->name, arch->clone.other_arch->clone.name);
175  fatal(SEE_LAST_ERROR);
176  }
177  }
178  });
179 }
180 
184 static void check_spells(void) {
185  int abort = 0;
186 
187  manager->archetypes()->each([&abort] (const auto& arch) {
188  if (arch->clone.type == SPELL && arch->clone.skill == NULL) {
189  LOG(llevError, "Spell archetype %s [%s] has no skill defined!\n", arch->name, arch->clone.name);
190  abort = 1;
191  }
192  });
193 
194  if (abort)
196 }
197 
203  return manager->dumpUndefined();
204 }
205 
211  check_spells();
212  check_summoned();
213  manager->treasures()->each([] (auto list) {
214  if (list->items) {
215  check_treasurelist(list->items, list);
216  }
217  });
218 }
219 
232 treasurelist *find_treasurelist(const char *name) {
233  if (!strcmp(name, "none"))
234  return NULL;
235  return manager->treasures()->get(name);
236 }
237 
239  return nroftreasures;
240 }
242  return manager->treasures()->count();
243 }
244 
246  return manager->archetypes()->count();
247 }
248 
249 archetype *get_next_archetype(archetype *current) {
250  return manager->archetypes()->next(current);
251 }
252 
253 archetype *find_archetype(const char *name) {
254  return manager->archetypes()->get(name);
255 }
256 
257 archetype *try_find_archetype(const char *name) {
258  return manager->archetypes()->find(name);
259 }
260 
262  return manager->animations()->count();
263 }
264 
265 Animations *find_animation(const char *name) {
266  return manager->animations()->get(name);
267 }
268 
269 Animations *try_find_animation(const char *name) {
270  return manager->animations()->find(name);
271 }
272 
274  manager->animations()->each(*op);
275 }
276 
277 const Face *find_face(const char *name) {
278  return manager->faces()->get(name);
279 }
280 
281 const Face *try_find_face(const char *name, const Face *error) {
282  auto found = manager->faces()->find(name);
283  if (found)
284  return found;
285  return error;
286 }
287 
288 size_t get_faces_count() {
289  return manager->faces()->count();
290 }
291 
293  manager->faces()->each(*op);
294 }
295 
297  manager->facesets()->each(*op);
298 }
299 
301  manager->archetypes()->each(*op);
302 }
303 
305  return manager;
306 }
307 
315  return manager->faces()->findById(id);
316 }
317 
319  return manager->messages()->random();
320 }
321 
327 const GeneralMessage *get_message_from_identifier(const char *identifier) {
328  return manager->messages()->find(identifier);
329 }
330 
332  return manager->faces()->checksum();
333 }
334 
336  return manager->facesets()->findById(id);
337 }
338 
339 template<class T>
340 static void do_pack(AssetWriter<T> *writer, AssetsCollection<T> *assets, StringBuffer *buf) {
341  assets->each([writer, buf] (T *asset) {
342  writer->write(asset, buf);
343  });
344  delete writer;
345 }
346 
348  ArtifactWriter writer;
350  while (list) {
351  writer.write(list, buf);
352  list = list->next;
353  }
354 }
355 
357  FormulaeWriter writer;
358  recipelist *list = get_formulalist(1);
359  while (list) {
360  writer.write(list, buf);
361  list = list->next;
362  }
363 }
364 
365 static void build_filename(const char *name, const char *prefix, char *dest, size_t max) {
366  auto dot = strrchr(name, '.');
367  if (!dot) {
368  snprintf(dest, max, "%s.%s", name, prefix);
369  return;
370  }
371  memset(dest, 0, max);
372  dot++;
373 
374  memcpy(dest, name, dot - name);
375  strncat(dest, prefix, max);
376  strncat(dest, ".", max);
377  strncat(dest, dot, max);
378  strncat(dest, ".png", max);
379 }
380 
388 static void add_to_tar(mtar_t *tar, void *data, size_t len, const char *filename) {
389  mtar_header_t h;
390  memset(&h, 0, sizeof(h));
391  strncpy(h.name, filename, sizeof(h.name));
392  h.size = len;
393  h.type = MTAR_TREG;
394  h.mode = 0664;
395  h.mtime = time(NULL);
396  /* Build raw header and write */
397  if (MTAR_ESUCCESS != mtar_write_header(tar, &h)) {
398  LOG(llevError, "Failed to write tar header for %s\n", filename);
400  }
401  if (MTAR_ESUCCESS != mtar_write_data(tar, data, len)) {
402  LOG(llevError, "Failed to write tar data for %s\n", filename);
404  }
405 }
406 
411 static void pack_images(mtar_t *tar) {
412  manager->faces()->each([&tar] (const auto face) {
413  manager->facesets()->each([&tar, &face] (const auto fs) {
414  if (!fs->prefix || fs->allocated <= face->number || !fs->faces[face->number].datalen) {
415  return;
416  }
417  char filename[500];
418  build_filename(face->name, fs->prefix, filename, sizeof(filename));
419  add_to_tar(tar, fs->faces[face->number].data, fs->faces[face->number].datalen, filename);
420  });
421  });
422 }
423 
424 void assets_pack(const char *what, const char *filename) {
425 #define MAX_PACK 100
426  char *split[MAX_PACK];
427  char *dup = strdup_local(what);
428  size_t count = split_string(dup, split, MAX_PACK, '+');
429  if (count == 0) {
430  LOG(llevError, "Invalid pack type %s\n", what);
432  }
433  bool isTar = (count > 1) || (strcmp(split[0], "images") == 0);
434  mtar_t tar;
435  if (isTar) {
436  if (MTAR_ESUCCESS != mtar_open(&tar, filename, "w")) {
437  LOG(llevError, "Failed to open tar file %s\n", filename);
439  }
440  }
441 
442  for (size_t t = 0; t < count; t++) {
443  const char *type = split[t];
444  const char *name = nullptr;
445 
447  if (strcmp(type, "treasures") == 0) {
448  do_pack(new TreasureWriter(), manager->treasures(), buf);
449  name = "crossfire.trs";
450  } else if (strcmp(type, "faces") == 0) {
451  do_pack(new FaceWriter(), manager->faces(), buf);
452  do_pack(new AnimationWriter(), manager->animations(), buf);
453  name = "crossfire.face";
454  } else if (strcmp(type, "archs") == 0) {
455  do_pack(new ArchetypeWriter(), manager->archetypes(), buf);
456  name = "crossfire.arc";
457  } else if (strcmp(type, "messages") == 0) {
458  do_pack(new MessageWriter(), manager->messages(), buf);
459  name = "messages";
460  } else if (strcmp(type, "facesets") == 0) {
461  do_pack(new FacesetWriter(), manager->facesets(), buf);
462  name = "image_info";
463  } else if (strcmp(type, "artifacts") == 0) {
464  pack_artifacts(buf);
465  name = "artifacts";
466  } else if (strcmp(type, "formulae") == 0) {
467  pack_formulae(buf);
468  name = "formulae";
469  } else if (strcmp(type, "images") == 0) {
470  pack_images(&tar);
471  stringbuffer_delete(buf);
472  continue; // Already stored in tar.
473  } else {
474  LOG(llevError, "Invalid asset type '%s'\n", type);
476  }
477 
478  size_t length = stringbuffer_length(buf);
479  char *data = stringbuffer_finish(buf);
480 
481  if (isTar) {
482  add_to_tar(&tar, data, length, name);
483  } else {
484  FILE *out = fopen(filename, "w+");
485  if (!out) {
486  LOG(llevError, "Failed to open file '%s'\n", filename);
488  }
489  if (fwrite(static_cast<void*>(data), 1, length, out) != length) {
490  LOG(llevError, "Failed to write all data!\n", filename);
491  fclose(out);
493  }
494  fclose(out);
495  }
496  free(data);
497  }
498 
499  if (isTar) {
500  if (MTAR_ESUCCESS != mtar_finalize(&tar)) {
501  LOG(llevError, "Failed to finalize tar file %s\n", filename);
503  }
504  if (MTAR_ESUCCESS != mtar_close(&tar)) {
505  LOG(llevError, "Failed to closed tar file %s\n", filename);
507  }
508  }
509  free(dup);
510 }
511 
513  manager->archetypes()->each([] (archetype *arch) {
514  object *op = &arch->clone;
515  if (op->speed < 0) {
516  op->speed_left = op->speed_left - RANDOM() % 100 / 100.0;
517  op->speed = -op->speed; // Make this always positive
518  }
519  });
520 }
521 
544 long recipe_find_ingredient_cost(const char *name) {
545  long mult;
546  const char *cp;
547  char part1[100];
548  char part2[100];
549 
550  /* same as atoi(), but skip number */
551  mult = 0;
552  while (isdigit(*name)) {
553  mult = 10*mult+(*name-'0');
554  name++;
555  }
556  if (mult > 0)
557  name++;
558  else
559  mult = 1;
560  /* first, try to match the name of an archetype */
561 
562  long value = 0;
563  bool found = false;
564  manager->archetypes()->each([&value, &found, &name, &part1] (const archetype *at) {
565  if (found) {
566  return;
567  }
568 
569  if (at->clone.title != NULL) {
570  /* inefficient, but who cares? */
571  snprintf(part1, sizeof(part1), "%s %s", at->clone.name, at->clone.title);
572  if (!strcasecmp(part1, name)) {
573  value = at->clone.value;
574  found = true;
575  return;
576  }
577  }
578  if (!strcasecmp(at->clone.name, name)) {
579  value = at->clone.value;
580  found = true;
581  return;
582  }
583  });
584  if (found) {
585  return mult * value;
586  }
587 
588  /* second, try to match an artifact ("arch of something") */
589  cp = strstr(name, " of ");
590  if (cp != NULL) {
591  safe_strncpy(part1, name, sizeof(part1));
592  part1[cp-name] = '\0';
593  safe_strncpy(part2, cp + 4, sizeof(part2));
594  manager->archetypes()->each([&value, &found, &part1, &part2] (const archetype *at) {
595  if (found) {
596  return;
597  }
598 
599  if (!strcasecmp(at->clone.name, part1) && at->clone.title == NULL) {
600  /* find the first artifact derived from that archetype (same type) */
601  for (auto al = first_artifactlist; al != NULL; al = al->next) {
602  if (al->type == at->clone.type) {
603  for (auto art = al->items; art != NULL; art = art->next) {
604  if (!strcasecmp(art->item->name, part2)) {
605  value = at->clone.value * art->item->value;
606  found = true;
607  return;
608  }
609  }
610  }
611  }
612  }
613  });
614  }
615  if (found) {
616  return mult * value;
617  }
618 
619  /* third, try to match a body part ("arch's something") */
620  cp = strstr(name, "'s ");
621  if (cp != NULL) {
622  safe_strncpy(part1, name, sizeof(part1));
623  part1[cp-name] = '\0';
624  safe_strncpy(part2, cp + 3, sizeof(part2));
625  /* examine all archetypes matching the first part of the name */
626  manager->archetypes()->each([&value, &found, &part1, &part2] (const archetype *at) {
627  if (found) {
628  return;
629  }
630  if (!strcasecmp(at->clone.name, part1) && at->clone.title == NULL) {
631  if (at->clone.randomitems != NULL) {
632  auto at2 = find_treasure_by_name(at->clone.randomitems->items, part2, 0);
633  if (at2 != NULL) {
634  value = at2->clone.value * isqrt(at->clone.level * 2);
635  found = true;
636  return;
637  }
638  }
639  }
640  });
641  }
642  if (found) {
643  return mult * value;
644  }
645 
646  /* failed to find any matching items -- formula should be checked */
647  LOG(llevError, "Couldn't find cost for ingredient %s\n", name);
648  return -1;
649 }
T * next(T *current)
void init_formulae(BufferReader *reader, const char *filename)
Definition: recipe.c:143
static void build_filename(const char *name, const char *prefix, char *dest, size_t max)
Definition: assets.cpp:365
long recipe_find_ingredient_cost(const char *name)
Definition: assets.cpp:544
static void check_spells(void)
Definition: assets.cpp:184
Treasures * treasures()
Definition: AssetsManager.h:53
static void check_treasurelist(treasure *t, const treasurelist *tl)
Definition: assets.cpp:95
void(* face_op)(const Face *)
Definition: assets.h:24
const char * hooks_filename[20]
Definition: global.h:333
void init_attackmess(BufferReader *reader, const char *filename)
Definition: init.c:476
T * get(const Key &name)
unsigned char uint8_t
Definition: win32.h:161
StringBuffer * buf
Definition: readable.c:1591
virtual void write(const recipelist *list, StringBuffer *buf)
#define strdup_local
Definition: compat.h:25
void(* faceset_op)(const face_sets *)
Definition: assets.h:26
struct recipeliststruct * next
Definition: recipe.h:41
AllAnimations * animations()
Definition: AssetsManager.h:48
archetype * find_archetype(const char *name)
Definition: assets.cpp:253
void fatal(enum fatal_error err)
Definition: utils.c:597
uint8_t hooks_count
Definition: global.h:332
void init_artifacts(BufferReader *reader, const char *filename)
Definition: artifact.c:543
void archetypes_for_each(arch_op op)
Definition: assets.cpp:300
treasurelist * find_treasurelist(const char *name)
Definition: assets.cpp:232
Definition: face.h:14
StringBuffer * stringbuffer_new(void)
Definition: stringbuffer.c:57
int get_bitmap_checksum()
Definition: assets.cpp:331
unsigned mode
Definition: microtar.h:44
int checksum() const
Definition: Faces.h:28
int mtar_write_data(mtar_t *tar, const void *data, unsigned size)
Definition: microtar.c:357
const Face * findById(uint16_t id)
Definition: Faces.cpp:52
void assets_init()
Definition: assets.cpp:67
face_sets * find_faceset(int id)
Definition: assets.cpp:335
Animations * find_animation(const char *name)
Definition: assets.cpp:265
static AssetsManager * manager
Definition: assets.cpp:62
Facesets * facesets()
Definition: AssetsManager.h:64
static void pack_formulae(StringBuffer *buf)
Definition: assets.cpp:356
#define safe_strncpy
Definition: compat.h:23
struct archt * item
Definition: treasure.h:64
archetype * try_find_archetype(const char *name)
Definition: assets.cpp:257
#define MAX_PACK
void collect(const std::string &directory)
AssetsManager * getManager()
Definition: assets.cpp:304
size_t assets_number_of_animations()
Definition: assets.cpp:261
const Face * try_find_face(const char *name, const Face *error)
Definition: assets.cpp:281
static void pack_images(mtar_t *tar)
Definition: assets.cpp:411
void * archetypes_tracker
Definition: global.h:336
int mtar_close(mtar_t *tar)
Definition: microtar.c:211
int mtar_finalize(mtar_t *tar)
Definition: microtar.c:373
struct artifactliststruct * next
Definition: artifact.h:29
GeneralMessage * random()
Definition: Messages.cpp:50
Faces * faces()
Definition: AssetsManager.h:38
size_t assets_number_of_treasurelists()
Definition: assets.cpp:241
void(* anim_op)(const Animations *)
Definition: assets.h:25
static void add_to_tar(mtar_t *tar, void *data, size_t len, const char *filename)
Definition: assets.cpp:388
face_sets * findById(int id)
Definition: Facesets.cpp:19
#define snprintf
Definition: win32.h:46
collectorHook hooks[20]
Definition: global.h:334
int strcasecmp(const char *s1, const char *s2)
size_t assets_number_of_archetypes()
Definition: assets.cpp:245
void(* arch_op)(archetype *)
Definition: assets.h:23
Archetypes * archetypes()
Definition: AssetsManager.h:43
size_t stringbuffer_length(StringBuffer *sb)
Definition: stringbuffer.c:162
int mtar_write_header(mtar_t *tar, const mtar_header_t *h)
Definition: microtar.c:323
size_t count() const
static void pack_artifacts(StringBuffer *buf)
Definition: assets.cpp:347
GeneralMessage * get_random_message()
Definition: assets.cpp:318
size_t get_faces_count()
Definition: assets.cpp:288
size_t nroftreasures
Definition: assets.cpp:82
archetype * get_next_archetype(archetype *current)
Definition: assets.cpp:249
struct treasurestruct * next_no
Definition: treasure.h:68
void setTracker(AssetsTracker< archetype > *tracker)
Messages * messages()
Definition: AssetsManager.h:58
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
EXTERN artifactlist * first_artifactlist
Definition: global.h:118
static void check_generators(void)
Definition: assets.cpp:143
void assets_pack(const char *what, const char *filename)
Definition: assets.cpp:424
unsigned size
Definition: microtar.h:46
void assets_finish_archetypes_for_play()
Definition: assets.cpp:512
unsigned short uint16_t
Definition: win32.h:163
unsigned type
Definition: microtar.h:48
void assets_end_load()
Definition: assets.cpp:209
void facesets_for_each(faceset_op op)
Definition: assets.cpp:296
void addLoader(AssetLoader *loader)
virtual void write(const artifactlist *list, StringBuffer *buf)
uint8_t chance
Definition: treasure.h:70
const char * name
Definition: treasure.h:83
#define FLAG_GENERATOR
Definition: define.h:248
struct treasurestruct * next_yes
Definition: treasure.h:67
virtual void write(const T *asset, StringBuffer *buf)=0
Animations * try_find_animation(const char *name)
Definition: assets.cpp:269
void faces_for_each(face_op op)
Definition: assets.cpp:292
void stringbuffer_delete(StringBuffer *sb)
Definition: stringbuffer.c:71
void animations_for_each(anim_op op)
Definition: assets.cpp:273
struct Settings settings
Definition: init.c:39
size_t dumpUndefined()
const Face * find_face(const char *name)
Definition: assets.cpp:277
unsigned mtime
Definition: microtar.h:47
char name[100]
Definition: microtar.h:49
const GeneralMessage * get_message_from_identifier(const char *identifier)
Definition: assets.cpp:327
T * find(const Key &name)
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Definition: utils.c:500
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
struct treasurestruct * next
Definition: treasure.h:66
void check_summoned(void)
Definition: assets.cpp:170
void assets_collect(const char *datadir)
Definition: assets.cpp:114
const char * name
Definition: treasure.h:65
#define SP_SUMMON_GOLEM
Definition: spells.h:86
#define FLAG_CONTENT_ON_GEN
Definition: define.h:374
int mtar_open(mtar_t *tar, const char *filename, const char *mode)
Definition: microtar.c:177
char * stringbuffer_finish(StringBuffer *sb)
Definition: stringbuffer.c:76
static void do_pack(AssetWriter< T > *writer, AssetsCollection< T > *assets, StringBuffer *buf)
Definition: assets.cpp:340
size_t assets_number_of_treasures()
Definition: assets.cpp:238
void assets_free()
Definition: assets.cpp:74
recipelist * get_formulalist(int i)
Definition: recipe.c:96
size_t assets_dump_undefined()
Definition: assets.cpp:202
const Face * get_face_by_id(uint16_t id)
Definition: assets.cpp:314
void each(std::function< void(T *)> op)