Crossfire Server, Trunk  R20513
image.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
19 #include "global.h"
20 
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "image.h"
28 
34 
40 
41 
45 unsigned int nrofpixmaps = 0;
46 
56 static const char *const colorname[] = {
57  "black", /* 0 */
58  "white", /* 1 */
59  "blue", /* 2 */
60  "red", /* 3 */
61  "orange", /* 4 */
62  "light_blue", /* 5 */
63  "dark_orange", /* 6 */
64  "green", /* 7 */
65  "light_green", /* 8 */
66  "grey", /* 9 */
67  "brown", /* 10 */
68  "yellow", /* 11 */
69  "khaki" /* 12 */
70 };
71 
83 static int compare_face(const New_Face *a, const New_Face *b) {
84  if (strcmp(a->name, "bug.111") == 0) {
85  if (strcmp(b->name, "bug.111") == 0)
86  return 0;
87  return -1;
88  } else if (strcmp(b->name, "bug.111") == 0)
89  return 1;
90  return strcmp(a->name, b->name);
91 }
92 
106 static uint8_t find_color(const char *name) {
107  uint8_t i;
108 
109  for (i = 0; i < sizeof(colorname)/sizeof(*colorname); i++)
110  if (!strcmp(name, colorname[i]))
111  return i;
112 
113  LOG(llevError, "Unknown color: %s\n", name);
114  return 0;
115 }
116 
124 static void read_face_data(void) {
125  char buf[MAX_BUF], *cp;
126  New_Face *on_face = NULL;
127  FILE *fp;
128 
129  snprintf(buf, sizeof(buf), "%s/faces", settings.datadir);
130  if ((fp = fopen(buf, "r")) == NULL) {
131  LOG(llevError, "faces: couldn't open file: %s\n", strerror(errno));
132  exit(-1);
133  }
134 
135  while (fgets(buf, MAX_BUF, fp) != NULL) {
136  if (*buf == '#')
137  continue;
138  if (!strncmp(buf, "end", 3)) {
139  on_face = NULL;
140  } else if (!strncmp(buf, "face", 4)) {
141  unsigned tmp;
142 
143  cp = buf+5;
144  cp[strlen(cp)-1] = '\0'; /* remove newline */
145 
146  if ((tmp = find_face(cp, (unsigned)-1)) == (unsigned)-1) {
147  LOG(llevError, "faces: couldn't find '%s'\n", cp);
148  on_face = NULL;
149  continue;
150  }
151  on_face = &new_faces[tmp];
152  on_face->visibility = 0;
153  } else if (on_face == NULL) {
154  LOG(llevError, "faces: got line with no face set: %s\n", buf);
155  } else if (!strncmp(buf, "visibility", 10)) {
156  on_face->visibility = atoi(buf+11);
157  } else if (!strncmp(buf, "magicmap", 8)) {
158  cp = buf+9;
159  cp[strlen(cp)-1] = '\0';
160  on_face->magicmap = find_color(cp);
161  } else if (!strncmp(buf, "is_floor", 8)) {
162  int value = atoi(buf+9);
163  if (value)
164  on_face->magicmap |= FACE_FLOOR;
165  } else
166  LOG(llevDebug, "faces: unknown line in %s\n", buf);
167  }
168  fclose(fp);
169 }
170 
180 void read_bmap_names(void) {
181  char buf[MAX_BUF], *p;
182  FILE *fp;
183  unsigned int i;
184  size_t l;
185 
186  bmaps_checksum = 0;
187  snprintf(buf, sizeof(buf), "%s/bmaps.paths", settings.datadir);
188  if ((fp = fopen(buf, "r")) == NULL) {
189  LOG(llevError, "bmaps: couldn't open: %s\n", strerror(errno));
190  exit(-1);
191  }
192 
193  nrofpixmaps = 0;
194 
195  /* First count how many bitmaps we have, so we can allocate correctly */
196  while (fgets(buf, MAX_BUF, fp) != NULL) {
197  if (buf[0] != '#' && buf[0] != '\n') {
198  nrofpixmaps++;
199  }
200  }
201 
202  rewind(fp);
203  assert(nrofpixmaps > 0);
204  new_faces = (New_Face *)malloc(sizeof(New_Face)*nrofpixmaps);
205  if (new_faces == NULL) {
207  }
208 
209  for (i = 0; i < nrofpixmaps; i++) {
210  new_faces[i].name = NULL;
211  new_faces[i].visibility = 0;
212  new_faces[i].magicmap = 255;
213  new_faces[i].smoothface = (uint16_t)-1;
214  }
215 
216  i = 0;
217  while (i < nrofpixmaps && fgets(buf, MAX_BUF, fp) != NULL) {
218  if (*buf == '#')
219  continue;
220 
221  p = strrchr(buf, '/');
222  if ((p == NULL) || (strtok(p, " \t\n") == NULL)) {
223  LOG(llevError, "bmaps: syntax error in %s\n", buf);
225  }
226  /* strtok converted the final newline or tab to NULL so all is ok */
227  new_faces[i].name = strdup_local(p + 1);
228 
229  /* We need to calculate the checksum of the bmaps file
230  * name->number mapping to send to the client. This does not
231  * need to match what sum or other utility may come up with -
232  * as long as we get the same results on the same real file
233  * data, it does the job as it lets the client know if
234  * the file has the same data or not.
235  */
237  bmaps_checksum += i&0xff;
238  bmaps_checksum &= 0xffffffff;
239 
241  bmaps_checksum += (i>>8)&0xff;
242  bmaps_checksum &= 0xffffffff;
243  for (l = 0; l < strlen(p); l++) {
245  bmaps_checksum += p[l];
246  bmaps_checksum &= 0xffffffff;
247  }
248 
249  i++;
250  }
251  fclose(fp);
252 
253  if (i != nrofpixmaps) {
254  LOG(llevError, "read_bmap_names: first read gave %d faces but only loaded %d??\n", nrofpixmaps, i);
256  }
257 
258  LOG(llevDebug, "bmaps: loaded %d faces\n", nrofpixmaps);
259 
260  qsort(new_faces, nrofpixmaps, sizeof(New_Face), (int (*)(const void *, const void *))compare_face);
261 
262  for (i = 0; i < nrofpixmaps; i++) {
263  new_faces[i].number = i;
264  }
265 
266  read_face_data();
267 
268  for (i = 0; i < nrofpixmaps; i++) {
269  if (new_faces[i].magicmap == 255) {
270  new_faces[i].magicmap = 0;
271  }
272  }
273  /* Actually forcefully setting the colors here probably should not
274  * be done - it could easily create confusion.
275  */
276  blank_face = &new_faces[find_face(BLANK_FACE_NAME, 0)];
277  blank_face->magicmap = find_color("khaki")|FACE_FLOOR;
278 
279  empty_face = &new_faces[find_face(EMPTY_FACE_NAME, 0)];
280 
281  smooth_face = &new_faces[find_face(SMOOTH_FACE_NAME, 0)];
282 }
283 
303 unsigned find_face(const char *name, unsigned error) {
304  New_Face *bp, tmp;
305 
306  tmp.name = name;
307  bp = (New_Face *)bsearch(&tmp, new_faces, nrofpixmaps, sizeof(New_Face), (int (*)(const void *, const void *))compare_face);
308 
309  return bp ? bp->number : error;
310 }
311 
323 int read_smooth(void) {
324  char buf[MAX_BUF], *p, *q;
325  FILE *fp;
326  unsigned regular, smoothed;
327  int nrofsmooth = 0;
328 
329  snprintf(buf, sizeof(buf), "%s/smooth", settings.datadir);
330  if ((fp = fopen(buf, "r")) == NULL) {
331  LOG(llevError, "Cannot open smooth file: %s\n", strerror(errno));
332  exit(-1);
333  }
334 
335  while (fgets(buf, MAX_BUF, fp) != NULL) {
336  if (*buf == '#')
337  continue;
338 
339  if ((p = strchr(buf, '\n')))
340  *p = '\0';
341 
342  p = strchr(buf, ' ');
343  if (!p)
344  continue;
345 
346  *p = '\0';
347  q = buf;
348  regular = find_face(q, (unsigned)-1);
349  if (regular == (unsigned)-1) {
350  LOG(llevError, "invalid regular face: %s\n", q);
351  continue;
352  }
353  q = p+1;
354  smoothed = find_face(q, (unsigned)-1);
355  if (smoothed == (unsigned)-1) {
356  LOG(llevError, "invalid smoothed face: %s\n", q);
357  continue;
358  }
359 
360  new_faces[regular].smoothface = smoothed;
361 
362  nrofsmooth++;
363  }
364  fclose(fp);
365 
366  LOG(llevDebug, "smooth: loaded %d entries\n", nrofsmooth);
367  return nrofsmooth;
368 }
369 
379 int find_smooth(uint16_t face, uint16_t *smoothed) {
380  (*smoothed) = 0;
381 
382  if (face < nrofpixmaps) {
383  if (new_faces[face].smoothface == ((uint16_t)-1))
384  return 0;
385 
386  (*smoothed) = new_faces[face].smoothface;
387  return 1;
388  }
389 
390  return 0;
391 }
392 
396 void free_all_images(void) {
397  unsigned int i;
398 
399  for (i = 0; i < nrofpixmaps; i++)
400  free((char*)(new_faces[i].name));
401  free(new_faces);
402 }
403 
412 static void check_faceset_fallback(int faceset, int togo) {
413  int fallback = facesets[faceset].fallback;
414 
415  /* proper case - falls back to base set */
416  if (fallback == 0)
417  return;
418 
419  if (!facesets[fallback].prefix) {
420  LOG(llevError, "Face set %d falls to non set faceset %d\n", faceset, fallback);
421  abort();
422  }
423  togo--;
424  if (togo == 0) {
425  LOG(llevError, "Infinite loop found in facesets. aborting.\n");
426  abort();
427  }
428  check_faceset_fallback(fallback, togo);
429 }
430 
448 void read_client_images(void) {
449  char filename[400];
450  char buf[HUGE_BUF];
451  char *cp, *cps[7+1], *slash;
452  FILE *infile;
453  int len, fileno, i;
454  unsigned int num;
455 
456  memset(facesets, 0, sizeof(facesets));
457  snprintf(filename, sizeof(filename), "%s/image_info", settings.datadir);
458  if ((infile = fopen(filename, "r")) == NULL) {
459  LOG(llevError, "Unable to open %s\n", filename);
460  abort();
461  }
462  while (fgets(buf, HUGE_BUF-1, infile) != NULL) {
463  if (buf[0] == '#')
464  continue;
465  if (split_string(buf, cps, sizeof(cps)/sizeof(*cps), ':') != 7)
466  LOG(llevError, "Bad line in image_info file, ignoring line:\n %s", buf);
467  else {
468  len = atoi(cps[0]);
469  if (len >= MAX_FACE_SETS) {
470  LOG(llevError, "To high a setnum in image_info file: %d > %d\n", len, MAX_FACE_SETS);
471  abort();
472  }
473  facesets[len].prefix = strdup_local(cps[1]);
474  facesets[len].fullname = strdup_local(cps[2]);
475  facesets[len].fallback = atoi(cps[3]);
476  facesets[len].size = strdup_local(cps[4]);
477  facesets[len].extension = strdup_local(cps[5]);
478  facesets[len].comment = strdup_local(cps[6]);
479  }
480  }
481  fclose(infile);
482  for (i = 0; i < MAX_FACE_SETS; i++) {
483  if (facesets[i].prefix)
484  check_faceset_fallback(i, MAX_FACE_SETS);
485  }
486  /* Loaded the faceset information - now need to load up the
487  * actual faces.
488  */
489 
490  for (fileno = 0; fileno < MAX_FACE_SETS; fileno++) {
491  /* if prefix is not set, this is not used */
492  if (!facesets[fileno].prefix)
493  continue;
494  facesets[fileno].faces = calloc(nrofpixmaps, sizeof(face_info));
495 
496  snprintf(filename, sizeof(filename), "%s/crossfire.%d", settings.datadir, fileno);
497  LOG(llevDebug, "images: loading from %s\n", filename);
498 
499  if ((infile = fopen(filename, "rb")) == NULL) {
500  LOG(llevError, "Unable to open %s\n", filename);
501  abort();
502  }
503  while (fgets(buf, HUGE_BUF-1, infile) != NULL) {
504  if (strncmp(buf, "IMAGE ", 6) != 0) {
505  LOG(llevError, "read_client_images:Bad image line - not IMAGE, instead\n%s", buf);
506  abort();
507  }
508  cp = buf + 6;
509  len = atoi(cp);
510  if (len == 0 || len > MAX_IMAGE_SIZE) {
511  LOG(llevError, "read_client_images: length not valid: %d > %d \n%s", len, MAX_IMAGE_SIZE, buf);
512  abort();
513  }
514 
515  for ( ; *cp != ' ' && *cp != '\n' && *cp != '\0'; cp++) {
516  /* Increment pointer until next token. */
517  }
518 
519  if (*cp != ' ') {
520  LOG(llevError, "read_client_images: couldn't read name\n");
521  abort();
522  }
523  cp++;
524  /* cp points to the start of the full name */
525  slash = strrchr(cp, '/');
526  if (slash != NULL)
527  cp = slash + 1;
528  if (cp[strlen(cp) - 1] == '\n')
529  cp[strlen(cp) - 1] = '\0';
530 
531  /* cp points to the start of the picture name itself */
532  num = find_face(cp, (unsigned)-1);
533  if (num == (unsigned)-1) {
534  LOG(llevError, "read_client_images: couldn't find picture %s\n", cp);
535  abort();
536  }
537  if (num >= nrofpixmaps) {
538  LOG(llevError, "read_client_images: invalid picture number %d for %s\n", num, cp);
539  abort();
540  }
541 
542  facesets[fileno].faces[num].datalen = len;
543  facesets[fileno].faces[num].data = malloc(len);
544  if ((i = fread(facesets[fileno].faces[num].data, len, 1, infile)) != 1) {
545  LOG(llevError, "read_client_images: Did not read desired amount of data, wanted %d, got %d\n%s", len, i, buf);
546  abort();
547  }
548  facesets[fileno].faces[num].checksum = 0;
549  for (i = 0; i < len; i++) {
550  ROTATE_RIGHT(facesets[fileno].faces[num].checksum);
551  facesets[fileno].faces[num].checksum += facesets[fileno].faces[num].data[i];
552  facesets[fileno].faces[num].checksum &= 0xffffffff;
553  }
554  }
555  fclose(infile);
556  } /* For fileno < MAX_FACE_SETS */
557 }
558 
563 int is_valid_faceset(int fsn) {
564  if (fsn >= 0 && fsn < MAX_FACE_SETS && facesets[fsn].prefix)
565  return TRUE;
566  return FALSE;
567 }
568 
572 void free_socket_images(void) {
573  int num;
574  unsigned int q;
575 
576  for (num = 0; num < MAX_FACE_SETS; num++) {
577  if (facesets[num].prefix) {
578  for (q = 0; q < nrofpixmaps; q++)
579  free(facesets[num].faces[q].data);
580  free(facesets[num].prefix);
581  free(facesets[num].fullname);
582  free(facesets[num].size);
583  free(facesets[num].extension);
584  free(facesets[num].comment);
585  free(facesets[num].faces);
586  }
587  }
588 }
589 
602 int get_face_fallback(int faceset, int imageno) {
603  /* faceset 0 is supposed to have every image, so just return. Doing
604  * so also prevents infinite loops in the case if it not having
605  * the face, but in that case, we are likely to crash when we try
606  * to access the data, but that is probably preferable to an infinite
607  * loop.
608  */
609  if (faceset == 0)
610  return 0;
611 
612  if (!facesets[faceset].prefix) {
613  LOG(llevError, "get_face_fallback called with unused set (%d)?\n", faceset);
614  return 0; /* use default set */
615  }
616  if (facesets[faceset].faces[imageno].data)
617  return faceset;
618  return get_face_fallback(facesets[faceset].fallback, imageno);
619 }
Error, serious thing.
Definition: logger.h:11
Actual image data the client will display.
Definition: image.h:10
face_info * faces
images in this faceset
Definition: image.h:24
#define ROTATE_RIGHT(c)
Definition: global.h:169
unsigned char uint8_t
Definition: win32.h:161
New_Face * new_faces
Contains face information, with names, numbers, magicmap color and such.
Definition: image.c:33
#define strdup_local
Definition: compat.h:25
void fatal(enum fatal_error err)
fatal() is meant to be called whenever a fatal signal is intercepted.
Definition: utils.c:596
#define HUGE_BUF
Used for messages - some can be quite long.
Definition: define.h:37
int is_valid_faceset(int fsn)
Checks specified faceset is valid.
Definition: image.c:563
#define TRUE
Definition: compat.h:10
#define FALSE
Definition: compat.h:11
#define FACE_FLOOR
Definition: newclient.h:277
Global type definitions and header inclusions.
unsigned int nrofpixmaps
Number of bitmaps loaded from the "bmaps" file.
Definition: image.c:45
uint16_t number
This is the image id.
Definition: face.h:15
static void read_face_data(void)
This reads the lib/faces file, getting color and visibility information.
Definition: image.c:124
const char * name
Face name, as used by archetypes and such.
Definition: face.h:20
int read_smooth(void)
Reads the smooth file to know how to smooth datas.
Definition: image.c:323
Image-related structures.
uint8_t * data
Image data.
Definition: image.h:11
#define snprintf
Definition: win32.h:46
char * prefix
Faceset short name, used in pictures names (base, clsc).
Definition: image.h:18
#define BLANK_FACE_NAME
Definition: define.h:591
char * extension
Supplementary description.
Definition: image.h:22
static uint8_t find_color(const char *name)
Finds a color by name.
Definition: image.c:106
uint8_t visibility
Definition: face.h:17
uint16_t smoothface
Smoothed face for this, (uint16_t)-1 for none.
Definition: face.h:19
New_Face * smooth_face
Definition: image.c:39
char * size
Human-readable set size.
Definition: image.h:21
uint8_t magicmap
Color to show this in magic map.
Definition: face.h:18
int find_smooth(uint16_t face, uint16_t *smoothed)
Find the smooth face for a given face.
Definition: image.c:379
void free_socket_images(void)
Frees all faceset information.
Definition: image.c:572
void read_client_images(void)
Loads all the image types into memory.
Definition: image.c:448
uint32_t checksum
Checksum of face data.
Definition: image.h:13
char * comment
Human-readable comment for this set.
Definition: image.h:23
New_Face * empty_face
Definition: image.c:39
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
void free_all_images(void)
Deallocates memory allocated by read_bmap_names() and read_smooth().
Definition: image.c:396
unsigned short uint16_t
Definition: win32.h:163
void read_bmap_names(void)
This reads the bmaps file to get all the bitmap names and stuff.
Definition: image.c:180
#define MAX_IMAGE_SIZE
Definition: image.h:32
const char * datadir
Read only data files.
Definition: global.h:244
#define MAX_FACE_SETS
Maximum number of image sets the program will handle.
Definition: image.h:26
face_sets facesets[MAX_FACE_SETS]
All facesets.
Definition: image.c:47
New_Face * blank_face
Following can just as easily be pointers, but it is easier to keep them like this.
Definition: image.c:39
uint16_t datalen
Length of data.
Definition: image.h:12
Only for debugging purposes.
Definition: logger.h:13
static const char *const colorname[]
The only thing this table is used for now is to translate the colorname in the magicmap field of the ...
Definition: image.c:56
EXTERN int bmaps_checksum
Definition: global.h:166
struct Settings settings
Server settings.
Definition: init.c:40
#define SMOOTH_FACE_NAME
Definition: define.h:593
char * fullname
Full faceset name.
Definition: image.h:19
New face structure - this enforces the notion that data is face by face only - you can not change the...
Definition: face.h:14
size_t split_string(char *str, char *array[], size_t array_size, char sep)
Splits a string delimited by passed in sep value into characters into an array of strings...
Definition: utils.c:499
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.c:51
int get_face_fallback(int faceset, int imageno)
This returns the set we will actually use when sending a face.
Definition: image.c:602
#define EMPTY_FACE_NAME
Definition: define.h:592
uint8_t fallback
Faceset to use when an image is not found in this faceset, index in facesets.
Definition: image.h:20
Information about one face set.
Definition: image.h:17
unsigned find_face(const char *name, unsigned error)
This returns an the face number of face &#39;name&#39;.
Definition: image.c:303
static int compare_face(const New_Face *a, const New_Face *b)
Used for bsearch searching for faces by name.
Definition: image.c:83
static void check_faceset_fallback(int faceset, int togo)
Checks fallback are correctly defined.
Definition: image.c:412