Crossfire Server, Branch 1.12  R12190
image.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_image_c =
00003  *   "$Id: image.c 11578 2009-02-23 22:02:27Z lalo $";
00004  */
00005 
00006 /*
00007     CrossFire, A Multiplayer game for X-windows
00008 
00009     Copyright (C) 2002 Mark Wedel & Crossfire Development Team
00010     Copyright (C) 1992 Frank Tore Johansen
00011 
00012     This program is free software; you can redistribute it and/or modify
00013     it under the terms of the GNU General Public License as published by
00014     the Free Software Foundation; either version 2 of the License, or
00015     (at your option) any later version.
00016 
00017     This program is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020     GNU General Public License for more details.
00021 
00022     You should have received a copy of the GNU General Public License
00023     along with this program; if not, write to the Free Software
00024     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00025 
00026     The maintainer of this code can be reached at crossfire-devel@real-time.com
00027 */
00028 
00034 #include <global.h>
00035 #include <stdio.h>
00036 #include "image.h"
00037 
00038 New_Face *new_faces;
00039 
00052 struct bmappair {
00053     char *name;
00054     unsigned int number;
00055 };
00060 static struct bmappair *xbm = NULL;
00061 
00066 New_Face *blank_face, *empty_face, *smooth_face;
00067 
00068 
00070 static int nroffiles = 0;
00071 
00076 int nrofpixmaps = 0;
00077 
00078 face_sets facesets[MAX_FACE_SETS];    
00087 static const char *const colorname[] = {
00088     "black",        /* 0  */
00089     "white",        /* 1  */
00090     "blue",         /* 2  */
00091     "red",          /* 3  */
00092     "orange",       /* 4  */
00093     "light_blue",   /* 5  */
00094     "dark_orange",  /* 6  */
00095     "green",        /* 7  */
00096     "light_green",  /* 8  */
00097     "grey",         /* 9  */
00098     "brown",        /* 10 */
00099     "yellow",       /* 11 */
00100     "khaki"         /* 12 */
00101 };
00102 
00106 static int compar(const struct bmappair *a, const struct bmappair *b) {
00107     return strcmp(a->name, b->name);
00108 }
00109 
00123 static uint8 find_color(const char *name) {
00124     uint8 i;
00125 
00126     for (i = 0; i < sizeof(colorname)/sizeof(*colorname); i++)
00127         if (!strcmp(name, colorname[i]))
00128             return i;
00129 
00130     LOG(llevError, "Unknown color: %s\n", name);
00131     return 0;
00132 }
00133 
00141 static void read_face_data(void) {
00142     char buf[MAX_BUF], *cp;
00143     New_Face *on_face = NULL;
00144     FILE *fp;
00145 
00146     snprintf(buf, sizeof(buf), "%s/faces", settings.datadir);
00147     LOG(llevDebug, "Reading faces from %s...\n", buf);
00148     if ((fp = fopen(buf, "r")) == NULL) {
00149         LOG(llevError, "Cannot open faces file: %s\n", strerror_local(errno, buf, sizeof(buf)));
00150         exit(-1);
00151     }
00152 
00153     while (fgets(buf, MAX_BUF, fp) != NULL) {
00154         if (*buf == '#')
00155             continue;
00156         if (!strncmp(buf, "end", 3)) {
00157             on_face = NULL;
00158         } else if (!strncmp(buf, "face", 4)) {
00159             unsigned tmp;
00160 
00161             cp = buf+5;
00162             cp[strlen(cp)-1] = '\0'; /* remove newline */
00163 
00164             if ((tmp = find_face(cp, (unsigned)-1)) == (unsigned)-1) {
00165                 LOG(llevError, "Could not find face %s\n", cp);
00166                 continue;
00167             }
00168             on_face = &new_faces[tmp];
00169             on_face->visibility = 0;
00170         } else if (on_face == NULL) {
00171             LOG(llevError, "Got line with no face set: %s\n", buf);
00172         } else if (!strncmp(buf, "visibility", 10)) {
00173             on_face->visibility = atoi(buf+11);
00174         } else if (!strncmp(buf, "magicmap", 8)) {
00175             cp = buf+9;
00176             cp[strlen(cp)-1] = '\0';
00177             on_face->magicmap = find_color(cp);
00178         } else if (!strncmp(buf, "is_floor", 8)) {
00179             int value = atoi(buf+9);
00180             if (value)
00181                 on_face->magicmap |= FACE_FLOOR;
00182         } else
00183             LOG(llevDebug, "Got unknown line in faces file: %s\n", buf);
00184     }
00185     LOG(llevDebug, "done\n");
00186     fclose(fp);
00187 }
00188 
00198 void read_bmap_names(void) {
00199     char buf[MAX_BUF], *p, *q;
00200     FILE *fp;
00201     int value, nrofbmaps = 0, i;
00202     size_t l;
00203 
00204     bmaps_checksum = 0;
00205     snprintf(buf, sizeof(buf), "%s/bmaps", settings.datadir);
00206     LOG(llevDebug, "Reading bmaps from %s...\n", buf);
00207     if ((fp = fopen(buf, "r")) == NULL) {
00208         LOG(llevError, "Cannot open bmaps file: %s\n", strerror_local(errno, buf, sizeof(buf)));
00209         exit(-1);
00210     }
00211 
00212     /* First count how many bitmaps we have, so we can allocate correctly */
00213     while (fgets(buf, MAX_BUF, fp) != NULL)
00214         if (buf[0] != '#' && buf[0] != '\n')
00215             nrofbmaps++;
00216     rewind(fp);
00217 
00218     xbm = (struct bmappair *)malloc(sizeof(struct bmappair)*nrofbmaps);
00219     if (xbm == NULL) {
00220         LOG(llevError, "read_bmap_names: xbm memory allocation failure.\n");
00221         abort();
00222     }
00223     memset(xbm, 0, sizeof(struct bmappair)*nrofbmaps);
00224 
00225     nroffiles = 0;
00226     while (nroffiles < nrofbmaps && fgets(buf, MAX_BUF, fp) != NULL) {
00227         if (*buf == '#')
00228             continue;
00229 
00230         p = (*buf == '\\') ? (buf+1) : buf;
00231         if (!(p = strtok(p, " \t")) || !(q = strtok(NULL, " \t\n"))) {
00232             LOG(llevDebug, "Warning, syntax error: %s\n", buf);
00233             continue;
00234         }
00235         value = atoi(p);
00236         xbm[nroffiles].name = strdup_local(q);
00237 
00238         /* We need to calculate the checksum of the bmaps file
00239          * name->number mapping to send to the client.  This does not
00240          * need to match what sum or other utility may come up with -
00241          * as long as we get the same results on the same real file
00242          * data, it does the job as it lets the client know if
00243          * the file has the same data or not.
00244          */
00245         ROTATE_RIGHT(bmaps_checksum);
00246         bmaps_checksum += value&0xff;
00247         bmaps_checksum &= 0xffffffff;
00248 
00249         ROTATE_RIGHT(bmaps_checksum);
00250         bmaps_checksum += (value>>8)&0xff;
00251         bmaps_checksum &= 0xffffffff;
00252         for (l = 0; l < strlen(q); l++) {
00253             ROTATE_RIGHT(bmaps_checksum);
00254             bmaps_checksum += q[l];
00255             bmaps_checksum &= 0xffffffff;
00256         }
00257 
00258         xbm[nroffiles].number = value;
00259         nroffiles++;
00260         if (value >= nrofpixmaps)
00261             nrofpixmaps = value+1;
00262     }
00263     fclose(fp);
00264 
00265     LOG(llevDebug, "done (got %d/%d/%d)\n", nrofpixmaps, nrofbmaps, nroffiles);
00266 
00267     new_faces = (New_Face *)malloc(sizeof(New_Face)*nrofpixmaps);
00268     if (new_faces == NULL) {
00269         LOG(llevError, "read_bmap_names: new_faces memory allocation failure.\n");
00270         abort();
00271     }
00272 
00273     for (i = 0; i < nrofpixmaps; i++) {
00274         new_faces[i].name = "";
00275         new_faces[i].number = i;
00276         new_faces[i].visibility = 0;
00277         new_faces[i].magicmap = 255;
00278         new_faces[i].smoothface = (uint16)-1;
00279     }
00280 
00281     for (i = 0; i < nroffiles; i++) {
00282         new_faces[xbm[i].number].name = xbm[i].name;
00283     }
00284 
00285     qsort(xbm, nroffiles, sizeof(struct bmappair), (int (*)(const void *, const void *))compar);
00286 
00287     read_face_data();
00288 
00289     for (i = 0; i < nrofpixmaps; i++) {
00290         if (new_faces[i].magicmap == 255) {
00291             new_faces[i].magicmap = 0;
00292         }
00293     }
00294     /* Actually forcefully setting the colors here probably should not
00295      * be done - it could easily create confusion.
00296      */
00297     blank_face = &new_faces[find_face(BLANK_FACE_NAME, 0)];
00298     blank_face->magicmap = find_color("khaki")|FACE_FLOOR;
00299 
00300     empty_face = &new_faces[find_face(EMPTY_FACE_NAME, 0)];
00301 
00302     smooth_face = &new_faces[find_face(SMOOTH_FACE_NAME, 0)];
00303 }
00304 
00324 unsigned find_face(const char *name, unsigned error) {
00325     struct bmappair *bp, tmp;
00326     char *p;
00327 
00328     if ((p = strchr(name, '\n')))
00329         *p = '\0';
00330 
00331     tmp.name = (char *)name;
00332     bp = (struct bmappair *)bsearch(&tmp, xbm, nroffiles, sizeof(struct bmappair), (int (*)(const void *, const void *))compar);
00333 
00334     return bp ? bp->number : error;
00335 }
00336 
00348 int read_smooth(void) {
00349     char buf[MAX_BUF], *p, *q;
00350     FILE *fp;
00351     int regular, smoothed, nrofsmooth = 0;
00352 
00353     snprintf(buf, sizeof(buf), "%s/smooth", settings.datadir);
00354     LOG(llevDebug, "Reading smooth from %s...\n", buf);
00355     if ((fp = fopen(buf, "r")) == NULL) {
00356         LOG(llevError, "Cannot open smooth file %s: %s\n", strerror_local(errno, buf, sizeof(buf)));
00357         exit(-1);
00358     }
00359 
00360     while (fgets(buf, MAX_BUF, fp) != NULL) {
00361         if (*buf == '#')
00362             continue;
00363 
00364         p = strchr(buf, ' ');
00365         if (!p)
00366             continue;
00367 
00368         *p = '\0';
00369         q = buf;
00370         regular = find_face(q, (unsigned)-1);
00371         if (regular == (unsigned)-1) {
00372             LOG(llevError, "invalid regular face: %s\n", q);
00373             continue;
00374         }
00375         q = p+1;
00376         smoothed = find_face(q, (unsigned)-1);
00377         if (smoothed == (unsigned)-1) {
00378             LOG(llevError, "invalid smoothed face: %s\n", q);
00379             continue;
00380         }
00381 
00382         new_faces[regular].smoothface = smoothed;
00383 
00384         nrofsmooth++;
00385     }
00386     fclose(fp);
00387 
00388     LOG(llevDebug, "done (got %d smooth entries)\n", nrofsmooth);
00389     return nrofsmooth;
00390 }
00391 
00401 int find_smooth(uint16 face, uint16 *smoothed) {
00402     (*smoothed) = 0;
00403 
00404     if (face < nrofpixmaps) {
00405         if (new_faces[face].smoothface == ((uint16)-1))
00406             return 0;
00407 
00408         (*smoothed) = new_faces[face].smoothface;
00409         return 1;
00410     }
00411 
00412     return 0;
00413 }
00414 
00418 void free_all_images(void) {
00419     int i;
00420 
00421     for (i = 0; i < nroffiles; i++)
00422         free(xbm[i].name);
00423     free(xbm);
00424     free(new_faces);
00425 }
00426 
00435 static void check_faceset_fallback(int faceset, int togo) {
00436     int fallback = facesets[faceset].fallback;
00437 
00438     /* proper case - falls back to base set */
00439     if (fallback == 0)
00440         return;
00441 
00442     if (!facesets[fallback].prefix) {
00443         LOG(llevError, "Face set %d falls to non set faceset %d\n", faceset, fallback);
00444         abort();
00445     }
00446     togo--;
00447     if (togo == 0) {
00448         LOG(llevError, "Infinite loop found in facesets. aborting.\n");
00449         abort();
00450     }
00451     check_faceset_fallback(fallback, togo);
00452 }
00453 
00471 void read_client_images(void) {
00472     char filename[400];
00473     char buf[HUGE_BUF];
00474     char *cp, *cps[7];
00475     FILE *infile;
00476     int num, len, compressed, fileno, i, badline;
00477 
00478     memset(facesets, 0, sizeof(facesets));
00479     snprintf(filename, sizeof(filename), "%s/image_info", settings.datadir);
00480     if ((infile = open_and_uncompress(filename, 0, &compressed)) == NULL) {
00481         LOG(llevError, "Unable to open %s\n", filename);
00482         abort();
00483     }
00484     while (fgets(buf, HUGE_BUF-1, infile) != NULL) {
00485         badline = 0;
00486 
00487         if (buf[0] == '#')
00488             continue;
00489         if (!(cps[0] = strtok(buf, ":")))
00490             badline = 1;
00491         for (i = 1; i < 7; i++) {
00492             if (!(cps[i] = strtok(NULL, ":")))
00493                 badline = 1;
00494         }
00495         if (badline) {
00496             LOG(llevError, "Bad line in image_info file, ignoring line:\n  %s", buf);
00497         } else {
00498             len = atoi(cps[0]);
00499             if (len >= MAX_FACE_SETS) {
00500                 LOG(llevError, "To high a setnum in image_info file: %d > %d\n", len, MAX_FACE_SETS);
00501                 abort();
00502             }
00503             facesets[len].prefix = strdup_local(cps[1]);
00504             facesets[len].fullname = strdup_local(cps[2]);
00505             facesets[len].fallback = atoi(cps[3]);
00506             facesets[len].size = strdup_local(cps[4]);
00507             facesets[len].extension = strdup_local(cps[5]);
00508             facesets[len].comment = strdup_local(cps[6]);
00509         }
00510     }
00511     close_and_delete(infile, compressed);
00512     for (i = 0; i < MAX_FACE_SETS; i++) {
00513         if (facesets[i].prefix)
00514             check_faceset_fallback(i, MAX_FACE_SETS);
00515     }
00516     /* Loaded the faceset information - now need to load up the
00517      * actual faces.
00518      */
00519 
00520     for (fileno = 0; fileno < MAX_FACE_SETS; fileno++) {
00521         /* if prefix is not set, this is not used */
00522         if (!facesets[fileno].prefix)
00523             continue;
00524         facesets[fileno].faces = calloc(nrofpixmaps, sizeof(face_info));
00525 
00526         snprintf(filename, sizeof(filename), "%s/crossfire.%d", settings.datadir, fileno);
00527         LOG(llevDebug, "Loading image file %s\n", filename);
00528 
00529         if ((infile = open_and_uncompress(filename, 0, &compressed)) == NULL) {
00530             LOG(llevError, "Unable to open %s\n", filename);
00531             abort();
00532         }
00533         while (fgets(buf, HUGE_BUF-1, infile) != NULL) {
00534             if (strncmp(buf, "IMAGE ", 6) != 0) {
00535                 LOG(llevError, "read_client_images:Bad image line - not IMAGE, instead\n%s", buf);
00536                 abort();
00537             }
00538             num = atoi(buf+6);
00539             if (num < 0 || num >= nrofpixmaps) {
00540                 LOG(llevError, "read_client_images: Image num %d not in 0..%d\n%s", num, nrofpixmaps, buf);
00541                 abort();
00542             }
00543             /* Skip accross the number data */
00544             for (cp = buf+6; *cp != ' '; cp++)
00545                 ;
00546             len = atoi(cp);
00547             if (len == 0 || len > MAX_IMAGE_SIZE) {
00548                 LOG(llevError, "read_client_images: length not valid: %d > %d \n%s", len, MAX_IMAGE_SIZE, buf);
00549                 abort();
00550             }
00551             /* We don't actualy care about the name if the image that
00552              * is embedded in the image file, so just ignore it.
00553              */
00554             facesets[fileno].faces[num].datalen = len;
00555             facesets[fileno].faces[num].data = malloc(len);
00556             if ((i = fread(facesets[fileno].faces[num].data, len, 1, infile)) != 1) {
00557                 LOG(llevError, "read_client_images: Did not read desired amount of data, wanted %d, got %d\n%s", len, i, buf);
00558                 abort();
00559             }
00560             facesets[fileno].faces[num].checksum = 0;
00561             for (i = 0; i < len; i++) {
00562                 ROTATE_RIGHT(facesets[fileno].faces[num].checksum);
00563                 facesets[fileno].faces[num].checksum += facesets[fileno].faces[num].data[i];
00564                 facesets[fileno].faces[num].checksum &= 0xffffffff;
00565             }
00566         }
00567         close_and_delete(infile, compressed);
00568     } /* For fileno < MAX_FACE_SETS */
00569 }
00570 
00575 int is_valid_faceset(int fsn) {
00576     if (fsn >= 0 && fsn < MAX_FACE_SETS && facesets[fsn].prefix)
00577         return TRUE;
00578     return FALSE;
00579 }
00580 
00584 void free_socket_images(void) {
00585     int num, q;
00586 
00587     for (num = 0; num < MAX_FACE_SETS; num++) {
00588         if (facesets[num].prefix) {
00589             for (q = 0; q < nrofpixmaps; q++)
00590                 if (facesets[num].faces[q].data)
00591                     free(facesets[num].faces[q].data);
00592             free(facesets[num].prefix);
00593             free(facesets[num].fullname);
00594             free(facesets[num].size);
00595             free(facesets[num].extension);
00596             free(facesets[num].comment);
00597             free(facesets[num].faces);
00598         }
00599     }
00600 }
00601 
00614 int get_face_fallback(int faceset, int imageno) {
00615     /* faceset 0 is supposed to have every image, so just return.  Doing
00616      * so also prevents infinite loops in the case if it not having
00617      * the face, but in that case, we are likely to crash when we try
00618      * to access the data, but that is probably preferable to an infinite
00619      * loop.
00620      */
00621     if (faceset == 0)
00622         return 0;
00623 
00624     if (!facesets[faceset].prefix) {
00625         LOG(llevError, "get_face_fallback called with unused set (%d)?\n", faceset);
00626         return 0;   /* use default set */
00627     }
00628     if (facesets[faceset].faces[imageno].data)
00629         return faceset;
00630     return get_face_fallback(facesets[faceset].fallback, imageno);
00631 }