Crossfire Client, Branches  R11627
image.c
Go to the documentation of this file.
1 const char * const rcsid_common_image_c =
2  "$Id: image.c 9201 2008-06-01 17:32:45Z anmaster $";
3 /*
4  Crossfire client, a client program for the crossfire program.
5 
6  Copyright (C) 2001 Mark Wedel & Crossfire Development Team
7 
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22  The author can be reached via e-mail to crossfire-devel@real-time.com
23 */
24 
25 /*
26  * This file contains image related functions - this is a higher level up -
27  * it mostly deals with the caching of the images, processing the image commands
28  * from the server, etc.
29  */
30 
31 #include <config.h>
32 #include <stdlib.h>
33 #include <sys/stat.h>
34 #ifndef WIN32
35 #include <unistd.h>
36 #else
37 #include <io.h>
38 #include <direct.h>
39 #endif
40 #include <ctype.h>
41 
42 #include "client.h"
43 #include "external.h"
44 
45 /* Rotate right from bsd sum. */
46 #define ROTATE_RIGHT(c) if ((c) & 01) (c) = ((c) >>1) + 0x80000000; else (c) >>= 1;
47 
48 /*#define CHECKSUM_DEBUG*/
49 
50 
51 struct FD_Cache {
52  char name[MAX_BUF];
53  int fd;
55 
56 
57 /* given the filename, this tries to load the data. It returns 0 success,
58  * -1 on failure. It returns the data and len, the passed
59  * options. csum is set to zero or unset - changes have made it such
60  * that the caller knows whether or not the checksum matches, so there
61  * is little point to re-do it.
62  * data should be a buffer already allocated.
63  */
64 
65 static int load_image(char *filename, uint8 *data, int *len, uint32 *csum)
66 {
67  int fd, i;
68  char *cp;
69 
70  /* If the name includes an @, then that is a combined image file,
71  * so we need to load the image a bit specially. By using these
72  * combined image files, it reduces number of opens needed.
73  * In fact, we keep track of which ones we have opened to improve
74  * performance. Note that while not currently done, this combined
75  * image scheme could be done when storing images in the players
76  * ~/.crossfire/image-cache directory.
77  */
78  if ((cp=strchr(filename,'@'))!=NULL) {
79  char *lp;
80  int offset, length, last=-1;
81 
82  offset = atoi(cp + 1);
83  lp = strchr(cp,':');
84  if (!lp) {
85  LOG(LOG_ERROR,"common::load_image","Corrupt filename - has '@' but no ':' ?(%s)", filename);
86  return -1;
87  }
88  length = atoi(lp + 1);
89  *cp = 0;
90  for (i=0; i<MAX_FACE_SETS; i++) {
91  if (!strcmp(fd_cache[i].name, filename)) break;
92  if (last==-1 && fd_cache[i].fd == -1) last = i;
93  }
94  /* Didn't find a matching entry yet, so make one */
95  if (i == MAX_FACE_SETS) {
96  if (last == -1) {
97  LOG(LOG_WARNING,"common::load_image","fd_cache filled up? unable to load matching cache entry");
98  *cp = '@'; /* put @ back in string */
99  return -1;
100  }
101 #ifdef WIN32
102  if ((fd_cache[last].fd = open(filename, O_RDONLY | O_BINARY))==-1) {
103 #else
104  if ((fd_cache[last].fd = open(filename, O_RDONLY))==-1) {
105 #endif
106  LOG(LOG_WARNING,"common::load_image","unable to load listed cache file %s",filename);
107  *cp = '@'; /* put @ back in string */
108  return -1;
109  }
110  strcpy(fd_cache[last].name, filename);
111  i=last;
112  }
113  lseek(fd_cache[i].fd, offset, SEEK_SET);
114 #ifdef WIN32
115  *len = read(fd_cache[i].fd, data, length);
116 #else
117  *len = read(fd_cache[i].fd, data, 65535);
118 #endif
119  *cp = '@';
120  }
121  else {
122 #ifdef WIN32
123  int length = 0;
124  if ((fd=open(filename, O_RDONLY | O_BINARY))==-1) return -1;
125  length = lseek(fd, 0, SEEK_END);
126  lseek(fd, 0, SEEK_SET);
127  *len=read(fd, data, length);
128 #else
129  if ((fd=open(filename, O_RDONLY))==-1) return -1;
130  *len=read(fd, data, 65535);
131 #endif
132  close(fd);
133  }
134 
136  *csum = 0;
137  return 0;
138 
139 #if 0
140  /* Shouldn't be needed anymore */
141  *csum=0;
142  for (i=0; i<*len; i++) {
143  ROTATE_RIGHT(*csum);
144  *csum += data[i];
145  *csum &= 0xffffffff;
146  }
147 #endif
148 
149 }
150 /*******************************************************************************
151  *
152  * This is our image caching logic. We use a hash to make the name lookups
153  * happen quickly - this is done for speed, but also because we don't really
154  * have a good idea on how many images may used. It also means that as the
155  * cache gets filled up with images in a random order, the lookup is still
156  * pretty quick.
157  *
158  * If a bucket is filled with an entry that is not of the right name,
159  * we store/look for the correct one in the next bucket.
160  *
161  *******************************************************************************
162  */
163 
164 /* This should be a power of 2 */
165 #define IMAGE_HASH 8192
166 
168 
169 /* This holds the name we recieve with the 'face' command so we know what
170  * to save it as when we actually get the face.
171  */
172 static char *facetoname[MAXPIXMAPNUM];
173 
174 
175 struct Image_Cache {
176  char *image_name;
179 
180 
181 /* This function is basically hasharch from the server, common/arch.c
182  * a few changes - first, we stop processing when we reach the first
183  * . - this is because I'm not sure if hashing .111 at the end of
184  * all the image names will be very useful.
185  */
186 
187 static uint32 image_hash_name(char *str, int tablesize) {
188  uint32 hash = 0;
189  char *p;
190 
191  /* use the same one-at-a-time hash function the server now uses */
192  for (p = str; *p!='\0' && *p != '.'; p++) {
193  hash += *p;
194  hash += hash << 10;
195  hash ^= hash >> 6;
196  }
197  hash += hash << 3;
198  hash ^= hash >> 11;
199  hash += hash << 15;
200  return hash % tablesize;
201 }
202 
203 /* This function returns an index into the image_cache for
204  * a matching entry, -1 if no match is found.
205  */
206 static sint32 image_find_hash(char *str)
207 {
208  uint32 hash = image_hash_name(str, IMAGE_HASH), newhash;
209 
210  newhash = hash;
211  do {
212  /* No entry - return immediately */
213  if (image_cache[newhash].image_name == NULL) return -1;
214  if (!strcmp(image_cache[newhash].image_name, str)) return newhash;
215  newhash ++;
216  if (newhash == IMAGE_HASH) newhash=0;
217  } while (newhash != hash);
218 
219  /* If the hash table is full, this is bad because we won't be able to
220  * add any new entries.
221  */
222  LOG(LOG_WARNING,"common::image_find_hash","Hash table is full, increase IMAGE_CACHE size");
223  return -1;
224 }
225 
226 static void image_remove_hash(char *imagename, Cache_Entry *ce)
227 {
228  int hash_entry;
229  Cache_Entry *last;
230 
231  hash_entry = image_find_hash(imagename);
232  if (hash_entry == -1) {
233  LOG(LOG_ERROR,"common::image_remove_hash","Unable to find cache entry for %s, %s", imagename, ce->filename);
234  return;
235  }
236  if (image_cache[hash_entry].cache_entry == ce) {
237  image_cache[hash_entry].cache_entry = ce->next;
238  free(ce->filename);
239  free(ce);
240  return;
241  }
242  last = image_cache[hash_entry].cache_entry;
243  while (last->next && last->next != ce) last=last->next;
244  if (!last->next) {
245  LOG(LOG_ERROR,"common::image_rmove_hash","Unable to find cache entry for %s, %s", imagename, ce->filename);
246  return;
247  }
248  last->next = ce->next;
249  free(ce->filename);
250  free(ce);
251 }
252 
253 
254 
255 /* This finds and returns the Cache_Entry of the image that matches name
256  * and checksum if has_sum is set. If has_sum is not set, we can't
257  * do a checksum comparison.
258  */
259 
260 static Cache_Entry *image_find_cache_entry(char *imagename, uint32 checksum, int has_sum)
261 {
262  int hash_entry;
263  Cache_Entry *entry;
264 
265  hash_entry = image_find_hash(imagename);
266  if (hash_entry == -1) return NULL;
267  entry = image_cache[hash_entry].cache_entry;
268  if (has_sum) {
269  while (entry) {
270  if (entry->checksum == checksum) break;
271  entry = entry->next;
272  }
273  }
274  return entry; /* This could be NULL */
275 }
276 
277 /* Add a hash entry. Returns the entry we added, NULL on failure.. */
278 static Cache_Entry *image_add_hash(char *imagename, char *filename, uint32 checksum, uint32 ispublic)
279 {
280  Cache_Entry *new_entry;
281  uint32 hash = image_hash_name(imagename, IMAGE_HASH), newhash;
282 
283  newhash = hash;
284  while (image_cache[newhash].image_name != NULL &&
285  strcmp(image_cache[newhash].image_name, imagename)) {
286  newhash ++;
287  if (newhash == IMAGE_HASH) newhash=0;
288  /* If the hash table is full, can't do anything */
289  if (newhash == hash) {
290  LOG(LOG_WARNING,"common::image_find_hash","Hash table is full, increase IMAGE_CACHE size");
291  return NULL;
292  }
293  }
294  if (!image_cache[newhash].image_name) {
295  image_cache[newhash].image_name = strdup(imagename);
296  }
297 
298  /* We insert the new entry at the start of the list of the buckets
299  * for this entry. In the case of the players entries, this probably
300  * improves performance, presuming ones later in the file are more likely
301  * to be used compared to those at the start of the file.
302  */
303  new_entry = malloc(sizeof(struct Cache_Entry));
304  new_entry->filename = strdup(filename);
305  new_entry->checksum = checksum;
306  new_entry->ispublic = ispublic;
307  new_entry->image_data = NULL;
308  new_entry->next = image_cache[newhash].cache_entry;
309  image_cache[newhash].cache_entry = new_entry;
310  return new_entry;
311 }
312 
313 /* Process a line from the bmaps.client file. In theory, the
314  * format should be quite strict, as it is computer generated,
315  * but we try to be lenient/follow some conventions.
316  * Note that this is destructive to the data passed in line.
317  */
318 static void image_process_line(char *line, uint32 ispublic)
319 {
320  char imagename[MAX_BUF], filename[MAX_BUF];
322 
323  if (line[0] == '#') return; /* Ignore comments */
324 
325  if (sscanf(line, "%s %u %s", imagename, &checksum, filename)==3) {
326  image_add_hash(imagename, filename, checksum, ispublic);
327  } else {
328  LOG(LOG_WARNING,"common::image_process_line","Did not parse line %s properly?", line);
329  }
330 }
331 
333 {
334  FILE *fp;
335  char bmaps[MAX_BUF], inbuf[MAX_BUF];
336  int i;
337 
339  return;
340 
341  for (i = 0; i < MAXPIXMAPNUM; i++)
342  facetoname[i] = NULL;
343 
344  /* First, make sure that image_cache is nulled out */
345  memset(image_cache, 0, IMAGE_HASH * sizeof(struct Image_Cache));
346 
347 
348  snprintf(bmaps, sizeof(bmaps), "%s/bmaps.client",CF_DATADIR);
349  if ((fp=fopen(bmaps,"r"))!=NULL) {
350  while (fgets(inbuf, MAX_BUF-1, fp)!=NULL) {
351  image_process_line(inbuf, 1);
352  }
353  fclose(fp);
354  } else {
355  snprintf(inbuf, sizeof(inbuf), "Unable to open %s. You may wish to download and install the image file to improve performance.\n", bmaps);
356  draw_info(inbuf, NDI_RED);
357  }
358 
359  snprintf(bmaps, sizeof(bmaps), "%s/.crossfire/image-cache/bmaps.client", getenv("HOME"));
360  if ((fp=fopen(bmaps,"r"))!=NULL) {
361  while (fgets(inbuf, MAX_BUF-1, fp)!=NULL) {
362  image_process_line(inbuf, 0);
363  }
364  fclose(fp);
365  } /* User may not have a cache, so no error if not found */
366  for (i=0; i<MAX_FACE_SETS; i++) {
367  fd_cache[i].fd = -1;
368  fd_cache[i].name[0] = '\0';
369  }
370 }
371 
372 
373 /******************************************************************************
374  *
375  * Code related to face caching.
376  *
377  *****************************************************************************/
378 
380 
381 void requestface(int pnum, char *facename)
382 {
383  face_info.cache_misses++;
384  facetoname[pnum] = strdup_local(facename);
385  cs_print_string(csocket.fd, "askface %d", pnum);
386 }
387 
388 
389 
390 /* This is common for all the face commands (face2, face1, face).
391  * For face1 and face commands, faceset should always be zero.
392  * for face commands, has_sum and checksum will be zero.
393  * pnum is the face number, while face is the name.
394  * We actually don't care what the set it - it could be useful right now,
395  * but in the current caching scheme, we look through all the facesets for
396  * the image and if the checksum matches, we assume we have match.
397  * This approach makes sure that we don't have to store the same image multiple
398  * times simply because the set number may be different.
399  */
400 void finish_face_cmd(int pnum, uint32 checksum, int has_sum, char *face, int faceset)
401 {
402  int len;
403  uint32 nx,ny;
404  uint8 data[65536], *png_tmp;
405  char filename[1024];
406  uint32 newsum=0;
407  Cache_Entry *ce=NULL;
408 
409 #if 0
410  fprintf(stderr,"finish_face_cmd, pnum=%d, checksum=%d, face=%s\n",
411  pnum, checksum, face);
412 #endif
413  /* In the case of gfx, we don't care about checksum. For public and
414  * private areas, if we care about checksum, and the image doesn't match,
415  * we go onto the next step. If nothing found, we request it
416  * from the server.
417  */
418  snprintf(filename, sizeof(filename), "%s/.crossfire/gfx/%s.png", getenv("HOME"), face);
419  if (load_image(filename, data, &len, &newsum)==-1) {
420  ce=image_find_cache_entry(face, checksum, has_sum);
421  if (!ce) {
422  /* Not in our cache, so request it from the server */
423  requestface(pnum, face);
424  return;
425  }
426  else if (ce->image_data) {
427  /* If this has image_data, then it has already been rendered */
428  if (!associate_cache_entry(ce, pnum)) return;
429  }
430  if (ce->ispublic)
431  snprintf(filename, sizeof(filename), "%s/%s",
432  CF_DATADIR, ce->filename);
433  else
434  snprintf(filename, sizeof(filename), "%s/.crossfire/image-cache/%s",
435  getenv("HOME"), ce->filename);
436  if (load_image(filename, data, &len, &newsum)==-1) {
437  LOG(LOG_WARNING,"common::finish_face_cmd","file %s listed in cache file, but unable to load", filename);
438  requestface(pnum, face);
439  return;
440  }
441  }
442 
443  /* If we got here, we found an image and the checksum is OK. */
444 
445  if (!(png_tmp = png_to_data(data, len, &nx, &ny))) {
446  /* If the data is bad, remove it if it is in the players private cache */
447  LOG(LOG_WARNING,"common::finish_face_cmd","Got error on png_to_data, image=%s",face);
448  if (ce) {
449  if (!ce->ispublic) unlink(filename);
450  image_remove_hash(face,ce);
451  }
452 
453  requestface(pnum, face);
454  }
455 
456  /* create_and_rescale_image_from data is an external reference to a piece in
457  * the gui section of the code.
458  */
459  if (create_and_rescale_image_from_data(ce, pnum, png_tmp,nx, ny)) {
460  LOG(LOG_WARNING,"common::finish_face_cmd","Got error on create_and_rescale_image_from_data, file=%s",filename);
461  requestface(pnum, face);
462  }
463  free(png_tmp);
464 }
465 
466 
467 /* We can now connect to different servers, so we need to clear out
468  * any old images. We try to free the data also to prevent memory
469  * leaks.
470  * Note that we don't touch our hashed entries - so that when we
471  * connect to a new server, we still have all that information.
472  */
473 
475 {
476  int i;
477 
478  if (want_config[CONFIG_CACHE]) for (i=1; i<MAXPIXMAPNUM; i++) {
479  free(facetoname[i]);
480  facetoname[i]=NULL;
481  }
482 }
483 
484 
485 /* We only get here if the server believes we are caching images. */
486 /* We rely on the fact that the server will only send a face command for
487  * a particular number once - at current time, we have no way of knowing
488  * if we have already received a face for a particular number.
489  */
490 
491 void Face2Cmd(uint8 *data, int len)
492 {
493  int pnum;
494  uint8 setnum;
496  char *face;
497 
498  /* A quick sanity check, since if client isn't caching, all the data
499  * structures may not be initialized.
500  */
501  if (!use_config[CONFIG_CACHE]) {
502  LOG(LOG_WARNING,"common::Face2Cmd","Received a 'face' command when we are not caching");
503  return;
504  }
505  pnum = GetShort_String(data);
506  setnum = data[2];
507  checksum = GetInt_String(data+3);
508  face = (char*)data+7;
509  data[len] = '\0';
510 
511  finish_face_cmd(pnum, checksum, 1, face,setnum);
512 }
513 
514 void Image2Cmd(uint8 *data, int len)
515 {
516  int pnum,plen;
517  uint8 setnum;
518 
519  pnum = GetInt_String(data);
520  setnum = data[4];
521  plen = GetInt_String(data+5);
522  if (len<9 || (len-9)!=plen) {
523  LOG(LOG_WARNING,"common::Image2Cmd","Lengths don't compare (%d,%d)",
524  (len-9),plen);
525  return;
526  }
527  display_newpng(pnum,data+9,plen, setnum);
528 }
529 
530 /*
531  * This function is called when the server has sent us the actual
532  * png data for an image. If caching, we need to write this
533  * data to disk.
534  */
535 void display_newpng(int face, uint8 *buf, int buflen, int setnum)
536 {
537  char filename[MAX_BUF], basename[MAX_BUF];
538  uint8 *pngtmp;
539  FILE *tmpfile;
540  uint32 width, height, csum, i;
541  Cache_Entry *ce=NULL;
542 
543  if (use_config[CONFIG_CACHE]) {
544  if (facetoname[face]==NULL) {
545  LOG(LOG_WARNING,"common::display_newpng","Caching images, but name for %ld not set", face);
546  }
547  /* Make necessary leading directories */
548  snprintf(filename, sizeof(filename), "%s/.crossfire/image-cache",getenv("HOME"));
549  if (access(filename, R_OK | W_OK | X_OK)== -1)
550 #ifdef WIN32
551  mkdir(filename);
552 #else
553  mkdir(filename, 0755);
554 #endif
555 
556  snprintf(filename, sizeof(filename), "%s/.crossfire/image-cache/%c%c", getenv("HOME"),
557  facetoname[face][0], facetoname[face][1]);
558  if (access(filename, R_OK | W_OK | X_OK)== -1)
559 #ifdef WIN32
560  mkdir(filename);
561 #else
562  mkdir(filename,0755);
563 #endif
564 
565  /* If setnum is valid, and we have faceset information for it,
566  * put that prefix in. This will make it easier later on to
567  * allow the client to switch image sets on the fly, as it can
568  * determine what set the image belongs to.
569  * We also append the number to it - there could be several versions
570  * of 'face.base.111.x' if different servers have different image
571  * values.
572  */
573  if (setnum >=0 && setnum < MAX_FACE_SETS && face_info.facesets[setnum].prefix)
574  snprintf(basename, sizeof(basename),"%s.%s", facetoname[face], face_info.facesets[setnum].prefix);
575  else
576  strcpy(basename, facetoname[face]);
577 
578  /* Decrease it by one since it will immediately get increased
579  * in the loop below.
580  */
581  setnum--;
582  do {
583  setnum++;
584  snprintf(filename, sizeof(filename), "%s/.crossfire/image-cache/%c%c/%s.%d",
585  getenv("HOME"), facetoname[face][0],
586  facetoname[face][1], basename, setnum);
587  } while (access(filename, F_OK)==-0);
588 
589 #ifdef WIN32
590  if ((tmpfile = fopen(filename,"wb"))==NULL) {
591 #else
592  if ((tmpfile = fopen(filename,"w"))==NULL) {
593 #endif
594  LOG(LOG_WARNING,"common::display_newpng","Can not open %s for writing", filename);
595  }
596  else {
597  /* found a file we can write to */
598 
599  fwrite(buf, buflen, 1, tmpfile);
600  fclose(tmpfile);
601  csum=0;
602  for (i=0; (int)i<buflen; i++) {
603  ROTATE_RIGHT(csum);
604  csum += buf[i];
605  csum &= 0xffffffff;
606  }
607  snprintf(filename, sizeof(filename), "%c%c/%s.%d", facetoname[face][0], facetoname[face][1],
608  basename, setnum);
609  ce = image_add_hash(facetoname[face], filename, csum, 0);
610 
611  /* It may very well be more efficient to try to store these up
612  * and then write them as a bunch instead of constantly opening the
613  * file for appending. OTOH, hopefully people will be using the
614  * built image archives, so only a few faces actually need to get
615  * downloaded.
616  */
617  snprintf(filename, sizeof(filename), "%s/.crossfire/image-cache/bmaps.client", getenv("HOME"));
618  if ((tmpfile=fopen(filename, "a"))==NULL) {
619  LOG(LOG_WARNING,"common::display_newpng","Can not open %s for appending", filename);
620  }
621  else {
622  fprintf(tmpfile, "%s %u %c%c/%s.%d\n",
623  facetoname[face], csum, facetoname[face][0],
624  facetoname[face][1], basename, setnum);
625  fclose(tmpfile);
626  }
627  }
628  }
629 
630  pngtmp = png_to_data(buf, buflen, &width, &height);
631  if(create_and_rescale_image_from_data(ce, face, pngtmp, width, height)) {
632  LOG(LOG_WARNING, "common::display_newpng", "create_and_rescale_image_from_data failed for face %ld", face);
633  }
634 
635  if (use_config[CONFIG_CACHE]) {
636  free(facetoname[face]);
637  facetoname[face]=NULL;
638  }
639  free(pngtmp);
640 }
641 
642 
643 /* get_image_info takes the data from a replyinfo image_info
644  * and breaks it down. The info contained is the checkums,
645  * number of images, and faceset information. It stores this
646  * data into the face_info structure.
647  * Since we know data is null terminated, we can use the strchr
648  * operations with safety.
649  * In each block, we find the newline - if we find one, we presume
650  * the data is good, and update the face_info accordingly.
651  * if we don't find a newline, we return.
652  */
653 void get_image_info(uint8 *data, int len)
654 {
655  char *cp, *lp, *cps[7], buf[MAX_BUF];
656  int onset=0, badline=0,i;
657 
659 
660  lp = (char*)data;
661  cp = strchr(lp, '\n');
662  if (!cp || (cp - lp) > len) return;
663  face_info.num_images = atoi(lp);
664 
665  lp = cp+1;
666  cp = strchr(lp, '\n');
667  if (!cp || (cp - lp) > len) return;
668  face_info.bmaps_checksum = strtoul(lp, NULL, 10); /* need unsigned, so no atoi */
669 
670  lp = cp+1;
671  cp = strchr(lp, '\n');
672  while (cp && (cp - lp) <= len) {
673  *cp++ = '\0';
674 
675  /* The code below is pretty much the same as the code from the server
676  * which loads the original faceset file.
677  */
678  if (!(cps[0] = strtok(lp, ":"))) badline=1;
679  for (i=1; i<7; i++) {
680  if (!(cps[i] = strtok(NULL, ":"))) badline=1;
681  }
682  if (badline) {
683  LOG(LOG_WARNING,"common::get_image_info","bad data, ignoring line:/%s/", lp);
684  } else {
685  onset = atoi(cps[0]);
686  if (onset >=MAX_FACE_SETS) {
687  LOG(LOG_WARNING,"common::get_image_info","setnum is too high: %d > %d",
688  onset, MAX_FACE_SETS);
689  }
690  face_info.facesets[onset].prefix = strdup_local(cps[1]);
691  face_info.facesets[onset].fullname = strdup_local(cps[2]);
692  face_info.facesets[onset].fallback = atoi(cps[3]);
693  face_info.facesets[onset].size = strdup_local(cps[4]);
694  face_info.facesets[onset].extension = strdup_local(cps[5]);
695  face_info.facesets[onset].comment = strdup_local(cps[6]);
696  }
697  lp = cp;
698  cp = strchr(lp, '\n');
699  }
700  face_info.have_faceset_info = 1;
701  /* if the user has requested a specific face set and that set
702  * is not numeric, try to find a matching set and send the
703  * relevent setup command.
704  */
705  if (face_info.want_faceset && atoi(face_info.want_faceset)==0) {
706  for (onset=0; onset<MAX_FACE_SETS; onset++) {
707  if (face_info.facesets[onset].prefix &&
708  !strcasecmp(face_info.facesets[onset].prefix, face_info.want_faceset)) break;
709  if (face_info.facesets[onset].fullname &&
710  !strcasecmp(face_info.facesets[onset].fullname, face_info.want_faceset)) break;
711  }
712  if (onset < MAX_FACE_SETS) { /* We found a match */
713  face_info.faceset = onset;
714  cs_print_string(csocket.fd, "setup faceset %d", onset);
715  } else {
716  snprintf(buf, sizeof(buf), "Unable to find match for faceset %s on the server", face_info.want_faceset);
717  draw_info(buf, NDI_RED);
718  }
719  }
720 
721 }
722 
723 /* This gets a block of checksums from the server. This lets it
724  * prebuild the images or what not. It would probably be
725  * nice to add a gui callback someplace that gives a little status
726  * display (18% done or whatever) - that probably needs to be done
727  * further up.
728  *
729  * The start and stop values are not meaningful - they are here
730  * because the semantics of the requestinfo/replyinfo is that
731  * replyinfo includes the same request data as the requestinfo
732  * (thus, if the request failed for some reason, the client would
733  * know which one failed and then try again). Currently, we
734  * don't have any logic in the function below to deal with failures.
735  */
736 
737 void get_image_sums(char *data, int len)
738 {
739  int start, stop, imagenum, slen, faceset;
741  char *cp, *lp;
742 
743  cp = strchr((char*)data, ' ');
744  if (!cp || (cp - data) > len) return;
745  start = atoi((char*)data);
746 
747  while (isspace(*cp)) cp++;
748  lp = cp;
749  cp = strchr(lp, ' ');
750  if (!cp || (cp - data) > len) return;
751  stop = atoi(lp);
752 
753  replyinfo_last_face = stop;
754 
755  /* Can't use isspace here, because it matches with tab, ascii code
756  * 9 - this results in advancing too many spaces because
757  * starting at image 2304, the MSB of the image number will be
758  * 9. Using a check against space will work until we get up to
759  * 8192 images.
760  */
761  while (*cp==' ') cp++;
762  while ((cp - data) < len) {
763  imagenum = GetShort_String((uint8*)cp); cp += 2;
764  checksum = GetInt_String((uint8*)cp); cp += 4;
765  faceset = *cp; cp++;
766  slen = *cp; cp++;
767  /* Note that as is, this can break horribly if the client is missing a large number
768  * of images - that is because it will request a whole bunch which will overflow
769  * the servers output buffer, causing it to close the connection.
770  * What probably should be done is for the client to just request this checksum
771  * information in small batches so that even if the client has no local
772  * images, requesting the entire batch won't overflow the sockets buffer - this
773  * probably amounts to about 100 images at a time
774  */
775  finish_face_cmd(imagenum, checksum, 1, (char*)cp, faceset);
776  if (imagenum > stop)
777  LOG(LOG_WARNING,"common::get_image_sums","Received an image beyond our range? %d > %d", imagenum, stop);
778  cp += slen;
779  }
780 }
char * comment
Definition: client.h:336
int replyinfo_last_face
Definition: client.c:68
char facecachedir[MAX_BUF]
Definition: image.c:379
short GetShort_String(const unsigned char *data)
Definition: newsocket.c:162
static sint32 image_find_hash(char *str)
Definition: image.c:206
Definition: image.c:51
#define ROTATE_RIGHT(c)
Definition: image.c:46
ClientSocket csocket
Definition: client.c:78
static int load_image(char *filename, uint8 *data, int *len, uint32 *csum)
Definition: image.c:65
struct Image_Cache image_cache[IMAGE_HASH]
FaceSets facesets[MAX_FACE_SETS]
Definition: client.h:355
uint8 fallback
Definition: client.h:331
#define MAXPIXMAPNUM
Definition: client.h:470
uint32 ispublic
Definition: client.h:483
sint16 want_config[CONFIG_NUMS]
Definition: init.c:50
struct Cache_Entry * cache_entry
Definition: image.c:177
uint32 width
Definition: image.c:64
#define IMAGE_HASH
Definition: image.c:165
char * prefix
Definition: client.h:332
void Image2Cmd(uint8 *data, int len)
Definition: image.c:514
void * image_data
Definition: client.h:484
int create_and_rescale_image_from_data(Cache_Entry *ce, int pixmap_num, uint8 *rgba_data, int width, int height)
Definition: image.c:252
uint32 checksum
Definition: client.h:482
char * filename
Definition: client.h:481
void Face2Cmd(uint8 *data, int len)
Definition: image.c:491
void LOG(LogLevel level, const char *origin, const char *format,...)
Definition: misc.c:178
uint8 * png_to_data(uint8 *data, int len, uint32 *width, uint32 *height)
Definition: png.c:63
#define NDI_RED
Definition: newclient.h:204
char * size
Definition: client.h:334
static uint32 image_hash_name(char *str, int tablesize)
Definition: image.c:187
int cs_print_string(int fd, const char *str,...)
Definition: newsocket.c:259
sint16 use_config[CONFIG_NUMS]
Definition: init.c:50
struct Cache_Entry * next
Definition: client.h:485
struct FD_Cache fd_cache[MAX_FACE_SETS]
static Cache_Entry * image_add_hash(char *imagename, char *filename, uint32 checksum, uint32 ispublic)
Definition: image.c:278
#define CONFIG_CACHE
Definition: client.h:156
char * name
Definition: image.c:61
char name[MAX_BUF]
Definition: image.c:52
static Cache_Entry * image_find_cache_entry(char *imagename, uint32 checksum, int has_sum)
Definition: image.c:260
int fd
Definition: client.h:97
void reset_image_cache_data(void)
Definition: image.c:474
void get_image_sums(char *data, int len)
Definition: image.c:737
static void image_remove_hash(char *imagename, Cache_Entry *ce)
Definition: image.c:226
void display_newpng(int face, uint8 *buf, int buflen, int setnum)
Definition: image.c:535
uint32 height
Definition: image.c:64
char * extension
Definition: client.h:335
#define MAX_BUF
Definition: client-types.h:128
int replyinfo_status
Definition: client.c:68
unsigned int uint32
Definition: client-types.h:77
#define MAX_FACE_SETS
Definition: client.h:315
static void image_process_line(char *line, uint32 ispublic)
Definition: image.c:318
signed int sint32
Definition: client-types.h:78
char * strdup_local(const char *str)
Definition: misc.c:125
Definition: client.h:480
char * fullname
Definition: client.h:333
char * image_name
Definition: image.c:176
void requestface(int pnum, char *facename)
Definition: image.c:381
void init_common_cache_data(void)
Definition: image.c:332
void draw_info(const char *str, int color)
Definition: gx11.c:1773
const char *const rcsid_common_image_c
Definition: image.c:1
void finish_face_cmd(int pnum, uint32 checksum, int has_sum, char *face, int faceset)
Definition: image.c:400
Face_Information face_info
Definition: image.c:167
unsigned char uint8
Definition: client-types.h:81
int associate_cache_entry(Cache_Entry *ce, int pixnum)
Definition: image.c:372
#define RI_IMAGE_INFO
Definition: client.h:499
int GetInt_String(const unsigned char *data)
Definition: newsocket.c:143
static char * facetoname[MAXPIXMAPNUM]
Definition: image.c:172
int fd
Definition: image.c:53
void get_image_info(uint8 *data, int len)
Definition: image.c:653
uint32 checksum
Definition: image.c:62