Crossfire Client, Branch
R11627
|
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 }