ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/socket/image.C
Revision: 1.3
Committed: Sun Sep 10 13:43:33 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.2: +327 -268 lines
Log Message:
indent

File Contents

# User Rev Content
1 root 1.3
2 elmex 1.1 /*
3     * static char *rcsid_init_c =
4 root 1.3 * "$Id: image.C,v 1.2 2006-08-29 08:01:38 root Exp $";
5 elmex 1.1 */
6    
7     /*
8     CrossFire, A Multiplayer game for X-windows
9    
10     Copyright (C) 2001 Mark Wedel
11     Copyright (C) 1992 Frank Tore Johansen
12    
13     This program is free software; you can redistribute it and/or modify
14     it under the terms of the GNU General Public License as published by
15     the Free Software Foundation; either version 2 of the License, or
16     (at your option) any later version.
17    
18     This program is distributed in the hope that it will be useful,
19     but WITHOUT ANY WARRANTY; without even the implied warranty of
20     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21     GNU General Public License for more details.
22    
23     You should have received a copy of the GNU General Public License
24     along with this program; if not, write to the Free Software
25     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26    
27     The author can be reached via e-mail to crossfire-devel@real-time.com
28     */
29    
30     /** \file
31     * Image related communication
32     *
33     * \date 2003-12-02
34     *
35     * This file deals with the image related communication to the
36     * client. I've located all the functions in this file - this
37     * localizes it more, and means that we don't need to declare
38     * things like all the structures as globals.
39     */
40    
41     #include <global.h>
42     #include <sproto.h>
43    
44     #include <newclient.h>
45     #include <newserver.h>
46     #include <loader.h>
47    
48     #define MAX_FACE_SETS 20 /**< Maximum number of image sets the program will handle */
49    
50     /** Information about one image */
51 root 1.3 typedef struct FaceInfo
52     {
53    
54     uint8 *data; /**< image data */
55    
56     uint16 datalen; /**< length of the xpm data */
57    
58     uint32 checksum; /**< Checksum of face data */
59 elmex 1.1 } FaceInfo;
60    
61     /** Information about one face set */
62 root 1.3 typedef struct
63     {
64    
65     char *prefix; /**< */
66     char *fullname;
67    
68     uint8 fallback; /**< faceset to use when an image is not found in this faceset */
69     char *size;
70     char *extension;
71     char *comment;
72    
73     FaceInfo *faces; /**< images in this faceset */
74 elmex 1.1 } FaceSets;
75    
76     static FaceSets facesets[MAX_FACE_SETS]; /**< All facesets */
77    
78     /**
79     * Checks specified faceset is valid
80     * \param fsn faceset number
81     */
82 root 1.3 int
83     is_valid_faceset (int fsn)
84 elmex 1.1 {
85 root 1.3 if (fsn >= 0 && fsn < MAX_FACE_SETS && facesets[fsn].prefix)
86     return TRUE;
87     return FALSE;
88 elmex 1.1 }
89    
90     /**
91     * Frees all faceset information
92     */
93 root 1.3 void
94     free_socket_images (void)
95 elmex 1.1 {
96 root 1.3 int num, q;
97 elmex 1.1
98 root 1.3 for (num = 0; num < MAX_FACE_SETS; num++)
99     {
100     if (facesets[num].prefix)
101     {
102     for (q = 0; q < nrofpixmaps; q++)
103     if (facesets[num].faces[q].data)
104     free (facesets[num].faces[q].data);
105     free (facesets[num].prefix);
106     free (facesets[num].fullname);
107     free (facesets[num].size);
108     free (facesets[num].extension);
109     free (facesets[num].comment);
110     free (facesets[num].faces);
111 root 1.2 }
112 elmex 1.1 }
113     }
114    
115     /**
116     * This returns the set we will actually use when sending
117     * a face. This is used because the image files may be sparse.
118     * This function is recursive. imageno is the face number we are
119     * trying to send
120     *
121     * If face is not found in specified faceset, tries with 'fallback' faceset.
122     *
123     * \param faceset faceset to check
124     * \param imageno image number
125     *
126     */
127 root 1.3 static int
128     get_face_fallback (int faceset, int imageno)
129 elmex 1.1 {
130 root 1.3 /* faceset 0 is supposed to have every image, so just return. Doing
131     * so also prevents infinite loops in the case if it not having
132     * the face, but in that case, we are likely to crash when we try
133     * to access the data, but that is probably preferable to an infinite
134     * loop.
135     */
136     if (faceset == 0)
137     return 0;
138    
139     if (!facesets[faceset].prefix)
140     {
141     LOG (llevError, "get_face_fallback called with unused set (%d)?\n", faceset);
142     return 0; /* use default set */
143     }
144     if (facesets[faceset].faces[imageno].data)
145     return faceset;
146     return get_face_fallback (facesets[faceset].fallback, imageno);
147 elmex 1.1 }
148    
149     /**
150     * Checks fallback are correctly defined.
151     * This is a simple recursive function that makes sure the fallbacks
152     * are all proper (eg, the fall back to defined sets, and also
153     * eventually fall back to 0). At the top level, togo is set to MAX_FACE_SETS,
154     * if togo gets to zero, it means we have a loop.
155     * This is only run when we first load the facesets.
156     */
157 root 1.3 static void
158     check_faceset_fallback (int faceset, int togo)
159 elmex 1.1 {
160 root 1.3 int fallback = facesets[faceset].fallback;
161 elmex 1.1
162 root 1.3 /* proper case - falls back to base set */
163     if (fallback == 0)
164     return;
165    
166     if (!facesets[fallback].prefix)
167     {
168     LOG (llevError, "Face set %d falls to non set faceset %d\n", faceset, fallback);
169     abort ();
170     }
171     togo--;
172     if (togo == 0)
173     {
174     LOG (llevError, "Infinite loop found in facesets. aborting.\n");
175     abort ();
176 elmex 1.1 }
177 root 1.3 check_faceset_fallback (fallback, togo);
178 elmex 1.1 }
179    
180     #define MAX_IMAGE_SIZE 10000
181    
182     /**
183     * Loads all the image types into memory.
184     *
185     * This way, we can easily send them to the client. We should really do something
186     * better than abort on any errors - on the other hand, these are all fatal
187     * to the server (can't work around them), but the abort just seems a bit
188     * messy (exit would probably be better.)
189     *
190     * Couple of notes: We assume that the faces are in a continous block.
191     * This works fine for now, but this could perhaps change in the future
192     *
193     * Function largely rewritten May 2000 to be more general purpose.
194     * The server itself does not care what the image data is - to the server,
195     * it is just data it needs to allocate. As such, the code is written
196     * to do such.
197     */
198    
199 root 1.3 void
200     read_client_images (void)
201 elmex 1.1 {
202 root 1.3 char filename[400];
203     char buf[HUGE_BUF];
204     char *cp, *cps[7];
205     FILE *infile;
206     int num, len, compressed, fileno, i, badline;
207    
208     memset (facesets, 0, sizeof (facesets));
209     sprintf (filename, "%s/image_info", settings.datadir);
210     if ((infile = open_and_uncompress (filename, 0, &compressed)) == NULL)
211     {
212     LOG (llevError, "Unable to open %s\n", filename);
213     abort ();
214     }
215     while (fgets (buf, HUGE_BUF - 1, infile) != NULL)
216     {
217     badline = 0;
218    
219     if (buf[0] == '#')
220     continue;
221     if (!(cps[0] = strtok (buf, ":")))
222     badline = 1;
223     for (i = 1; i < 7; i++)
224     {
225     if (!(cps[i] = strtok (NULL, ":")))
226     badline = 1;
227     }
228     if (badline)
229     {
230     LOG (llevError, "Bad line in image_info file, ignoring line:\n %s", buf);
231 root 1.2 }
232 root 1.3 else
233     {
234     len = atoi (cps[0]);
235     if (len >= MAX_FACE_SETS)
236     {
237     LOG (llevError, "To high a setnum in image_info file: %d > %d\n", len, MAX_FACE_SETS);
238     abort ();
239 root 1.2 }
240 root 1.3 facesets[len].prefix = strdup_local (cps[1]);
241     facesets[len].fullname = strdup_local (cps[2]);
242     facesets[len].fallback = atoi (cps[3]);
243     facesets[len].size = strdup_local (cps[4]);
244     facesets[len].extension = strdup_local (cps[5]);
245     facesets[len].comment = strdup_local (cps[6]);
246 root 1.2 }
247 elmex 1.1 }
248 root 1.3 close_and_delete (infile, compressed);
249     for (i = 0; i < MAX_FACE_SETS; i++)
250     {
251     if (facesets[i].prefix)
252     check_faceset_fallback (i, MAX_FACE_SETS);
253     }
254     /* Loaded the faceset information - now need to load up the
255     * actual faces.
256     */
257    
258     for (fileno = 0; fileno < MAX_FACE_SETS; fileno++)
259     {
260     /* if prefix is not set, this is not used */
261     if (!facesets[fileno].prefix)
262     continue;
263     facesets[fileno].faces = (FaceInfo *) calloc (nrofpixmaps, sizeof (FaceInfo));
264    
265     sprintf (filename, "%s/crossfire.%d", settings.datadir, fileno);
266     LOG (llevDebug, "Loading image file %s\n", filename);
267    
268     if ((infile = open_and_uncompress (filename, 0, &compressed)) == NULL)
269     {
270     LOG (llevError, "Unable to open %s\n", filename);
271     abort ();
272 root 1.2 }
273 root 1.3 while (fgets (buf, HUGE_BUF - 1, infile) != NULL)
274     {
275     if (strncmp (buf, "IMAGE ", 6) != 0)
276     {
277     LOG (llevError, "read_client_images:Bad image line - not IMAGE, instead\n%s", buf);
278     abort ();
279 root 1.2 }
280 root 1.3 num = atoi (buf + 6);
281     if (num < 0 || num >= nrofpixmaps)
282     {
283     LOG (llevError, "read_client_images: Image num %d not in 0..%d\n%s", num, nrofpixmaps, buf);
284     abort ();
285 root 1.2 }
286 root 1.3 /* Skip accross the number data */
287     for (cp = buf + 6; *cp != ' '; cp++);
288     len = atoi (cp);
289     if (len == 0 || len > MAX_IMAGE_SIZE)
290     {
291     LOG (llevError, "read_client_images: length not valid: %d > %d \n%s", len, MAX_IMAGE_SIZE, buf);
292     abort ();
293 root 1.2 }
294 root 1.3 /* We don't actualy care about the name if the image that
295     * is embedded in the image file, so just ignore it.
296     */
297     facesets[fileno].faces[num].datalen = len;
298     facesets[fileno].faces[num].data = (uint8 *) malloc (len);
299     if ((i = fread (facesets[fileno].faces[num].data, len, 1, infile)) != 1)
300     {
301     LOG (llevError, "read_client_images: Did not read desired amount of data, wanted %d, got %d\n%s", len, i, buf);
302     abort ();
303 root 1.2 }
304 root 1.3 facesets[fileno].faces[num].checksum = 0;
305     for (i = 0; i < len; i++)
306     {
307     ROTATE_RIGHT (facesets[fileno].faces[num].checksum);
308     facesets[fileno].faces[num].checksum += facesets[fileno].faces[num].data[i];
309     facesets[fileno].faces[num].checksum &= 0xffffffff;
310 root 1.2 }
311     }
312 root 1.3 close_and_delete (infile, compressed);
313     } /* For fileno < MAX_FACE_SETS */
314 elmex 1.1 }
315    
316     /**
317     * Client tells us what type of faces it wants. Also sets
318     * the caching attribute.
319     *
320     */
321    
322 root 1.3 void
323     SetFaceMode (char *buf, int len, NewSocket * ns)
324 elmex 1.1 {
325 root 1.3 char tmp[256];
326 elmex 1.1
327 root 1.3 int mask = (atoi (buf) & CF_FACE_CACHE), mode = (atoi (buf) & ~CF_FACE_CACHE);
328 elmex 1.1
329 root 1.3 if (mode == CF_FACE_NONE)
330     {
331     ns->facecache = 1;
332     }
333     else if (mode != CF_FACE_PNG)
334     {
335     sprintf (tmp, "drawinfo %d %s", NDI_RED, "Warning - send unsupported face mode. Will use Png");
336     Write_String_To_Socket (ns, tmp, strlen (tmp));
337 elmex 1.1 #ifdef ESRV_DEBUG
338 root 1.3 LOG (llevDebug, "SetFaceMode: Invalid mode from client: %d\n", mode);
339 elmex 1.1 #endif
340     }
341 root 1.3 if (mask)
342     {
343     ns->facecache = 1;
344 elmex 1.1 }
345     }
346    
347     /**
348     * Client has requested pixmap that it somehow missed getting.
349     * This will be called often if the client is
350     * caching images.
351     */
352    
353 root 1.3 void
354     SendFaceCmd (char *buff, int len, NewSocket * ns)
355 elmex 1.1 {
356 root 1.3 long tmpnum = atoi (buff);
357     short facenum = tmpnum & 0xffff;
358 elmex 1.1
359 root 1.3 if (facenum != 0)
360     esrv_send_face (ns, facenum, 1);
361 elmex 1.1 }
362    
363     /**
364     * Sends a face to a client if they are in pixmap mode
365     * nothing gets sent in bitmap mode.
366     * If nocache is true (nonzero), ignore the cache setting from the client -
367     * this is needed for the askface, in which we really do want to send the
368     * face (and askface is the only place that should be setting it). Otherwise,
369     * we look at the facecache, and if set, send the image name.
370     */
371    
372 root 1.3 void
373     esrv_send_face (NewSocket * ns, short face_num, int nocache)
374 elmex 1.1 {
375 root 1.3 SockList sl;
376     char fallback;
377 elmex 1.1
378 root 1.3 if (face_num <= 0 || face_num >= nrofpixmaps)
379     {
380     LOG (llevError, "esrv_send_face (%d) out of bounds??\n", face_num);
381     return;
382     }
383    
384     sl.buf = (unsigned char *) malloc (MAXSOCKBUF);
385     fallback = get_face_fallback (ns->faceset, face_num);
386    
387     if (facesets[fallback].faces[face_num].data == NULL)
388     {
389     LOG (llevError, "esrv_send_face: faces[%d].data == NULL\n", face_num);
390     return;
391     }
392    
393     if (ns->facecache && !nocache)
394     {
395     if (ns->image2)
396     strcpy ((char *) sl.buf, "face2 ");
397     else if (ns->sc_version >= 1026)
398     strcpy ((char *) sl.buf, "face1 ");
399     else
400     strcpy ((char *) sl.buf, "face ");
401    
402     sl.len = strlen ((const char *) sl.buf);
403     SockList_AddShort (&sl, face_num);
404     if (ns->image2)
405     SockList_AddChar (&sl, fallback);
406     if (ns->sc_version >= 1026)
407     SockList_AddInt (&sl, facesets[fallback].faces[face_num].checksum);
408     strcpy ((char *) sl.buf + sl.len, new_faces[face_num].name);
409     sl.len += strlen (new_faces[face_num].name);
410     Send_With_Handling (ns, &sl);
411     }
412     else
413     {
414     if (ns->image2)
415     strcpy ((char *) sl.buf, "image2 ");
416     else
417     strcpy ((char *) sl.buf, "image ");
418     sl.len = strlen ((char *) sl.buf);
419     SockList_AddInt (&sl, face_num);
420     if (ns->image2)
421     SockList_AddChar (&sl, fallback);
422     SockList_AddInt (&sl, facesets[fallback].faces[face_num].datalen);
423     memcpy (sl.buf + sl.len, facesets[fallback].faces[face_num].data, facesets[fallback].faces[face_num].datalen);
424     sl.len += facesets[fallback].faces[face_num].datalen;
425     Send_With_Handling (ns, &sl);
426 elmex 1.1 }
427 root 1.3 ns->faces_sent[face_num] |= NS_FACESENT_FACE;
428     free (sl.buf);
429 elmex 1.1 }
430    
431     /**
432     * Sends the number of images, checksum of the face file,
433     * and the image_info file information. See the doc/Developers/protocol
434     * if you want further detail.
435     */
436    
437 root 1.3 void
438     send_image_info (NewSocket * ns, char *params)
439 elmex 1.1 {
440 root 1.3 SockList sl;
441     int i;
442 elmex 1.1
443 root 1.3 sl.buf = (unsigned char *) malloc (MAXSOCKBUF);
444 elmex 1.1
445 root 1.3 sprintf ((char *) sl.buf, "replyinfo image_info\n%d\n%d\n", nrofpixmaps - 1, bmaps_checksum);
446     for (i = 0; i < MAX_FACE_SETS; i++)
447     {
448     if (facesets[i].prefix)
449     {
450     sprintf ((char *) sl.buf + strlen ((const char *) sl.buf), "%d:%s:%s:%d:%s:%s:%s",
451     i, facesets[i].prefix, facesets[i].fullname, facesets[i].fallback,
452     facesets[i].size, facesets[i].extension, facesets[i].comment);
453 root 1.2 }
454 elmex 1.1 }
455 root 1.3 sl.len = strlen ((const char *) sl.buf);
456     Send_With_Handling (ns, &sl);
457     free (sl.buf);
458 elmex 1.1 }
459    
460     /**
461     * Sends requested face information.
462     * \param ns socket to send to
463     * \param params contains first and last index of face
464     *
465     * For each image in [start..stop] sends
466     * - checksum
467     * - name
468     */
469 root 1.3 void
470     send_image_sums (NewSocket * ns, char *params)
471 elmex 1.1 {
472 root 1.3 int start, stop;
473     short i;
474     char qq;
475     char *cp, buf[MAX_BUF];
476     SockList sl;
477    
478     sl.buf = (unsigned char *) malloc (MAXSOCKBUF);
479    
480     start = atoi (params);
481     for (cp = params; *cp != '\0'; cp++)
482     if (*cp == ' ')
483     break;
484    
485     stop = atoi (cp);
486     if (stop < start || *cp == '\0' || (stop - start) > 1000 || stop >= nrofpixmaps)
487     {
488     sprintf (buf, "replyinfo image_sums %d %d", start, stop);
489     cs_write_string (ns, buf, strlen (buf));
490     return;
491     }
492     sprintf ((char *) sl.buf, "replyinfo image_sums %d %d ", start, stop);
493    
494     sl.len = strlen ((const char *) sl.buf);
495    
496     for (i = start; i <= stop; i++)
497     {
498     SockList_AddShort (&sl, i);
499     ns->faces_sent[i] |= NS_FACESENT_FACE;
500    
501     qq = get_face_fallback (ns->faceset, i);
502     SockList_AddInt (&sl, facesets[qq].faces[i].checksum);
503     SockList_AddChar (&sl, qq);
504    
505     qq = strlen (new_faces[i].name);
506     SockList_AddChar (&sl, (char) (qq + 1));
507     strcpy ((char *) sl.buf + sl.len, new_faces[i].name);
508     sl.len += qq;
509     SockList_AddChar (&sl, 0);
510     }
511     /* It would make more sense to catch this pre-emptively in the code above.
512     * however, if this really happens, we probably just want to cut down the
513     * size to less than 1000, since that is what we claim the protocol would
514     * support.
515     */
516     if (sl.len >= MAXSOCKBUF)
517     {
518     LOG (llevError, "send_image_send: buffer overrun, %d > %d\n", sl.len, MAXSOCKBUF);
519     abort ();
520 elmex 1.1 }
521 root 1.3 Send_With_Handling (ns, &sl);
522     free (sl.buf);
523 elmex 1.1 }