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