Crossfire Client, Branch  R11627
image.c
Go to the documentation of this file.
00001 const char * const rcsid_common_image_c =
00002     "$Id: image.c 9201 2008-06-01 17:32:45Z anmaster $";
00003 /*
00004     Crossfire client, a client program for the crossfire program.
00005 
00006     Copyright (C) 2001 Mark Wedel & Crossfire Development Team
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 
00022     The author can be reached via e-mail to crossfire-devel@real-time.com
00023 */
00024 
00025 /*
00026  * This file contains image related functions - this is a higher level up -
00027  * it mostly deals with the caching of the images, processing the image commands
00028  * from the server, etc.
00029  */
00030 
00031 #include <config.h>
00032 #include <stdlib.h>
00033 #include <sys/stat.h>
00034 #ifndef WIN32
00035 #include <unistd.h>
00036 #else
00037 #include <io.h>
00038 #include <direct.h>
00039 #endif
00040 #include <ctype.h>
00041 
00042 #include "client.h"
00043 #include "external.h"
00044 
00045 /* Rotate right from bsd sum. */
00046 #define ROTATE_RIGHT(c) if ((c) & 01) (c) = ((c) >>1) + 0x80000000; else (c) >>= 1;
00047 
00048 /*#define CHECKSUM_DEBUG*/
00049 
00050 
00051 struct FD_Cache {
00052     char    name[MAX_BUF];
00053     int     fd;
00054 } fd_cache[MAX_FACE_SETS];
00055 
00056 
00057 /* given the filename, this tries to load the data.  It returns 0 success,
00058  * -1 on failure.  It returns the data and len, the passed
00059  * options.  csum is set to zero or unset - changes have made it such
00060  * that the caller knows whether or not the checksum matches, so there
00061  * is little point to re-do it.
00062  * data should be a buffer already allocated.
00063  */
00064 
00065 static int load_image(char *filename, uint8 *data, int *len, uint32 *csum)
00066 {
00067     int fd, i;
00068     char *cp;
00069 
00070     /* If the name includes an @, then that is a combined image file,
00071      * so we need to load the image a bit specially.  By using these
00072      * combined image files, it reduces number of opens needed.
00073      * In fact, we keep track of which ones we have opened to improve
00074      * performance.  Note that while not currently done, this combined
00075      * image scheme could be done when storing images in the players
00076      * ~/.crossfire/image-cache directory.
00077      */
00078     if ((cp=strchr(filename,'@'))!=NULL) {
00079         char *lp;
00080         int offset, length, last=-1;
00081 
00082         offset = atoi(cp + 1);
00083         lp = strchr(cp,':');
00084         if (!lp) {
00085             LOG(LOG_ERROR,"common::load_image","Corrupt filename - has '@' but no ':' ?(%s)", filename);
00086             return -1;
00087         }
00088         length = atoi(lp + 1);
00089         *cp = 0;
00090         for (i=0; i<MAX_FACE_SETS; i++) {
00091             if (!strcmp(fd_cache[i].name, filename)) break;
00092             if (last==-1 && fd_cache[i].fd == -1) last = i;
00093         }
00094         /* Didn't find a matching entry yet, so make one */
00095         if (i == MAX_FACE_SETS) {
00096             if (last == -1) {
00097                 LOG(LOG_WARNING,"common::load_image","fd_cache filled up?  unable to load matching cache entry");
00098                 *cp = '@';      /* put @ back in string */
00099                 return -1;
00100             }
00101 #ifdef WIN32
00102             if ((fd_cache[last].fd = open(filename, O_RDONLY | O_BINARY))==-1) {
00103 #else
00104             if ((fd_cache[last].fd = open(filename, O_RDONLY))==-1) {
00105 #endif
00106                 LOG(LOG_WARNING,"common::load_image","unable to load listed cache file %s",filename);
00107                 *cp = '@';      /* put @ back in string */
00108                 return -1;
00109             }
00110             strcpy(fd_cache[last].name, filename);
00111             i=last;
00112         }
00113         lseek(fd_cache[i].fd, offset, SEEK_SET);
00114 #ifdef WIN32
00115         *len = read(fd_cache[i].fd, data, length);
00116 #else
00117         *len = read(fd_cache[i].fd, data, 65535);
00118 #endif
00119         *cp = '@';
00120     }
00121     else {
00122 #ifdef WIN32
00123     int length = 0;
00124         if ((fd=open(filename, O_RDONLY | O_BINARY))==-1) return -1;
00125     length = lseek(fd, 0, SEEK_END);
00126     lseek(fd, 0, SEEK_SET);
00127     *len=read(fd, data, length);
00128 #else
00129         if ((fd=open(filename, O_RDONLY))==-1) return -1;
00130         *len=read(fd, data, 65535);
00131 #endif
00132         close(fd);
00133     }
00134 
00135     face_info.cache_hits++;
00136     *csum = 0;
00137     return 0;
00138 
00139 #if 0
00140     /* Shouldn't be needed anymore */
00141         *csum=0;
00142         for (i=0; i<*len; i++) {
00143             ROTATE_RIGHT(*csum);
00144             *csum += data[i];
00145             *csum &= 0xffffffff;
00146         }
00147 #endif
00148 
00149 }
00150 /*******************************************************************************
00151  *
00152  * This is our image caching logic.  We use a hash to make the name lookups
00153  * happen quickly - this is done for speed, but also because we don't really
00154  * have a good idea on how many images may used.  It also means that as the
00155  * cache gets filled up with images in a random order, the lookup is still
00156  * pretty quick.
00157  *
00158  * If a bucket is filled with an entry that is not of the right name,
00159  * we store/look for the correct one in the next bucket.
00160  *
00161  *******************************************************************************
00162  */
00163 
00164 /* This should be a power of 2 */
00165 #define IMAGE_HASH  8192
00166 
00167 Face_Information face_info;
00168 
00169 /* This holds the name we recieve with the 'face' command so we know what
00170  * to save it as when we actually get the face.
00171  */
00172 static char *facetoname[MAXPIXMAPNUM];
00173 
00174 
00175 struct Image_Cache {
00176     char    *image_name;
00177     struct Cache_Entry  *cache_entry;
00178 } image_cache[IMAGE_HASH];
00179 
00180 
00181 /* This function is basically hasharch from the server, common/arch.c
00182  * a few changes - first, we stop processing when we reach the first
00183  * . - this is because I'm not sure if hashing .111 at the end of
00184  * all the image names will be very useful.
00185  */
00186 
00187 static uint32 image_hash_name(char *str, int tablesize) {
00188     uint32 hash = 0;
00189     char *p;
00190 
00191     /* use the same one-at-a-time hash function the server now uses */
00192     for (p = str; *p!='\0' && *p != '.'; p++) {
00193         hash += *p;
00194         hash += hash << 10;
00195         hash ^= hash >>  6;
00196     }
00197     hash += hash <<  3;
00198     hash ^= hash >> 11;
00199     hash += hash << 15;
00200     return hash % tablesize;
00201 }
00202 
00203 /* This function returns an index into the image_cache for
00204  * a matching entry, -1 if no match is found.
00205  */
00206 static sint32 image_find_hash(char *str)
00207 {
00208     uint32  hash = image_hash_name(str, IMAGE_HASH), newhash;
00209 
00210     newhash = hash;
00211     do {
00212         /* No entry - return immediately */
00213         if (image_cache[newhash].image_name == NULL) return -1;
00214         if (!strcmp(image_cache[newhash].image_name, str)) return newhash;
00215         newhash ++;
00216         if (newhash == IMAGE_HASH) newhash=0;
00217     } while (newhash != hash);
00218 
00219     /* If the hash table is full, this is bad because we won't be able to
00220      * add any new entries.
00221      */
00222     LOG(LOG_WARNING,"common::image_find_hash","Hash table is full, increase IMAGE_CACHE size");
00223     return -1;
00224 }
00225 
00226 static void image_remove_hash(char *imagename, Cache_Entry *ce)
00227 {
00228     int hash_entry;
00229     Cache_Entry *last;
00230 
00231     hash_entry = image_find_hash(imagename);
00232     if (hash_entry == -1) {
00233         LOG(LOG_ERROR,"common::image_remove_hash","Unable to find cache entry for %s, %s", imagename, ce->filename);
00234         return;
00235     }
00236     if (image_cache[hash_entry].cache_entry == ce) {
00237         image_cache[hash_entry].cache_entry = ce->next;
00238         free(ce->filename);
00239         free(ce);
00240         return;
00241     }
00242     last = image_cache[hash_entry].cache_entry;
00243     while (last->next && last->next != ce) last=last->next;
00244     if (!last->next) {
00245         LOG(LOG_ERROR,"common::image_rmove_hash","Unable to find cache entry for %s, %s", imagename, ce->filename);
00246         return;
00247     }
00248     last->next = ce->next;
00249     free(ce->filename);
00250     free(ce);
00251 }
00252 
00253 
00254 
00255 /* This finds and returns the Cache_Entry of the image that matches name
00256  * and checksum if has_sum is set.  If has_sum is not set, we can't
00257  * do a checksum comparison.
00258  */
00259 
00260 static Cache_Entry *image_find_cache_entry(char *imagename, uint32 checksum, int has_sum)
00261 {
00262     int hash_entry;
00263     Cache_Entry *entry;
00264 
00265     hash_entry = image_find_hash(imagename);
00266     if (hash_entry == -1) return NULL;
00267     entry = image_cache[hash_entry].cache_entry;
00268     if (has_sum) {
00269         while (entry) {
00270             if (entry->checksum == checksum) break;
00271             entry = entry->next;
00272         }
00273     }
00274     return entry;   /* This could be NULL */
00275 }
00276 
00277 /* Add a hash entry.  Returns the entry we added, NULL on failure.. */
00278 static Cache_Entry *image_add_hash(char *imagename, char *filename, uint32 checksum, uint32 ispublic)
00279 {
00280     Cache_Entry *new_entry;
00281     uint32  hash = image_hash_name(imagename, IMAGE_HASH), newhash;
00282 
00283     newhash = hash;
00284     while (image_cache[newhash].image_name != NULL &&
00285       strcmp(image_cache[newhash].image_name, imagename)) {
00286         newhash ++;
00287         if (newhash == IMAGE_HASH) newhash=0;
00288         /* If the hash table is full, can't do anything */
00289         if (newhash == hash) {
00290             LOG(LOG_WARNING,"common::image_find_hash","Hash table is full, increase IMAGE_CACHE size");
00291             return NULL;
00292         }
00293     }
00294     if (!image_cache[newhash].image_name) {
00295         image_cache[newhash].image_name = strdup(imagename);
00296     }
00297 
00298     /* We insert the new entry at the start of the list of the buckets
00299      * for this entry.  In the case of the players entries, this probably
00300      * improves performance, presuming ones later in the file are more likely
00301      * to be used compared to those at the start of the file.
00302      */
00303     new_entry = malloc(sizeof(struct Cache_Entry));
00304     new_entry->filename = strdup(filename);
00305     new_entry->checksum = checksum;
00306     new_entry->ispublic = ispublic;
00307     new_entry->image_data = NULL;
00308     new_entry->next = image_cache[newhash].cache_entry;
00309     image_cache[newhash].cache_entry = new_entry;
00310     return new_entry;
00311 }
00312 
00313 /* Process a line from the bmaps.client file.  In theory, the
00314  * format should be quite strict, as it is computer generated,
00315  * but we try to be lenient/follow some conventions.
00316  * Note that this is destructive to the data passed in line.
00317  */
00318 static void image_process_line(char *line, uint32 ispublic)
00319 {
00320     char imagename[MAX_BUF], filename[MAX_BUF];
00321     uint32 checksum;
00322 
00323     if (line[0] == '#') return;             /* Ignore comments */
00324 
00325     if (sscanf(line, "%s %u %s", imagename, &checksum, filename)==3) {
00326         image_add_hash(imagename, filename, checksum, ispublic);
00327     } else {
00328         LOG(LOG_WARNING,"common::image_process_line","Did not parse line %s properly?", line);
00329     }
00330 }
00331 
00332 void init_common_cache_data(void)
00333 {
00334     FILE *fp;
00335     char    bmaps[MAX_BUF], inbuf[MAX_BUF];
00336     int i;
00337 
00338     if (!want_config[CONFIG_CACHE])
00339         return;
00340 
00341     for (i = 0; i < MAXPIXMAPNUM; i++)
00342         facetoname[i] = NULL;
00343 
00344     /* First, make sure that image_cache is nulled out */
00345     memset(image_cache, 0, IMAGE_HASH * sizeof(struct Image_Cache));
00346 
00347 
00348     snprintf(bmaps, sizeof(bmaps), "%s/bmaps.client",CF_DATADIR);
00349     if ((fp=fopen(bmaps,"r"))!=NULL) {
00350         while (fgets(inbuf, MAX_BUF-1, fp)!=NULL) {
00351             image_process_line(inbuf, 1);
00352         }
00353         fclose(fp);
00354     } else {
00355         snprintf(inbuf, sizeof(inbuf), "Unable to open %s.  You may wish to download and install the image file to improve performance.\n", bmaps);
00356         draw_info(inbuf, NDI_RED);
00357     }
00358 
00359     snprintf(bmaps, sizeof(bmaps), "%s/.crossfire/image-cache/bmaps.client", getenv("HOME"));
00360     if ((fp=fopen(bmaps,"r"))!=NULL) {
00361         while (fgets(inbuf, MAX_BUF-1, fp)!=NULL) {
00362             image_process_line(inbuf, 0);
00363         }
00364         fclose(fp);
00365     } /* User may not have a cache, so no error if not found */
00366     for (i=0; i<MAX_FACE_SETS; i++) {
00367         fd_cache[i].fd = -1;
00368         fd_cache[i].name[0] = '\0';
00369     }
00370 }
00371 
00372 
00373 /******************************************************************************
00374  *
00375  * Code related to face caching.
00376  *
00377  *****************************************************************************/
00378 
00379 char facecachedir[MAX_BUF];
00380 
00381 void requestface(int pnum, char *facename)
00382 {
00383     face_info.cache_misses++;
00384     facetoname[pnum] = strdup_local(facename);
00385     cs_print_string(csocket.fd, "askface %d", pnum);
00386 }
00387 
00388 
00389 
00390 /* This is common for all the face commands (face2, face1, face).
00391  * For face1 and face commands, faceset should always be zero.
00392  * for face commands, has_sum and checksum will be zero.
00393  * pnum is the face number, while face is the name.
00394  * We actually don't care what the set it - it could be useful right now,
00395  * but in the current caching scheme, we look through all the facesets for
00396  * the image and if the checksum matches, we assume we have match.
00397  * This approach makes sure that we don't have to store the same image multiple
00398  * times simply because the set number may be different.
00399  */
00400 void finish_face_cmd(int pnum, uint32 checksum, int has_sum, char *face, int faceset)
00401 {
00402     int len;
00403     uint32 nx,ny;
00404     uint8 data[65536], *png_tmp;
00405     char filename[1024];
00406     uint32 newsum=0;
00407     Cache_Entry *ce=NULL;
00408 
00409 #if 0
00410     fprintf(stderr,"finish_face_cmd, pnum=%d, checksum=%d, face=%s\n",
00411             pnum, checksum, face);
00412 #endif
00413     /* In the case of gfx, we don't care about checksum.  For public and
00414      * private areas, if we care about checksum, and the image doesn't match,
00415      * we go onto the next step.  If nothing found, we request it
00416      * from the server.
00417      */
00418     snprintf(filename, sizeof(filename), "%s/.crossfire/gfx/%s.png", getenv("HOME"), face);
00419     if (load_image(filename, data, &len, &newsum)==-1) {
00420         ce=image_find_cache_entry(face, checksum, has_sum);
00421         if (!ce) {
00422             /* Not in our cache, so request it from the server */
00423                 requestface(pnum, face);
00424                 return;
00425         }
00426         else if (ce->image_data) {
00427             /* If this has image_data, then it has already been rendered */
00428             if (!associate_cache_entry(ce, pnum)) return;
00429         }
00430         if (ce->ispublic)
00431             snprintf(filename, sizeof(filename), "%s/%s",
00432                     CF_DATADIR, ce->filename);
00433         else
00434             snprintf(filename, sizeof(filename), "%s/.crossfire/image-cache/%s",
00435                     getenv("HOME"), ce->filename);
00436         if (load_image(filename, data, &len, &newsum)==-1) {
00437             LOG(LOG_WARNING,"common::finish_face_cmd","file %s listed in cache file, but unable to load", filename);
00438             requestface(pnum, face);
00439             return;
00440         }
00441     }
00442 
00443     /* If we got here, we found an image and the checksum is OK. */
00444 
00445     if (!(png_tmp = png_to_data(data, len, &nx, &ny))) {
00446         /* If the data is bad, remove it if it is in the players private cache */
00447         LOG(LOG_WARNING,"common::finish_face_cmd","Got error on png_to_data, image=%s",face);
00448         if (ce) {
00449             if (!ce->ispublic) unlink(filename);
00450             image_remove_hash(face,ce);
00451         }
00452 
00453         requestface(pnum, face);
00454     }
00455 
00456     /* create_and_rescale_image_from data is an external reference to a piece in
00457      * the gui section of the code.
00458      */
00459     if (create_and_rescale_image_from_data(ce, pnum, png_tmp,nx, ny)) {
00460         LOG(LOG_WARNING,"common::finish_face_cmd","Got error on create_and_rescale_image_from_data, file=%s",filename);
00461         requestface(pnum, face);
00462     }
00463     free(png_tmp);
00464 }
00465 
00466 
00467 /* We can now connect to different servers, so we need to clear out
00468  * any old images.  We try to free the data also to prevent memory
00469  * leaks.
00470  * Note that we don't touch our hashed entries - so that when we
00471  * connect to a new server, we still have all that information.
00472  */
00473 
00474 void reset_image_cache_data(void)
00475 {
00476     int i;
00477 
00478     if (want_config[CONFIG_CACHE]) for (i=1; i<MAXPIXMAPNUM; i++) {
00479         free(facetoname[i]);
00480         facetoname[i]=NULL;
00481     }
00482 }
00483 
00484 
00485 /* We only get here if the server believes we are caching images. */
00486 /* We rely on the fact that the server will only send a face command for
00487  * a particular number once - at current time, we have no way of knowing
00488  * if we have already received a face for a particular number.
00489  */
00490 
00491 void Face2Cmd(uint8 *data,  int len)
00492 {
00493     int pnum;
00494     uint8 setnum;
00495     uint32  checksum;
00496     char *face;
00497 
00498     /* A quick sanity check, since if client isn't caching, all the data
00499      * structures may not be initialized.
00500      */
00501     if (!use_config[CONFIG_CACHE]) {
00502         LOG(LOG_WARNING,"common::Face2Cmd","Received a 'face' command when we are not caching");
00503         return;
00504     }
00505     pnum = GetShort_String(data);
00506     setnum = data[2];
00507     checksum = GetInt_String(data+3);
00508     face = (char*)data+7;
00509     data[len] = '\0';
00510 
00511     finish_face_cmd(pnum, checksum, 1, face,setnum);
00512 }
00513 
00514 void Image2Cmd(uint8 *data,  int len)
00515 {
00516     int pnum,plen;
00517     uint8 setnum;
00518 
00519     pnum = GetInt_String(data);
00520     setnum = data[4];
00521     plen = GetInt_String(data+5);
00522     if (len<9 || (len-9)!=plen) {
00523         LOG(LOG_WARNING,"common::Image2Cmd","Lengths don't compare (%d,%d)",
00524                 (len-9),plen);
00525         return;
00526     }
00527     display_newpng(pnum,data+9,plen, setnum);
00528 }
00529 
00530 /*
00531  * This function is called when the server has sent us the actual
00532  * png data for an image.  If caching, we need to write this
00533  * data to disk.
00534  */
00535 void display_newpng(int face, uint8 *buf, int buflen, int setnum)
00536 {
00537     char    filename[MAX_BUF], basename[MAX_BUF];
00538     uint8   *pngtmp;
00539     FILE *tmpfile;
00540     uint32 width, height, csum, i;
00541     Cache_Entry *ce=NULL;
00542 
00543     if (use_config[CONFIG_CACHE]) {
00544         if (facetoname[face]==NULL) {
00545             LOG(LOG_WARNING,"common::display_newpng","Caching images, but name for %ld not set", face);
00546         }
00547         /* Make necessary leading directories */
00548         snprintf(filename, sizeof(filename), "%s/.crossfire/image-cache",getenv("HOME"));
00549         if (access(filename, R_OK | W_OK | X_OK)== -1)
00550 #ifdef WIN32
00551             mkdir(filename);
00552 #else
00553             mkdir(filename, 0755);
00554 #endif
00555 
00556         snprintf(filename, sizeof(filename), "%s/.crossfire/image-cache/%c%c", getenv("HOME"),
00557                  facetoname[face][0], facetoname[face][1]);
00558         if (access(filename, R_OK | W_OK | X_OK)== -1)
00559 #ifdef WIN32
00560             mkdir(filename);
00561 #else
00562             mkdir(filename,0755);
00563 #endif
00564 
00565         /* If setnum is valid, and we have faceset information for it,
00566          * put that prefix in.  This will make it easier later on to
00567          * allow the client to switch image sets on the fly, as it can
00568          * determine what set the image belongs to.
00569          * We also append the number to it - there could be several versions
00570          * of 'face.base.111.x' if different servers have different image
00571          * values.
00572          */
00573         if (setnum >=0 && setnum < MAX_FACE_SETS && face_info.facesets[setnum].prefix)
00574             snprintf(basename, sizeof(basename),"%s.%s", facetoname[face], face_info.facesets[setnum].prefix);
00575         else
00576             strcpy(basename, facetoname[face]);
00577 
00578         /* Decrease it by one since it will immediately get increased
00579          * in the loop below.
00580          */
00581         setnum--;
00582         do {
00583             setnum++;
00584             snprintf(filename, sizeof(filename), "%s/.crossfire/image-cache/%c%c/%s.%d",
00585                     getenv("HOME"), facetoname[face][0],
00586                     facetoname[face][1], basename, setnum);
00587         } while (access(filename, F_OK)==-0);
00588 
00589 #ifdef WIN32
00590         if ((tmpfile = fopen(filename,"wb"))==NULL) {
00591 #else
00592         if ((tmpfile = fopen(filename,"w"))==NULL) {
00593 #endif
00594             LOG(LOG_WARNING,"common::display_newpng","Can not open %s for writing", filename);
00595         }
00596         else {
00597             /* found a file we can write to */
00598 
00599             fwrite(buf, buflen, 1, tmpfile);
00600             fclose(tmpfile);
00601             csum=0;
00602             for (i=0; (int)i<buflen; i++) {
00603                 ROTATE_RIGHT(csum);
00604                 csum += buf[i];
00605                 csum &= 0xffffffff;
00606             }
00607             snprintf(filename, sizeof(filename), "%c%c/%s.%d", facetoname[face][0], facetoname[face][1],
00608                     basename, setnum);
00609             ce = image_add_hash(facetoname[face], filename,  csum, 0);
00610 
00611             /* It may very well be more efficient to try to store these up
00612              * and then write them as a bunch instead of constantly opening the
00613              * file for appending.  OTOH, hopefully people will be using the
00614              * built image archives, so only a few faces actually need to get
00615              * downloaded.
00616              */
00617             snprintf(filename, sizeof(filename), "%s/.crossfire/image-cache/bmaps.client", getenv("HOME"));
00618             if ((tmpfile=fopen(filename, "a"))==NULL) {
00619                 LOG(LOG_WARNING,"common::display_newpng","Can not open %s for appending", filename);
00620             }
00621             else {
00622                 fprintf(tmpfile, "%s %u %c%c/%s.%d\n",
00623                         facetoname[face], csum, facetoname[face][0],
00624                         facetoname[face][1], basename, setnum);
00625                 fclose(tmpfile);
00626             }
00627         }
00628     }
00629 
00630     pngtmp = png_to_data(buf, buflen, &width, &height);
00631     if(create_and_rescale_image_from_data(ce, face, pngtmp, width, height)) {
00632         LOG(LOG_WARNING, "common::display_newpng", "create_and_rescale_image_from_data failed for face %ld", face);
00633     }
00634 
00635     if (use_config[CONFIG_CACHE]) {
00636         free(facetoname[face]);
00637         facetoname[face]=NULL;
00638     }
00639     free(pngtmp);
00640 }
00641 
00642 
00643 /* get_image_info takes the data from a replyinfo image_info
00644  * and breaks it down.  The info contained is the checkums,
00645  * number of images, and faceset information.  It stores this
00646  * data into the face_info structure.
00647  * Since we know data is null terminated, we can use the strchr
00648  * operations with safety.
00649  * In each block, we find the newline - if we find one, we presume
00650  * the data is good, and update the face_info accordingly.
00651  * if we don't find a newline, we return.
00652  */
00653 void get_image_info(uint8 *data, int len)
00654 {
00655     char *cp, *lp, *cps[7], buf[MAX_BUF];
00656     int onset=0, badline=0,i;
00657 
00658     replyinfo_status |= RI_IMAGE_INFO;
00659 
00660     lp = (char*)data;
00661     cp = strchr(lp, '\n');
00662     if (!cp || (cp - lp) > len) return;
00663     face_info.num_images = atoi(lp);
00664 
00665     lp = cp+1;
00666     cp = strchr(lp, '\n');
00667     if (!cp || (cp - lp) > len) return;
00668     face_info.bmaps_checksum = strtoul(lp, NULL, 10);   /* need unsigned, so no atoi */
00669 
00670     lp = cp+1;
00671     cp = strchr(lp, '\n');
00672     while (cp && (cp - lp) <= len) {
00673         *cp++ = '\0';
00674 
00675         /* The code below is pretty much the same as the code from the server
00676          * which loads the original faceset file.
00677          */
00678         if (!(cps[0] = strtok(lp, ":"))) badline=1;
00679         for (i=1; i<7; i++) {
00680             if (!(cps[i] = strtok(NULL, ":"))) badline=1;
00681         }
00682         if (badline) {
00683             LOG(LOG_WARNING,"common::get_image_info","bad data, ignoring line:/%s/", lp);
00684         } else {
00685             onset = atoi(cps[0]);
00686             if (onset >=MAX_FACE_SETS) {
00687                 LOG(LOG_WARNING,"common::get_image_info","setnum is too high: %d > %d",
00688                     onset, MAX_FACE_SETS);
00689             }
00690             face_info.facesets[onset].prefix = strdup_local(cps[1]);
00691             face_info.facesets[onset].fullname = strdup_local(cps[2]);
00692             face_info.facesets[onset].fallback = atoi(cps[3]);
00693             face_info.facesets[onset].size = strdup_local(cps[4]);
00694             face_info.facesets[onset].extension = strdup_local(cps[5]);
00695             face_info.facesets[onset].comment = strdup_local(cps[6]);
00696         }
00697         lp = cp;
00698         cp = strchr(lp, '\n');
00699     }
00700     face_info.have_faceset_info = 1;
00701     /* if the user has requested a specific face set and that set
00702      * is not numeric, try to find a matching set and send the
00703      * relevent setup command.
00704      */
00705     if (face_info.want_faceset && atoi(face_info.want_faceset)==0) {
00706         for (onset=0; onset<MAX_FACE_SETS; onset++) {
00707             if (face_info.facesets[onset].prefix &&
00708                 !strcasecmp(face_info.facesets[onset].prefix, face_info.want_faceset)) break;
00709             if (face_info.facesets[onset].fullname &&
00710                 !strcasecmp(face_info.facesets[onset].fullname, face_info.want_faceset)) break;
00711         }
00712         if (onset < MAX_FACE_SETS) { /* We found a match */
00713             face_info.faceset = onset;
00714             cs_print_string(csocket.fd, "setup faceset %d", onset);
00715         } else {
00716             snprintf(buf, sizeof(buf), "Unable to find match for faceset %s on the server", face_info.want_faceset);
00717             draw_info(buf, NDI_RED);
00718         }
00719     }
00720 
00721 }
00722 
00723 /* This gets a block of checksums from the server.  This lets it
00724  * prebuild the images or what not.  It would probably be
00725  * nice to add a gui callback someplace that gives a little status
00726  * display (18% done or whatever) - that probably needs to be done
00727  * further up.
00728  *
00729  * The start and stop values are not meaningful - they are here
00730  * because the semantics of the requestinfo/replyinfo is that
00731  * replyinfo includes the same request data as the requestinfo
00732  * (thus, if the request failed for some reason, the client would
00733  * know which one failed and then try again).  Currently, we
00734  * don't have any logic in the function below to deal with failures.
00735  */
00736 
00737 void get_image_sums(char *data, int len)
00738 {
00739     int start, stop, imagenum, slen, faceset;
00740     uint32  checksum;
00741     char *cp, *lp;
00742 
00743     cp = strchr((char*)data, ' ');
00744     if (!cp || (cp - data) > len) return;
00745     start = atoi((char*)data);
00746 
00747     while (isspace(*cp)) cp++;
00748     lp = cp;
00749     cp = strchr(lp, ' ');
00750     if (!cp || (cp - data) > len) return;
00751     stop = atoi(lp);
00752 
00753     replyinfo_last_face = stop;
00754 
00755     /* Can't use isspace here, because it matches with tab, ascii code
00756      * 9 - this results in advancing too many spaces because
00757      * starting at image 2304, the MSB of the image number will be
00758      * 9.  Using a check against space will work until we get up to
00759      * 8192 images.
00760      */
00761     while (*cp==' ') cp++;
00762     while ((cp - data) < len) {
00763         imagenum = GetShort_String((uint8*)cp); cp += 2;
00764         checksum = GetInt_String((uint8*)cp); cp += 4;
00765         faceset = *cp; cp++;
00766         slen = *cp; cp++;
00767         /* Note that as is, this can break horribly if the client is missing a large number
00768          * of images - that is because it will request a whole bunch which will overflow
00769          * the servers output buffer, causing it to close the connection.
00770          * What probably should be done is for the client to just request this checksum
00771          * information in small batches so that even if the client has no local
00772          * images, requesting the entire batch won't overflow the sockets buffer - this
00773          * probably amounts to about 100 images at a time
00774          */
00775         finish_face_cmd(imagenum, checksum, 1, (char*)cp, faceset);
00776         if (imagenum > stop)
00777             LOG(LOG_WARNING,"common::get_image_sums","Received an image beyond our range? %d > %d", imagenum, stop);
00778         cp += slen;
00779     }
00780 }