1 |
/* |
2 |
* static char *rcsid_image_c = |
3 |
* "$Id: image.C,v 1.2 2006-08-29 08:01:35 root Exp $"; |
4 |
*/ |
5 |
|
6 |
/* |
7 |
CrossFire, A Multiplayer game for X-windows |
8 |
|
9 |
Copyright (C) 2002 Mark Wedel & Crossfire Development Team |
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 maintainer of this code can be reached at crossfire-devel@real-time.com |
27 |
*/ |
28 |
|
29 |
|
30 |
#include <global.h> |
31 |
#include <stdio.h> |
32 |
|
33 |
New_Face *new_faces; |
34 |
|
35 |
/* bmappair and xbm are used when looking for the image id numbers |
36 |
* of a face by name. xbm is sorted alphabetically so that bsearch |
37 |
* can be used to quickly find the entry for a name. the number is |
38 |
* then an index into the new_faces array. |
39 |
* This data is redundant with new_face information - the difference |
40 |
* is that this data gets sorted, and that doesn't necessarily happen |
41 |
* with the new_face data - when accessing new_face[some number], |
42 |
* that some number corresponds to the face at that number - for |
43 |
* xbm, it may not. At current time, these do in fact match because |
44 |
* the bmaps file is created in a sorted order. |
45 |
*/ |
46 |
|
47 |
struct bmappair { |
48 |
char *name; |
49 |
unsigned int number; |
50 |
}; |
51 |
|
52 |
static struct bmappair *xbm=NULL; |
53 |
|
54 |
/* Following can just as easily be pointers, but |
55 |
* it is easier to keep them like this. |
56 |
*/ |
57 |
New_Face *blank_face, *dark_faces[3], *empty_face, *smooth_face; |
58 |
|
59 |
|
60 |
/* nroffiles is the actual number of bitmaps defined. |
61 |
* nrofpixmaps is the number of bitmaps loaded. With |
62 |
* the automatic generation of the bmaps file, this is now equal |
63 |
* to nroffiles. |
64 |
* |
65 |
* The xbm array (which contains name and number information, and |
66 |
* is then sorted) contains nroffiles entries. the xbm_names |
67 |
* array (which is used for converting the numeric face to |
68 |
* a name) contains nrofpixmaps entries. |
69 |
*/ |
70 |
static int nroffiles = 0; |
71 |
int nrofpixmaps = 0; |
72 |
|
73 |
/** |
74 |
* id is the face to smooth, smooth is the 16x2 face used to smooth id. |
75 |
*/ |
76 |
struct smoothing { |
77 |
uint16 id; |
78 |
uint16 smooth; |
79 |
}; |
80 |
|
81 |
/** |
82 |
* Contains all defined smoothing entries. smooth is an array of nrofsmooth |
83 |
* entries. It is sorted by smooth[].id. |
84 |
*/ |
85 |
static struct smoothing *smooth=NULL; |
86 |
int nrofsmooth=0; |
87 |
|
88 |
/* the only thing this table is used for now is to |
89 |
* translate the colorname in the magicmap field of the |
90 |
* face into a numeric index that is then sent to the |
91 |
* client for magic map commands. The order of this table |
92 |
* must match that of the NDI colors in include/newclient.h. |
93 |
*/ |
94 |
static const char *const colorname[] = { |
95 |
"black", /* 0 */ |
96 |
"white", /* 1 */ |
97 |
"blue", /* 2 */ |
98 |
"red", /* 3 */ |
99 |
"orange", /* 4 */ |
100 |
"light_blue", /* 5 */ |
101 |
"dark_orange", /* 6 */ |
102 |
"green", /* 7 */ |
103 |
"light_green", /* 8 */ |
104 |
"grey", /* 9 */ |
105 |
"brown", /* 10 */ |
106 |
"yellow", /* 11 */ |
107 |
"khaki" /* 12 */ |
108 |
}; |
109 |
|
110 |
static int compar (const struct bmappair *a, const struct bmappair *b) { |
111 |
return strcmp (a->name, b->name); |
112 |
} |
113 |
static int compar_smooth (const struct smoothing *a, const struct smoothing *b) { |
114 |
if (a->id<b->id) |
115 |
return -1; |
116 |
if (b->id<a->id) |
117 |
return 1; |
118 |
return 0; |
119 |
} |
120 |
|
121 |
|
122 |
/* |
123 |
* Returns the matching color in the coloralias if found, |
124 |
* 0 otherwise. Note that 0 will actually be black, so there is no |
125 |
* way the calling function can tell if an error occurred or not |
126 |
*/ |
127 |
|
128 |
static uint8 find_color(const char *name) { |
129 |
uint8 i; |
130 |
for(i=0;i<sizeof(colorname)/sizeof(*colorname);i++) |
131 |
if(!strcmp(name,colorname[i])) |
132 |
return i; |
133 |
LOG(llevError,"Unknown color: %s\n",name); |
134 |
return 0; |
135 |
} |
136 |
|
137 |
/* This reads the lib/faces file, getting color and visibility information. |
138 |
* it is called by ReadBmapNames. |
139 |
*/ |
140 |
|
141 |
static void ReadFaceData(void) |
142 |
{ |
143 |
char buf[MAX_BUF], *cp; |
144 |
New_Face *on_face=NULL; |
145 |
FILE *fp; |
146 |
|
147 |
sprintf(buf,"%s/faces", settings.datadir); |
148 |
LOG(llevDebug,"Reading faces from %s...",buf); |
149 |
if ((fp=fopen(buf,"r"))==NULL) { |
150 |
LOG(llevError, "Cannot open faces file %s: %s\n", buf, strerror(errno)); |
151 |
exit(-1); |
152 |
} |
153 |
while (fgets(buf, MAX_BUF, fp)!=NULL) { |
154 |
if (*buf=='#') continue; |
155 |
if (!strncmp(buf,"end",3)) { |
156 |
on_face = NULL; |
157 |
} |
158 |
else if (!strncmp(buf,"face",4)) { |
159 |
int tmp; |
160 |
|
161 |
cp = buf + 5; |
162 |
cp[strlen(cp)-1] = '\0'; /* remove newline */ |
163 |
|
164 |
if ((tmp=FindFace(cp,-1))==-1) { |
165 |
LOG(llevError,"Could not find face %s\n", cp); |
166 |
continue; |
167 |
} |
168 |
on_face = &new_faces[tmp]; |
169 |
on_face->visibility=0; |
170 |
} |
171 |
else if (on_face==NULL) { |
172 |
LOG(llevError,"Got line with no face set: %s\n", buf); |
173 |
} |
174 |
else if (!strncmp(buf,"color_fg",8)) { |
175 |
cp = buf + 9; |
176 |
cp[strlen(cp)-1] = '\0'; |
177 |
if (on_face->magicmap==255) on_face->magicmap=find_color(cp); |
178 |
} |
179 |
else if (!strncmp(buf,"color_bg",8)) { |
180 |
/* ignore it */ |
181 |
} |
182 |
else if (!strncmp(buf,"visibility",10)) { |
183 |
on_face->visibility = atoi(buf + 11); |
184 |
} |
185 |
else if (!strncmp(buf,"magicmap",8)) { |
186 |
cp=buf+9; |
187 |
cp[strlen(cp)-1] = '\0'; |
188 |
on_face->magicmap=find_color(cp); |
189 |
} |
190 |
else if (!strncmp(buf,"is_floor",8)) { |
191 |
int value = atoi(buf+9); |
192 |
if (value) on_face->magicmap |= FACE_FLOOR; |
193 |
} |
194 |
else LOG(llevDebug,"Got unknown line in faces file: %s\n", buf); |
195 |
} |
196 |
LOG(llevDebug,"done\n"); |
197 |
fclose(fp); |
198 |
} |
199 |
|
200 |
/* This reads the bmaps file to get all the bitmap names and |
201 |
* stuff. It only needs to be done once, because it is player |
202 |
* independent (ie, what display the person is on will not make a |
203 |
* difference.) |
204 |
*/ |
205 |
|
206 |
void ReadBmapNames (void) { |
207 |
char buf[MAX_BUF], *p, *q; |
208 |
FILE *fp; |
209 |
int value, nrofbmaps = 0, i; |
210 |
size_t l; |
211 |
|
212 |
bmaps_checksum=0; |
213 |
sprintf (buf,"%s/bmaps", settings.datadir); |
214 |
LOG(llevDebug,"Reading bmaps from %s...",buf); |
215 |
if ((fp=fopen(buf,"r"))==NULL) { |
216 |
LOG(llevError, "Cannot open bmaps file %s: %s\n", buf, strerror(errno)); |
217 |
exit(-1); |
218 |
} |
219 |
|
220 |
/* First count how many bitmaps we have, so we can allocate correctly */ |
221 |
while (fgets (buf, MAX_BUF, fp)!=NULL) |
222 |
if(buf[0] != '#' && buf[0] != '\n' ) |
223 |
nrofbmaps++; |
224 |
rewind(fp); |
225 |
|
226 |
xbm = (struct bmappair *) malloc(sizeof(struct bmappair) * nrofbmaps); |
227 |
memset (xbm, 0, sizeof (struct bmappair) * nrofbmaps); |
228 |
|
229 |
while(nroffiles < nrofbmaps && fgets (buf, MAX_BUF, fp) != NULL) { |
230 |
if (*buf == '#') |
231 |
continue; |
232 |
|
233 |
p = (*buf == '\\') ? (buf + 1): buf; |
234 |
if (!(p = strtok (p , " \t")) || !(q = strtok (NULL , " \t\n"))) { |
235 |
LOG(llevDebug,"Warning, syntax error: %s\n", buf); |
236 |
continue; |
237 |
} |
238 |
value = atoi (p); |
239 |
xbm[nroffiles].name = strdup_local(q); |
240 |
|
241 |
/* We need to calculate the checksum of the bmaps file |
242 |
* name->number mapping to send to the client. This does not |
243 |
* need to match what sum or other utility may come up with - |
244 |
* as long as we get the same results on the same real file |
245 |
* data, it does the job as it lets the client know if |
246 |
* the file has the same data or not. |
247 |
*/ |
248 |
ROTATE_RIGHT(bmaps_checksum); |
249 |
bmaps_checksum += value & 0xff; |
250 |
bmaps_checksum &= 0xffffffff; |
251 |
|
252 |
ROTATE_RIGHT(bmaps_checksum); |
253 |
bmaps_checksum += (value >> 8) & 0xff; |
254 |
bmaps_checksum &= 0xffffffff; |
255 |
for (l=0; l<strlen(q); l++) { |
256 |
ROTATE_RIGHT(bmaps_checksum); |
257 |
bmaps_checksum += q[l]; |
258 |
bmaps_checksum &= 0xffffffff; |
259 |
} |
260 |
|
261 |
xbm[nroffiles].number = value; |
262 |
nroffiles++; |
263 |
if(value >= nrofpixmaps) |
264 |
nrofpixmaps = value+1; |
265 |
} |
266 |
fclose(fp); |
267 |
|
268 |
LOG(llevDebug,"done (got %d/%d/%d)\n",nrofpixmaps,nrofbmaps,nroffiles); |
269 |
|
270 |
new_faces = (New_Face *)malloc(sizeof(New_Face) * nrofpixmaps); |
271 |
for (i = 0; i < nrofpixmaps; i++) { |
272 |
new_faces[i].name = ""; |
273 |
new_faces[i].number = i; |
274 |
new_faces[i].visibility=0; |
275 |
new_faces[i].magicmap=255; |
276 |
} |
277 |
for (i = 0; i < nroffiles; i++) { |
278 |
new_faces[xbm[i].number].name = xbm[i].name; |
279 |
} |
280 |
|
281 |
qsort (xbm, nroffiles, sizeof(struct bmappair), (int (*)(const void*, const void*))compar); |
282 |
|
283 |
ReadFaceData(); |
284 |
|
285 |
for (i = 0; i < nrofpixmaps; i++) { |
286 |
if (new_faces[i].magicmap==255) { |
287 |
#if 0 /* Useful for initial debugging, not needed now */ |
288 |
LOG(llevDebug,"Face %s still had default magicmap, resetting to black\n", |
289 |
new_faces[i].name); |
290 |
#endif |
291 |
new_faces[i].magicmap=0; |
292 |
} |
293 |
} |
294 |
/* Actually forcefully setting the colors here probably should not |
295 |
* be done - it could easily create confusion. |
296 |
*/ |
297 |
blank_face = &new_faces[FindFace(BLANK_FACE_NAME, 0)]; |
298 |
blank_face->magicmap = find_color ("khaki") | FACE_FLOOR; |
299 |
|
300 |
empty_face = &new_faces[FindFace(EMPTY_FACE_NAME, 0)]; |
301 |
|
302 |
dark_faces[0] = &new_faces[FindFace (DARK_FACE1_NAME,0)]; |
303 |
dark_faces[1] = &new_faces[FindFace (DARK_FACE2_NAME,0)]; |
304 |
dark_faces[2] = &new_faces[FindFace (DARK_FACE3_NAME,0)]; |
305 |
|
306 |
smooth_face = &new_faces[FindFace(SMOOTH_FACE_NAME,0)]; |
307 |
} |
308 |
|
309 |
/* This returns an the face number of face 'name'. Number is constant |
310 |
* during an invocation, but not necessarily between versions (this |
311 |
* is because the faces are arranged in alphabetical order, so |
312 |
* if a face is removed or added, all faces after that will now |
313 |
* have a different number. |
314 |
* |
315 |
* the parameter error determines behaviour. If a face is |
316 |
* not found, then error is returned. This can be useful if |
317 |
* you want some default face used, or can be set to negative |
318 |
* so that it will be known that the face could not be found |
319 |
* (needed in client, so that it will know to request that image |
320 |
* from the server) |
321 |
*/ |
322 |
int FindFace (const char *name, int error) { |
323 |
struct bmappair *bp, tmp; |
324 |
char *p; |
325 |
|
326 |
if ((p = strchr (name, '\n'))) |
327 |
*p = '\0'; |
328 |
|
329 |
tmp.name = (char *)name; |
330 |
bp = (struct bmappair *)bsearch |
331 |
(&tmp, xbm, nroffiles, sizeof(struct bmappair), (int (*)(const void*, const void*))compar); |
332 |
|
333 |
return bp ? bp->number : error; |
334 |
} |
335 |
|
336 |
/* Reads the smooth file to know how to smooth datas. |
337 |
* the smooth file if made of 2 elements lines. |
338 |
* lines starting with # are comment |
339 |
* the first element of line is face to smooth |
340 |
* the next element is the 16x2 faces picture |
341 |
* used for smoothing |
342 |
*/ |
343 |
int ReadSmooth (void) { |
344 |
char buf[MAX_BUF], *p, *q; |
345 |
FILE *fp; |
346 |
int smoothcount = 0; |
347 |
|
348 |
bmaps_checksum=0; |
349 |
sprintf (buf,"%s/smooth", settings.datadir); |
350 |
LOG(llevDebug,"Reading smooth from %s...",buf); |
351 |
if ((fp=fopen(buf,"r"))==NULL) { |
352 |
LOG(llevError, "Cannot open smooth file %s: %s\n", strerror(errno)); |
353 |
exit(-1); |
354 |
} |
355 |
|
356 |
/* First count how many smooth we have, so we can allocate correctly */ |
357 |
while (fgets (buf, MAX_BUF, fp)!=NULL) |
358 |
if(buf[0] != '#' && buf[0] != '\n' ) |
359 |
smoothcount++; |
360 |
rewind(fp); |
361 |
|
362 |
smooth = (struct smoothing *) malloc(sizeof(struct smoothing) * (smoothcount)); |
363 |
memset (smooth, 0, sizeof (struct smoothing) * (smoothcount)); |
364 |
|
365 |
while(nrofsmooth < smoothcount && fgets (buf, MAX_BUF, fp)!=NULL) { |
366 |
if (*buf == '#') |
367 |
continue; |
368 |
p=strchr(buf,' '); |
369 |
if (!p) |
370 |
continue; |
371 |
*p='\0'; |
372 |
q=buf; |
373 |
smooth[nrofsmooth].id=FindFace(q,0); |
374 |
q=p+1; |
375 |
smooth[nrofsmooth].smooth=FindFace(q,0); |
376 |
nrofsmooth++; |
377 |
} |
378 |
fclose(fp); |
379 |
|
380 |
LOG(llevDebug,"done (got %d smooth entries)\n",nrofsmooth); |
381 |
qsort (smooth, nrofsmooth, sizeof(struct smoothing), (int (*)(const void*, const void*))compar_smooth); |
382 |
return nrofsmooth; |
383 |
} |
384 |
|
385 |
/** |
386 |
* Find the smooth face for a given face. |
387 |
* |
388 |
* @param face the face to find the smoothing face for |
389 |
* |
390 |
* @param smoothed return value: set to smooth face |
391 |
* |
392 |
* @return 1=smooth face found, 0=no smooth face found |
393 |
*/ |
394 |
int FindSmooth (uint16 face, uint16* smoothed) { |
395 |
struct smoothing *bp, tmp; |
396 |
|
397 |
tmp.id = face; |
398 |
bp = (struct smoothing *)bsearch |
399 |
(&tmp, smooth, nrofsmooth, sizeof(struct smoothing), (int (*)(const void*, const void*))compar_smooth); |
400 |
(*smoothed)=0; |
401 |
if (bp) |
402 |
(*smoothed)=bp->smooth; |
403 |
return bp ? 1 : 0; |
404 |
} |
405 |
|
406 |
/** |
407 |
* Deallocates memory allocated by ReadBmapNames() and ReadSmooth(). |
408 |
*/ |
409 |
void free_all_images(void) |
410 |
{ |
411 |
int i; |
412 |
|
413 |
for (i=0; i<nroffiles; i++) |
414 |
free(xbm[i].name); |
415 |
free(xbm); |
416 |
free(new_faces); |
417 |
free(smooth); |
418 |
} |