1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2013 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
14 /**
15  * @file
16  * Handles server->client commands; See player.c for client->server commands.
17  *
18  * Not necessarily all commands are handled - some might be in other files
19  * (like init.c)
20  *
21  * This file contains most of the commands for the dispatch loop. Most of the
22  * functions are self-explanatory.
23  *
24  * pixmap/bitmap : receive the picture, and display it.
25  * drawinfo      : draws a string in the info window.
26  * stats         : updates the local copy of the stats and displays it.
27  * handle_query  : prompts the user for input.
28  * send_reply    : sends off the reply for the input.
29  * player        : gets the player information.
30  * MapScroll     : scrolls the map on the client by some amount.
31  * MapCmd        : displays the map with layer packing or stack packing.
32  *   packing/unpacking is best understood by looking at the server code
33  *   (server/ericserver.c)
34  *   stack packing: for every map entry that changed, we pack 1 byte for the
35  *   x/y location, 1 byte for the count, and 2 bytes per face in the stack.
36  *   layer packing is harder, but I seem to remember more efficient: first we
37  *   pack in a list of all map cells that changed and are now empty.  The end
38  *   of this list is a 255, which is bigger that 121, the maximum packed map
39  *   location.
40  *   For each changed location we also pack in a list of all the faces and X/Y
41  *   coordinates by layer, where the layer is the depth in the map.  This
42  *   essentially takes slices through the map rather than stacks.
43  *   Then for each layer, (max is MAXMAPCELLFACES, a bad name) we start
44  *   packing the layer into the message.  First we pack in a face, then for
45  *   each place on the layer with the same face, we pack in the x/y location.
46  *   We mark the last x/y location with the high bit on (11*11 = 121 < 128).
47  *   We then continue on with the next face, which is why the code marks the
48  *   faces as -1 if they are finished.  Finally we mark the last face in the
49  *   layer again with the high bit, clearly limiting the total number of faces
50  *   to 32767, the code comments it's 16384, I'm not clear why, but the second
51  *   bit may be used somewhere else as well.
52  *   The unpacking routines basically perform the opposite operations.
53  */
54 
55 int mapupdatesent = 0;
56 
57 #include "client.h"
58 
59 #include <assert.h>
60 #include <ctype.h>
61 #include <errno.h>
62 
63 #include "external.h"
64 #include "mapdata.h"
65 
66 /* In general, the data from the server should not do bad
67  * things like this, but checking for it makes it easier
68  * to find bugs.  Often this is called within a loop
69  * that of iterating over the length of the buffer, hence
70  * the break.  Note that this may not prevent crashes,
71  * but at least we generate a message.
72  * Note that curpos & buflen may be string pointers or
73  * may be integers - as long as both are the same
74  * (both integers or both char *) it will work.
75  */
76 #define ASSERT_LEN(function, curpos, buflen)     \
77     if (curpos > buflen) { \
78             LOG(LOG_WARNING, function, "Data goes beyond length of buffer (%d>%d)", curpos, buflen); \
79             break; \
80 }
81 
82 char *news=NULL, *motd=NULL, *rules=NULL;
83 
84 /** Keeps track of what spellmon command is supported by the server. */
85 static int spellmon_level = 0;
86 
87 int num_races = 0;    /* Number of different races server has */
88 int used_races = 0;   /* How many races we have filled in */
89 
90 int num_classes = 0;  /* Same as race data above, but for classes */
91 int used_classes = 0;
92 
93 int stat_points;    /* Number of stat points for new characters */
94 int stat_min;       /* Minimum stat for new characters */
95 int stat_maximum;   /* Maximum stat for new characters */
96 int starting_map_number = 0;   /* Number of starting maps */
97 
98 Race_Class_Info *races=NULL, *classes=NULL;
99 Starting_Map_Info *starting_map_info = NULL;
100 
101 /* Best I can tell, none of this stat information is stored anyplace
102  * else in the server - MSW 2010-07-28
103  */
104 
105 #define NUM_STATS 7
106 /** Short name of stats. */
107 static const char *const short_stat_name[NUM_STATS] = {
108     "Str",    "Dex",    "Con",
109     "Wis",    "Cha",    "Int",
110     "Pow"
111 };
112 
113 /* Note that the label_cs and label_rs will be in this same
114  * order, eg, label_cs[0] will be strength, label_cs[1] will
115  * be con.  However, this order can be changed, so it should
116  * not be assumed that label_cs[1] will always be con.
117  */
118 struct Stat_Mapping stat_mapping[NUM_NEW_CHAR_STATS] = {
119     {"str", CS_STAT_STR, 0},
120     {"con", CS_STAT_CON, 1},
121     {"dex", CS_STAT_DEX, 2},
122     {"int", CS_STAT_INT, 3},
123     {"wis", CS_STAT_WIS, 4},
124     {"pow", CS_STAT_POW, 5},
125     {"cha", CS_STAT_CHA, 6}
126 };
127 
128 /**
129  * This function clears the data from the Race_Class_Info array.  Because the
130  * structure itself contains data that is allocated, some work needs to be
131  * done to clear that data.
132  *
133  */
free_all_starting_map_info()134 void free_all_starting_map_info()
135 {
136     int i;
137 
138     if (!starting_map_info) {
139         return;
140     }
141 
142     /* Because we are going free the array storage itself, there is no reason
143      * to clear the data[i].. values.
144      */
145     for (i=0; i<starting_map_number; i++) {
146         if (starting_map_info[i].arch_name) {
147             free(starting_map_info[i].arch_name);
148         }
149         if (starting_map_info[i].public_name) {
150             free(starting_map_info[i].public_name);
151         }
152         if (starting_map_info[i].description) {
153             free(starting_map_info[i].description);
154         }
155     }
156 
157     free(starting_map_info);
158     starting_map_info=NULL;
159     starting_map_number = 0;
160 }
161 
162 /**
163  * This processes the replyinfo starting_map_info
164  *
165  * The data is a series of length prefixed lines.
166  *
167  * @param data
168  * data returned from server.  Format is documented in protocol file.
169  * @param len
170  * length of data.
171  */
get_starting_map_info(unsigned char * data,int len)172 static void get_starting_map_info(unsigned char *data, int len) {
173     int pos, type, length, map_entry=-1;
174     char *cp;
175 
176     pos = 0;
177     while (pos < len) {
178         type = data[pos];
179         pos++;
180 
181         /* Right now, all the data is length prefixed strings, so
182          * the only real difference is where we store the data
183          */
184 
185         length = GetShort_String(data + pos);
186         pos += 2;
187 
188         if ((length+pos) > len) {
189             LOG(LOG_WARNING, "common::get_starting_map_info",
190                 "Length of data is greater than buffer (%d>%d)", length + pos, len);
191             return;
192         }
193 
194         cp = g_malloc(length+1);
195         strncpy(cp, (char *)data + pos, length);
196         cp[length] = 0;
197 
198         pos += length;
199 
200         /* If it is the arch name, it is a new entry, so we allocate
201          * space and clear it.  This isn't most efficient, but at
202          * the same time, I don't see there being many maps.
203          * Note: If g_realloc is given a null pointer (which starting_map_info
204          * will be after free or first load), g_realloc just acts as g_malloc.
205          */
206         if (type == INFO_MAP_ARCH_NAME) {
207             map_entry++;
208             starting_map_info = g_realloc(starting_map_info,
209                                         (map_entry + 1) * sizeof(Starting_Map_Info));
210 
211             if (starting_map_info == NULL) {
212                 LOG(LOG_ERROR, "get_starting_map_info",
213                         "Could not allocate memory: %s", strerror(errno));
214                 exit(EXIT_FAILURE);
215             }
216 
217             memset(&starting_map_info[map_entry], 0, sizeof(Starting_Map_Info));
218             starting_map_info[map_entry].arch_name = cp;
219         } else if (type == INFO_MAP_NAME) {
220             starting_map_info[map_entry].public_name = cp;
221         } else if (type == INFO_MAP_DESCRIPTION) {
222             starting_map_info[map_entry].description = cp;
223         } else {
224             /* Could be this is old client - but we can skip over
225              * this bad data so long as the length byte is valid.
226              */
227             LOG(LOG_WARNING, "common::get_starting_map_info",
228                 "Unknown type: %d\n", type);
229         }
230     }
231     starting_map_number = map_entry;
232     starting_map_update_info();
233 }
234 
235 /**
236  * This is process the newcharinfo requestinfo.
237  * In some cases, it stores away the value, for others, it just
238  * makes sure we understand them.
239  *
240  * The data is a series of length prefixed lines.
241  *
242  * @param data
243  * data returned from server.  Format is documented in protocol file.
244  * @param len
245  * length of data.
246  */
get_new_char_info(unsigned char * data,int len)247 static void get_new_char_info(unsigned char *data, int len) {
248     int olen=0, llen;
249 
250     /* We reset these values - if the user is switching between
251      * servers before restarting the client, these may have
252      * different values.
253      */
254     stat_points = 0;
255     stat_min = 0;
256     stat_maximum = 0;
257 
258     while (olen < len) {
259         char datatype, *cp;
260 
261         /* Where this line ends in the total buffer */
262         llen = olen + GetChar_String(data + olen);
263 
264         /* By protocol convention, this should already be NULL,
265          * but we ensure it is.  If the server has not included the
266          * null byte, we are overwriting some real data here, but
267          * the client will probably get an error at that point -
268          * if the server is not following the protocol, we really
269          * can't trust any of the data we get from it.
270          */
271         data[llen] = 0;
272 
273         if (llen > len) {
274             LOG(LOG_WARNING, "common::get_new_char_info",
275                 "Length of line is greater than buffer (%d>%d)", llen, len);
276             return;
277         }
278         olen++;
279         datatype = GetChar_String(data+olen); /* Type value */
280         olen++;
281         /* First skip all the spaces */
282         while (olen <= len) {
283             if (!isspace(data[olen])) {
284                 break;
285             }
286             olen++;
287         }
288         if (olen > len) {
289             LOG(LOG_WARNING, "common::get_new_char_info",
290                 "Overran length of buffer (%d>%d)", olen, len);
291             return;
292         }
293 
294         cp = (char *)data + olen;
295         /* Go until we find another space */
296         while (olen <= len) {
297             if (isspace(data[olen])) {
298                 break;
299             }
300             olen++;
301         }
302         data[olen] = 0;    /* Null terminate the string */
303         olen++;
304         if (olen > len) {
305             LOG(LOG_WARNING, "common::get_new_char_info",
306                 "Overran length of buffer (%d>%d)", olen, len);
307             return;
308         }
309         /* At this point, cp points to the string portion (variable name)
310          * of the line, with data+olen is the start of the next string
311          * (variable value).
312          */
313         if (!g_ascii_strcasecmp(cp,"points")) {
314             stat_points = atoi((char *)data + olen);
315             olen = llen + 1;
316             continue;
317         } else if (!g_ascii_strcasecmp(cp,"statrange")) {
318             if (sscanf((char *)data + olen, "%d %d", &stat_min, &stat_maximum) != 2) {
319                 LOG(LOG_WARNING, "common::get_new_char_info",
320                     "Unable to process statrange line (%s)", data + olen);
321             }
322             /* Either way, we go onto the next line */
323             olen = llen + 1;
324             continue;
325         } else if (!g_ascii_strcasecmp(cp,"statname")) {
326             /* The checking we do here is somewhat basic:
327              * 1) That we understand all the stat names that the server sends us
328              * 2) That we get the correct number of stats.
329              * Note that if the server sends us the same stat name twice, eg
330              * Str Str Dex Con ..., that will screw up this logic, but to a
331              * great extent, we have to trust that server is sending us correct
332              * information - sending the same stat twice does not follow that.
333              */
334             int i, matches=0;
335 
336             while (olen < llen) {
337                 for (i=0; i < NUM_STATS; i++) {
338                     if (!g_ascii_strncasecmp((char *)data + olen,
339                             short_stat_name[i], strlen(short_stat_name[i]))) {
340                         matches++;
341                         olen += strlen(short_stat_name[i]) + 1;
342                         break;
343                     }
344                 }
345                 if (i == NUM_STATS) {
346                     LOG(LOG_WARNING, "common::get_new_char_info",
347                         "Unable to find matching stat name (%s)", data + olen);
348                     break;
349                 }
350             }
351             if (matches != NUM_STATS) {
352                 LOG(LOG_WARNING, "common::get_new_char_info",
353                     "Did not get correct number of stats (%d!=%d)", matches, NUM_STATS);
354             }
355             olen = llen + 1;
356             continue;
357         } else if (!g_ascii_strcasecmp(cp,"race") || !g_ascii_strcasecmp(cp,"class")) {
358             if (g_ascii_strcasecmp((char *)data + olen, "requestinfo")) {
359                 LOG(LOG_WARNING, "common::get_new_char_info",
360                     "Got unexpected value for %s: %s", cp, data+olen);
361             }
362             olen = llen + 1;
363             continue;
364         } else if (!g_ascii_strcasecmp(cp,"startingmap")) {
365             if (g_ascii_strcasecmp((char *)data + olen, "requestinfo")) {
366                 LOG(LOG_WARNING, "common::get_new_char_info",
367                     "Got unexpected value for %s: %s", cp, data+olen);
368             } else {
369                 cs_print_string(csocket.fd, "requestinfo startingmap");
370                 free_all_starting_map_info();
371             }
372             olen = llen + 1;
373             continue;
374         } else {
375             if (datatype == 'V' || datatype == 'R') {
376                 LOG(LOG_WARNING, "common::get_new_char_info",
377                     "Got unsupported string from server, type %c, value %s", datatype, cp);
378                 /* pop up error here */
379             } else {
380                 /* pop up warning here */
381             }
382             olen = llen + 1;
383         }
384     }
385     if (stat_min == 0 || stat_maximum == 0 || stat_points == 0) {
386         /* this needs to be handled better, but I'm not sure how -
387          * we could fall back to legacy character creation mode,
388          * but that will go away at some point - in a sense, if the
389          * server is not sending us values, that is a broken/non comformant
390          * server - best we could perhaps do is throw up a window saying
391          * this client is not compatible with the server.
392          */
393         LOG(LOG_ERROR, "common::get_new_char_info",
394             "Processed all newcharinfo yet have 0 value: stat_min=%d, stat_maximum=%d, stat_points=%d",
395             stat_min, stat_maximum, stat_points);
396     } else {
397         new_char_window_update_info();
398     }
399 }
400 
401 
402 /**
403  * Used for bsearch searching.
404  */
rc_compar(const Race_Class_Info * a,const Race_Class_Info * b)405 static int rc_compar(const Race_Class_Info *a, const Race_Class_Info *b)
406 {
407     return g_ascii_strcasecmp(a->public_name, b->public_name);
408 }
409 
410 /**
411  * This function clears the data from the Race_Class_Info array.  Because the
412  * structure itself contains data that is allocated, some work needs to be
413  * done to clear that data.
414  *
415  * @param data
416  * array to clear
417  * @param num_entries
418  * size of the array.
419  */
free_all_race_class_info(Race_Class_Info * data,int num_entries)420 void free_all_race_class_info(Race_Class_Info *data, int num_entries)
421 {
422     int i;
423 
424     /* Because we are going free the array storage itself, there is no reason
425      * to clear the data[i].. values.
426      */
427     for (i=0; i<num_entries; i++) {
428         int j;
429 
430         if (data[i].arch_name) {
431             free(data[i].arch_name);
432         }
433         if (data[i].public_name) {
434             free(data[i].public_name);
435         }
436         if (data[i].description) {
437             free(data[i].description);
438         }
439 
440         for (j=0; j<data[i].num_rc_choice; j++) {
441             int k;
442 
443             for (k=0; k<data[i].rc_choice[j].num_values; k++) {
444                 free(data[i].rc_choice[j].value_arch[k]);
445                 free(data[i].rc_choice[j].value_desc[k]);
446             }
447             free(data[i].rc_choice[j].value_arch);
448             free(data[i].rc_choice[j].value_desc);
449             free(data[i].rc_choice[j].choice_name);
450             free(data[i].rc_choice[j].choice_desc);
451         }
452     }
453 
454     free(data);
455     data=NULL;
456 }
457 
458 /**
459  * This extracts the data from a replyinfo race_info/class_info request.  We
460  * only get this data if the client has made a requestinfo of this data.
461  *
462  * @param data
463  * data returned from server.  Format is documented in protocol file.
464  * @param len
465  * length of data
466  * @param rci
467  * Where to store the data.
468  */
process_race_class_info(unsigned char * data,int len,Race_Class_Info * rci)469 static void process_race_class_info(unsigned char *data, int len, Race_Class_Info *rci) {
470     char *cp = (char *)data, *nl;
471 
472     /* First thing is to process the remaining bit of the requestinfo line,
473      * which is the archetype name for this race/class
474      */
475     nl = strchr(cp, '\n');
476     if (nl) {
477         *nl=0;
478         rci->arch_name = g_strdup(cp);
479         cp = nl+1;
480     } else {
481         LOG(LOG_WARNING, "common::process_race_class_info", "Did not find archetype name");
482         return;
483     }
484 
485     /* Now we process the rest of the data - we look for a word the describes
486      * the data to follow.  cp is a pointer to the data we are processing.  nl
487      * is used to store temporary values.
488      */
489     do {
490         nl = strchr(cp, ' ');
491         /* If we did not find a space, may just mean we have reached the end
492          * of the data - could be a stray character, etc
493          */
494         if (!nl) {
495             break;
496         }
497 
498         if (nl) {
499             *nl = 0;
500             nl++;
501         }
502         if (!strcmp(cp, "name")) {
503             /* We get a name.  The string is not NULL terminated, but the
504              * length is transmitted.  So get the length, allocate a string
505              * large enough for that + NULL terminator, and copy string in,
506              * making sure to put terminator in place.  also make sure we
507              * update cp beyond this block of data.
508              */
509             int namelen;
510 
511             namelen = GetChar_String((unsigned char *)nl);
512             ASSERT_LEN("common::process_race_class_info",
513                     (unsigned char *)nl + namelen, data + len);
514             nl++;
515             rci->public_name = g_malloc(namelen+1);
516             strncpy(rci->public_name, nl, namelen);
517             rci->public_name[namelen] = 0;
518             cp = nl + namelen;
519         } else if (!strcmp(cp, "stats")) {
520             cp = nl;
521             /* This loop goes through the stat values - *cp points to the stat
522              * value - if 0, no more stats, hence the check here.
523              */
524             while (cp < (char *)data + len && *cp != 0) {
525                 int i;
526 
527                 for (i=0; i < NUM_NEW_CHAR_STATS; i++)
528                     if (stat_mapping[i].cs_value == *cp) {
529                         break;
530                     }
531 
532                 if (i == NUM_NEW_CHAR_STATS) {
533                     /* Just return with what we have */
534                     LOG(LOG_WARNING, "common::process_race_class_info",
535                         "Unknown stat value: %d", cp);
536                     return;
537                 }
538                 rci->stat_adj[stat_mapping[i].rc_offset] =
539                         GetShort_String((unsigned char *)cp + 1);
540                 cp += 3;
541             }
542             cp++;   /* Skip over 0 terminator */
543         } else if (!strcmp(cp, "msg")) {
544             /* This is really exactly same as name processing above, except
545              * length is 2 bytes in this case.
546              */
547             int msglen;
548 
549             msglen = GetShort_String((unsigned char *)nl);
550             ASSERT_LEN("common::process_race_class_info",
551                     (unsigned char *)nl + msglen, data + len);
552             nl+=2;
553             rci->description = g_malloc(msglen+1);
554             strncpy(rci->description, nl, msglen);
555             rci->description[msglen] = 0;
556             cp = nl + msglen;
557         } else if (!strcmp(cp, "choice")) {
558             int oc = rci->num_rc_choice, clen;
559 
560             rci->num_rc_choice++;
561             /* rc_choice may be null, but g_realloc still works there */
562             rci->rc_choice = g_realloc(rci->rc_choice, sizeof(struct RC_Choice) * rci->num_rc_choice);
563             memset(&rci->rc_choice[oc], 0, sizeof(struct RC_Choice));
564 
565             cp = nl;
566 
567             /* First is the coice string we return */
568             clen = GetChar_String((unsigned char *)cp);
569             cp++;
570             ASSERT_LEN("common::process_race_class_info",
571                     (unsigned char *)cp + clen, data + len);
572             rci->rc_choice[oc].choice_name = g_malloc(clen+1);
573             strncpy(rci->rc_choice[oc].choice_name, cp, clen);
574             rci->rc_choice[oc].choice_name[clen] = 0;
575             cp += clen;
576 
577             /* Next is the description */
578             clen = GetChar_String((unsigned char *)cp);
579             cp++;
580             ASSERT_LEN("common::process_race_class_info",
581                     (unsigned char *)cp + clen, data + len);
582             rci->rc_choice[oc].choice_desc = g_malloc(clen+1);
583             strncpy(rci->rc_choice[oc].choice_desc, cp, clen);
584             rci->rc_choice[oc].choice_desc[clen] = 0;
585             cp += clen;
586 
587             /* Now is a series of archetype/description pairs */
588             while (1) {
589                 int vn;
590 
591                 clen = GetChar_String((unsigned char *)cp);
592                 cp++;
593                 if (!clen) {
594                     break;    /* 0 length is end of data */
595                 }
596                 vn = rci->rc_choice[oc].num_values;
597                 rci->rc_choice[oc].num_values++;
598                 rci->rc_choice[oc].value_arch = g_realloc(rci->rc_choice[oc].value_arch,
599                                                         sizeof(char*) * rci->rc_choice[oc].num_values);
600                 rci->rc_choice[oc].value_desc = g_realloc(rci->rc_choice[oc].value_desc,
601                                                         sizeof(char*) * rci->rc_choice[oc].num_values);
602 
603                 ASSERT_LEN("common::process_race_class_info",
604                         (unsigned char *)cp + clen, data + len);
605                 rci->rc_choice[oc].value_arch[vn] = g_malloc(clen+1);
606                 strncpy(rci->rc_choice[oc].value_arch[vn], cp, clen);
607                 rci->rc_choice[oc].value_arch[vn][clen] = 0;
608                 cp += clen;
609 
610                 clen = GetChar_String((unsigned char *)cp);
611                 cp++;
612                 ASSERT_LEN("common::process_race_class_info",
613                         (unsigned char *)cp + clen, data + len);
614                 rci->rc_choice[oc].value_desc[vn] = g_malloc(clen+1);
615                 strncpy(rci->rc_choice[oc].value_desc[vn], cp, clen);
616                 rci->rc_choice[oc].value_desc[vn][clen] = 0;
617                 cp += clen;
618             }
619         } else {
620             /* Got some keyword we did not understand.  Because we do not know
621              * about it, we do not know how to skip it over - the data could
622              * very well contain spaces or other markers we look for.
623              */
624             LOG(LOG_WARNING, "common::process_race_class_info", "Got unknown keyword: %s", cp);
625             break;
626         }
627     } while ((unsigned char *)cp < data + len);
628 
629     /* The display code expects all of these to have a description -
630      * rather than add checks there for NULL values, simpler to
631      * just set things to an empty value.
632      */
633     if (!rci->description) {
634         rci->description = g_strdup("");
635     }
636 
637 }
638 
639 /**
640  * This is a little wrapper function that does some bounds checking and then
641  * calls process_race_info() to do the bulk of the work.
642  *
643  * @param data
644  * data returned from server.  Format is documented in protocol file.
645  * @param len
646  * length of data.
647  */
get_race_info(unsigned char * data,int len)648 static void get_race_info(unsigned char *data, int len) {
649     /* This should not happen - the client is only requesting race info for
650      * races it has received - and it knows how many of those it has.
651      */
652     if (used_races >= num_races) {
653         LOG(LOG_ERROR, "common::get_race_info",
654             "used races exceed num races, %d>=%d", used_races, num_races);
655         return;
656     }
657 
658     process_race_class_info(data, len, &races[used_races]);
659     used_races++;
660 
661     if (used_races == num_races) {
662         qsort(races, used_races, sizeof(Race_Class_Info),
663               (int (*)(const void *, const void *))rc_compar);
664 
665         new_char_window_update_info();
666     }
667 }
668 
669 /**
670  * This is a little wrapper function that does some bounds checking and then
671  * calls process_race_info() to do the bulk of the work.  Pretty much
672  * identical to get_race_info() except this is for classes.
673  *
674  * @param data
675  * data returned from server.  Format is documented in protocol file.
676  * @param len
677  * length of data.
678  */
get_class_info(unsigned char * data,int len)679 static void get_class_info(unsigned char *data, int len) {
680     /* This should not happen - the client is only requesting race info for
681      * classes it has received - and it knows how many of those it has.
682      */
683     if (used_classes >= num_classes) {
684         LOG(LOG_ERROR, "common::get_race_info",
685             "used classes exceed num classes, %d>=%d", used_classes, num_classes);
686         return;
687     }
688 
689     process_race_class_info(data, len, &classes[used_classes]);
690     used_classes++;
691 
692     if (used_classes == num_classes) {
693         qsort(classes, used_classes, sizeof(Race_Class_Info),
694               (int (*)(const void *, const void *))rc_compar);
695 
696         new_char_window_update_info();
697     }
698 }
699 
700 /**
701  *
702  * @param data
703  * @param len
704  */
get_exp_info(const unsigned char * data,int len)705 static void get_exp_info(const unsigned char *data, int len)
706 {
707     int pos, level;
708 
709     if (len < 2) {
710         LOG(LOG_ERROR, "common::get_exp_info", "no max level info from server provided");
711         return;
712     }
713 
714     exp_table_max = GetShort_String(data);
715     pos = 2;
716     exp_table = calloc(exp_table_max, sizeof(guint64));
717     for (level = 1; level <= exp_table_max && pos < len; level++) {
718         exp_table[level] = GetInt64_String(data+pos);
719         pos += 8;
720     }
721     if (level != exp_table_max) {
722         LOG(LOG_ERROR, "common::get_exp_info",
723             "Incomplete table sent - got %d entries, wanted %d", level, exp_table_max);
724     }
725 }
726 
727 /**
728  *
729  * @param data
730  * @param len
731  */
get_skill_info(char * data,int len)732 static void get_skill_info(char *data, int len)
733 {
734     char *cp, *nl, *sn;
735     int val;
736 
737     cp = data;
738     do {
739         nl = strchr(cp, '\n');
740         if (nl) {
741             *nl = 0;
742             nl++;
743         }
744         sn = strchr(cp, ':');
745         if (!sn) {
746             LOG(LOG_WARNING, "common::get_skill_info", "corrupt line: /%s/", cp);
747             return;
748         }
749 
750         *sn = 0;
751         sn++;
752         val = atoi(cp);
753         val -= CS_STAT_SKILLINFO;
754 
755         /* skill_names[MAX_SKILL] is the declaration, so check against that */
756         if (val < 0 || val >= MAX_SKILL) {
757             LOG(LOG_WARNING, "common::get_skill_info", "invalid skill number %d", val);
758             return;
759         }
760 
761         free(skill_names[val]);
762         skill_names[val] = g_strdup(sn);
763         cp = nl;
764     } while (cp < data+len);
765 }
766 
767 /**
768  * Handles the response from a 'requestinfo' command.  This function doesn't
769  * do much itself other than dispatch to other functions.
770  *
771  * @param buf
772  * @param len
773  */
ReplyInfoCmd(unsigned char * buf,int len)774 void ReplyInfoCmd(unsigned char *buf, int len) {
775     unsigned char *cp;
776     int i;
777 
778     /* Covers a bug in the server in that it could send a replyinfo with no
779      * parameters
780      */
781     if (!buf) {
782         return;
783     }
784 
785     for (i = 0; i < len; i++) {
786         /* Either a space or newline represents a break */
787         if (*(buf+i) == ' ' || *(buf+i) == '\n') {
788             break;
789         }
790     }
791     if (i >= len) {
792         /* Don't print buf, as it may contain binary data */
793         /* Downgrade this to DEBUG - if the client issued an unsupported
794          * requestinfo info to the server, we'll end up here - this could be
795          * normal behaviour
796          */
797         LOG(LOG_DEBUG, "common::ReplyInfoCmd", "Never found a space in the replyinfo");
798         return;
799     }
800 
801     /* Null out the space and put cp beyond it */
802     cp = buf+i;
803     *cp++ = '\0';
804     if (!strcmp((char*)buf, "image_info")) {
805         get_image_info(cp, len-i-1);        /* Located in common/image.c */
806     } else if (!strcmp((char*)buf, "image_sums")) {
807         get_image_sums((char*)cp, len-i-1); /* Located in common/image.c */
808     } else if (!strcmp((char*)buf, "skill_info")) {
809         get_skill_info((char*)cp, len-i-1); /* Located in common/commands.c */
810     } else if (!strcmp((char*)buf, "exp_table")) {
811         get_exp_info(cp, len-i-1);          /* Located in common/commands.c */
812     } else if (!strcmp((char*)buf, "motd")) {
813         if (motd) {
814             free((char*)motd);
815         }
816         motd = g_strdup((char *)cp);
817         update_login_info(INFO_MOTD);
818     } else if (!strcmp((char*)buf, "news")) {
819         if (news) {
820             free((char*)news);
821         }
822         news = g_strdup((char *)cp);
823         update_login_info(INFO_NEWS);
824     } else if (!strcmp((char*)buf, "rules")) {
825         if (rules) {
826             free((char*)rules);
827         }
828         rules = g_strdup((char *)cp);
829         update_login_info(INFO_RULES);
830     } else if (!strcmp((char*)buf, "race_list")) {
831         unsigned char *cp1;
832         for (cp1=cp; *cp !=0; cp++) {
833             if (*cp == '|') {
834                 *cp++ = '\0';
835                 /* The first separator has no data, so only send request to
836                  * server if this is not null.
837                  */
838                 if (*cp1!='\0') {
839                     cs_print_string(csocket.fd, "requestinfo race_info %s", cp1);
840                     num_races++;
841                 }
842                 cp1 = cp;
843             }
844         }
845         if (*cp1!='\0') {
846             cs_print_string(csocket.fd, "requestinfo race_info %s", cp1);
847             num_races++;
848         }
849         if (races) {
850             free_all_race_class_info(races, num_races);
851             num_races=0;
852             used_races=0;
853         }
854         races = calloc(num_races, sizeof(Race_Class_Info));
855 
856     }  else if (!strcmp((char*)buf, "class_list")) {
857         unsigned char *cp1;
858         for (cp1=cp; *cp !=0; cp++) {
859             if (*cp == '|') {
860                 *cp++ = '\0';
861                 /* The first separator has no data, so only send request to
862                  * server if this is not null.
863                  */
864                 if (*cp1!='\0') {
865                     cs_print_string(csocket.fd, "requestinfo class_info %s", cp1);
866                     num_classes++;
867                 }
868                 cp1 = cp;
869             }
870         }
871         /* last race isn't followed by a | */
872         if (*cp1 != '\0') {
873             cs_print_string(csocket.fd, "requestinfo class_info %s", cp1);
874             num_classes++;
875         }
876         if (classes) {
877             free_all_race_class_info(classes, num_classes);
878             num_classes=0;
879             used_classes=0;
880         }
881         classes = calloc(num_classes, sizeof(Race_Class_Info));
882     }  else if (!strcmp((char*)buf, "race_info")) {
883         get_race_info(cp, len -i -1);
884     } else if (!strcmp((char*)buf, "class_info")) {
885         get_class_info(cp, len -i -1);
886     } else if (!strcmp((char*)buf, "newcharinfo")) {
887         get_new_char_info(cp, len -i -1);
888     } else if (!strcmp((char*)buf, "startingmap")) {
889         get_starting_map_info(cp, len -i -1);
890     }
891 }
892 
893 /**
894  * Received a response to a setup from the server.  This function is basically
895  * the same as the server side function - we just do some different processing
896  * on the data.
897  *
898  * @param buf
899  * @param len
900  */
SetupCmd(char * buf,int len)901 void SetupCmd(char *buf, int len)
902 {
903     int s;
904     char *cmd, *param;
905 
906     /* Process the setup commands.
907      * Syntax is setup <cmdname1> <parameter> <cmdname2> <parameter> ...
908      *
909      * The server sends the status of the cmd back, or a FALSE if the cmd is
910      * unknown.  The client then must sort this out.
911      */
912 
913     LOG(LOG_DEBUG, "common::SetupCmd", "%s", buf);
914     for (s = 0; ; ) {
915         if (s >= len) { /* Ugly, but for secure...*/
916             break;
917         }
918 
919         cmd = &buf[s];
920 
921         /* Find the next space, and put a null there */
922         for (; buf[s] && buf[s] != ' '; s++)
923             ;
924         buf[s++] = 0;
925         while (buf[s] == ' ') {
926             s++;
927         }
928         if (s >= len) {
929             break;
930         }
931 
932         param = &buf[s];
933 
934         for (; buf[s] && buf[s] != ' '; s++)
935             ;
936         buf[s++] = 0;
937         while (s < len && buf[s] == ' ') {
938             s++;
939         }
940 
941         /* What is done with the returned data depends on what the server
942          * returns.  In some cases the client may fall back to other methods,
943          * report an error, or try another setup command.
944          */
945         if (!strcmp(cmd, "sound2")) {
946             /* No parsing needed, but we don't want a warning about unknown
947              * setup option below.
948              */
949         } else if (!strcmp(cmd, "sound")) {
950             /* No, this should not be !strcmp()... */
951         } else if (!strcmp(cmd, "mapsize")) {
952             int x, y = 0;
953             char *cp, tmpbuf[MAX_BUF];
954 
955             if (!g_ascii_strcasecmp(param, "false")) {
956                 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER,
957                               "Server only supports standard sized maps (11x11)");
958                 /* Do this because we may have been playing on a big server
959                  * before */
960                 use_config[CONFIG_MAPWIDTH] = 11;
961                 use_config[CONFIG_MAPHEIGHT] = 11;
962                 mapdata_set_size(use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
963                 resize_map_window(use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
964                 continue;
965             }
966             x = atoi(param);
967             for (cp = param; *cp != 0; cp++) {
968                 if (*cp == 'x' || *cp == 'X') {
969                     y = atoi(cp+1);
970                     break;
971                 }
972             }
973             /* A size larger than what the server supports was requested.
974              * Reduce the size to server maximum, and re-send the setup
975              * command.  Update our want sizes, and tell the player what is
976              * going on.
977              */
978             if (use_config[CONFIG_MAPWIDTH] > x || use_config[CONFIG_MAPHEIGHT] > y) {
979                 if (use_config[CONFIG_MAPWIDTH] > x) {
980                     use_config[CONFIG_MAPWIDTH] = x;
981                 }
982                 if (use_config[CONFIG_MAPHEIGHT] > y) {
983                     use_config[CONFIG_MAPHEIGHT] = y;
984                 }
985                 mapdata_set_size(use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
986                 cs_print_string(csocket.fd,
987                                 "setup mapsize %dx%d", use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
988                 snprintf(tmpbuf, sizeof(tmpbuf), "Server supports a max mapsize of %d x %d - requesting a %d x %d mapsize",
989                          x, y, use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
990                 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER,
991                               tmpbuf);
992             } else if (use_config[CONFIG_MAPWIDTH] == x && use_config[CONFIG_MAPHEIGHT] == y) {
993                 mapdata_set_size(use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
994                 resize_map_window(use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT]);
995             } else {
996                 /* The request was not bigger than what server supports, and
997                  * not the same size, so what is the problem?  Tell the user
998                  * that something is wrong.
999                  */
1000                 snprintf(tmpbuf, sizeof(tmpbuf), "Unable to set mapsize on server - we wanted %d x %d, server returned %d x %d",
1001                          use_config[CONFIG_MAPWIDTH], use_config[CONFIG_MAPHEIGHT], x, y);
1002                 draw_ext_info(
1003                     NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER, tmpbuf);
1004             }
1005         } else if (!strcmp(cmd, "darkness")) {
1006             /* Older servers might not support this setup command.
1007              */
1008             if (!strcmp(param, "FALSE")) {
1009                 LOG(LOG_WARNING, "common::SetupCmd", "Server returned FALSE for setup command %s", cmd);
1010             }
1011         } else if (!strcmp(cmd, "spellmon")) {
1012 
1013             /* Older servers might not support this setup command or all of
1014              * the extensions.
1015              *
1016              * Spellmon 2 was added to the protocol in January 2010 to send an
1017              * additional spell information string with casting requirements
1018              * including required items, if the spell needs arguments passed
1019              * (like text for rune of marking), etc.
1020              *
1021              * To use the new feature, "setup spellmon 1 spellmon 2" is sent,
1022              * and if "spellmon 1 spellmon FALSE" is returned then the server
1023              * doesn't accept 2 - sending spellmon 2 to a server that does not
1024              * support it is not problematic, so the spellmon 1 command will
1025              * still be handled correctly by the server.  If the server sends
1026              * "spellmon 1 spellmon 2" then the extended mode is in effect.
1027              *
1028              * It is not particularly important for the player to know what
1029              * level of command is accepted by the server.  The extra features
1030              * will simply not be functionally available.
1031              */
1032             if (!strcmp(param, "FALSE")) {
1033                 LOG(LOG_INFO, "common::SetupCmd", "Server returned FALSE for a %s setup command", cmd);
1034             } else {
1035                 spellmon_level = atoi(param);
1036             }
1037         } else if (!strcmp(cmd, "facecache")) {
1038             use_config[CONFIG_CACHE] = atoi(param);
1039         } else if (!strcmp(cmd, "faceset")) {
1040             if (!strcmp(param, "FALSE")) {
1041                 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER,
1042                               "Server does not support other image sets, will use default");
1043                 face_info.faceset = 0;
1044             }
1045         } else if (!strcmp(cmd, "map2cmd")) {
1046             if (!strcmp(param, "FALSE")) {
1047                 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER,
1048                               "Server does not support map2cmd!");
1049                 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER,
1050                               "This server is too old to support this client!");
1051                 client_disconnect();
1052             }
1053         } else if (!strcmp(cmd, "want_pickup")) {
1054             /* Nothing special to do as this is info pushed from server and
1055              * not having it isn't that bad.
1056              */
1057         } else if (!strcmp(cmd, "loginmethod")) {
1058             int method = atoi(param);
1059 
1060             /* If the server supports new login, start the process.  Pass what
1061              * version the server supports so client can do appropriate
1062              * work
1063              */
1064             if (method) {
1065                 start_login(method);
1066             }
1067         } else if (!strcmp(cmd, "newmapcmd")) {
1068             // if server doesn't support newmapcmd, too bad
1069         } else if (!strcmp(cmd, "tick")) {
1070             // Ticks drive the redraw loop via client_tick(), so we really
1071             // do need ticks. To support servers without ticks, add a timer
1072             // callback via g_timeout_add() that calls client_tick().
1073             if (!strcmp(param, "FALSE")) {
1074                 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER,
1075                               "Server does not support tick!");
1076                 draw_ext_info(NDI_RED, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_SERVER,
1077                               "This server is too old to support this client!");
1078                 client_disconnect();
1079             }
1080         } else if (!strcmp(cmd, "extendedTextInfos")) {
1081             // Even though this is deprecated, old servers are sitll being
1082             // actively used. Request extended text info (drawextinfo).
1083             if (csocket.cs_version < 1023 && strcmp(param, "FALSE")) { /* server didn't send FALSE*/
1084                 /* Server seems to accept extended text infos. Let's tell
1085                  * it what extended text info we want
1086                  */
1087                 for (int i = 1; i < 20; i++) {
1088                     char exttext[MAX_BUF];
1089                     snprintf(exttext, sizeof(exttext), "toggleextendedtext %d", i);
1090                     cs_print_string(csocket.fd, exttext);
1091                 }
1092             }
1093         } else {
1094             LOG(LOG_INFO, "common::SetupCmd",
1095                     "Got setup for a command we don't understand: %s %s",
1096                     cmd, param);
1097         }
1098     }
1099 }
1100 
1101 /**
1102  * Handles when the server says we can't be added.  In reality, we need to
1103  * close the connection and quit out, because the client is going to close us
1104  * down anyways.
1105  *
1106  * @param data
1107  * @param len
1108  */
AddMeFail(char * data,int len)1109 void AddMeFail(char *data, int len)
1110 {
1111     (void)data; /* __UNUSED__ */
1112     (void)len; /* __UNUSED__ */
1113 
1114     LOG(LOG_INFO, "common::AddMeFail", "addme_failed received.");
1115     return;
1116 }
1117 
1118 /**
1119  * This is really a throwaway command - there really isn't any reason to send
1120  * addme_success commands.
1121  *
1122  * @param data
1123  * @param len
1124  */
AddMeSuccess(char * data,int len)1125 void AddMeSuccess(char *data, int len)
1126 {
1127     (void)data; /* __UNUSED__ */
1128     (void)len; /* __UNUSED__ */
1129 
1130     hide_all_login_windows();
1131     show_main_client();
1132     LOG(LOG_DEBUG, "common::AddMeSuccess", "addme_success received.");
1133     return;
1134 }
1135 
1136 /**
1137  *
1138  * @param data
1139  * @param len
1140  */
GoodbyeCmd(char * data,int len)1141 void GoodbyeCmd(char *data, int len)
1142 {
1143     (void)data; /* __UNUSED__ */
1144     (void)len; /* __UNUSED__ */
1145 
1146     /* This could probably be greatly improved - I am not sure if anything
1147      * needs to be saved here, but it should be possible to reconnect to the
1148      * server or a different server without having to rerun the client.
1149      */
1150     LOG(LOG_WARNING, "common::GoodbyeCmd", "Received goodbye command from server - exiting");
1151     exit(0);
1152 }
1153 
1154 Animations animations[MAXANIM];
1155 
1156 /**
1157  *
1158  * @param data
1159  * @param len
1160  */
AnimCmd(unsigned char * data,int len)1161 void AnimCmd(unsigned char *data, int len)
1162 {
1163     short anum;
1164     int i, j;
1165 
1166     anum = GetShort_String(data);
1167     if (anum < 0 || anum > MAXANIM) {
1168         LOG(LOG_WARNING, "common::AnimCmd", "animation number invalid: %d", anum);
1169         return;
1170     }
1171 
1172     animations[anum].flags = GetShort_String(data+2);
1173     animations[anum].num_animations = (len-4)/2;
1174     if (animations[anum].num_animations < 1) {
1175         LOG(LOG_WARNING, "common::AnimCmd", "num animations invalid: %d",
1176             animations[anum].num_animations);
1177         return;
1178     }
1179     animations[anum].faces = g_malloc(sizeof(guint16)*animations[anum].num_animations);
1180     for (i = 4, j = 0; i < len; i += 2, j++) {
1181         animations[anum].faces[j] = GetShort_String(data+i);
1182     }
1183 
1184     if (j != animations[anum].num_animations) {
1185         LOG(LOG_WARNING, "common::AnimCmd",
1186             "Calculated animations does not equal stored animations? (%d!=%d)",
1187             j, animations[anum].num_animations);
1188     }
1189 
1190     animations[anum].speed = 0;
1191     animations[anum].speed_left = 0;
1192     animations[anum].phase = 0;
1193 
1194     LOG(LOG_DEBUG, "common::AnimCmd", "Received animation %d, %d faces", anum, animations[anum].num_animations);
1195 }
1196 
1197 /**
1198  * Receives the smooth mapping from the server.  Because this information is
1199  * reference a lot, the smoothing face is stored in the pixmap data - this
1200  * makes access much faster than searching an array of data for the face to
1201  * use.
1202  *
1203  * @param data
1204  * @param len
1205  */
SmoothCmd(unsigned char * data,int len)1206 void SmoothCmd(unsigned char *data, int len)
1207 {
1208     guint16 faceid;
1209     guint16 smoothing;
1210 
1211     /* len is unused.  We should check that we don't have an invalid short
1212      * command.  Hence, the compiler warning is valid.
1213      */
1214 
1215     faceid = GetShort_String(data);
1216     smoothing = GetShort_String(data+2);
1217     addsmooth(faceid, smoothing);
1218 }
1219 
1220 /**
1221  * Draws a string in the info window.
1222  *
1223  * @param data
1224  * @param len
1225  */
DrawInfoCmd(char * data,int len)1226 void DrawInfoCmd(char *data, int len)
1227 {
1228     int color = atoi(data);
1229     char *buf;
1230 
1231     (void)len; /* __UNUSED__ */
1232 
1233     buf = strchr(data, ' ');
1234     if (!buf) {
1235         LOG(LOG_WARNING, "common::DrawInfoCmd", "got no data");
1236         buf = "";
1237     } else {
1238         buf++;
1239     }
1240     draw_ext_info(color, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_COMMAND, buf);
1241 }
1242 
1243 TextManager *firstTextManager = NULL;
1244 
1245 /**
1246  *
1247  * @param type
1248  * @param callback
1249  */
setTextManager(int type,ExtTextManager callback)1250 void setTextManager(int type, ExtTextManager callback)
1251 {
1252     TextManager *current = firstTextManager;
1253 
1254     while (current != NULL) {
1255         if (current->type == type) {
1256             current->callback = callback;
1257             return;
1258         }
1259         current = current->next;
1260     }
1261     current = g_malloc(sizeof(TextManager));
1262     current->type = type;
1263     current->callback = callback;
1264     current->next = firstTextManager;
1265     firstTextManager = current;
1266 }
1267 
1268 /**
1269  *
1270  * @param type
1271  */
getTextManager(int type)1272 static ExtTextManager getTextManager(int type)
1273 {
1274     TextManager *current = firstTextManager;
1275     while (current != NULL) {
1276         if (current->type == type) {
1277             return current->callback;
1278         }
1279         current = current->next;
1280     }
1281     return NULL;
1282 }
1283 
1284 /**
1285  * We must extract color, type, subtype and dispatch to callback
1286  *
1287  * @param data
1288  * @param len
1289  */
DrawExtInfoCmd(char * data,int len)1290 void DrawExtInfoCmd(char *data, int len)
1291 {
1292     int color;
1293     int type, subtype;
1294     char *buf = data;
1295     int wordCount = 3;
1296     ExtTextManager fnct;
1297 
1298     while (wordCount > 0) {
1299         while (buf[0] == ' ') {
1300             buf++;
1301         }
1302         wordCount--;
1303         while (buf[0] != ' ') {
1304             if (buf[0] == '\0') {
1305                 LOG(LOG_WARNING,
1306                     "common::DrawExtInfoCmd", "Data is missing %d parameters %s",
1307                     wordCount,
1308                     data);
1309                 return;
1310             } else {
1311                 buf++;
1312             }
1313         }
1314         if (buf[0] == ' ') {
1315             buf++; /*remove trailing space to send clean data to callback */
1316         }
1317     }
1318     wordCount = sscanf(data, "%d %d %d", &color, &type, &subtype);
1319     if (wordCount != 3) {
1320         LOG(LOG_WARNING,
1321             "common::DrawExtInfoCmd", "Wrong parameters received. Could only parse %d out of 3 int in %s",
1322             wordCount,
1323             data);
1324         return;
1325     }
1326     fnct = getTextManager(type);
1327     if (fnct == NULL) {
1328         LOG(LOG_WARNING,
1329             "common::DrawExtInfoCmd", "Server send us a type %d but i can't find any callback for it",
1330             type);
1331         return;
1332     }
1333     fnct(color, type, subtype, buf);
1334 }
1335 
1336 /**
1337  * Maintain the last_used_skills LRU list for displaying the recently used
1338  * skills first.
1339  *
1340  * @param skill_id
1341  */
use_skill(int skill_id)1342 void use_skill(int skill_id)
1343 {
1344     int i = 0;
1345     int next;
1346     int prev = last_used_skills[0];
1347 
1348     if(last_used_skills[0] == skill_id) {
1349         return;
1350     }
1351 
1352     do {
1353         next = last_used_skills[i+1];
1354         last_used_skills[i+1] = prev;
1355         prev = next;
1356         ++i;
1357     } while(next != skill_id && next >= 0);
1358     last_used_skills[0] = skill_id;
1359 }
1360 
1361 /**
1362  * Updates the local copy of the stats and displays it.
1363  *
1364  * @param data
1365  * @param len
1366  */
StatsCmd(unsigned char * data,int len)1367 void StatsCmd(unsigned char *data, int len)
1368 {
1369     int i = 0, c, redraw = 0;
1370     gint64 last_exp;
1371 
1372     while (i < len) {
1373         c = data[i++];
1374         if (c >= CS_STAT_RESIST_START && c <= CS_STAT_RESIST_END) {
1375             cpl.stats.resists[c-CS_STAT_RESIST_START] = GetShort_String(data+i);
1376             i += 2;
1377             cpl.stats.resist_change = 1;
1378         } else if (c >= CS_STAT_SKILLINFO && c < (CS_STAT_SKILLINFO+CS_NUM_SKILLS)) {
1379             /* We track to see if the exp has gone from 0 to some total value
1380              * - we do this because the draw logic currently only draws skills
1381              * where the player has exp.  We need to communicate to the draw
1382              * function that it should draw all the players skills.  Using
1383              * redraw is a little overkill, because a lot of the data may not
1384              * be changing.  OTOH, such a transition should only happen
1385              * rarely, not not be a very big deal.
1386              */
1387             cpl.stats.skill_level[c-CS_STAT_SKILLINFO] = data[i++];
1388             last_exp = cpl.stats.skill_exp[c-CS_STAT_SKILLINFO];
1389             cpl.stats.skill_exp[c-CS_STAT_SKILLINFO] = GetInt64_String(data+i);
1390             use_skill(c-CS_STAT_SKILLINFO);
1391             if (last_exp == 0 && cpl.stats.skill_exp[c-CS_STAT_SKILLINFO]) {
1392                 redraw = 1;
1393             }
1394             i += 8;
1395         } else {
1396             switch (c) {
1397             case CS_STAT_HP:
1398                 cpl.stats.hp = GetShort_String(data+i);
1399                 i += 2;
1400                 break;
1401             case CS_STAT_MAXHP:
1402                 cpl.stats.maxhp = GetShort_String(data+i);
1403                 i += 2;
1404                 break;
1405             case CS_STAT_SP:
1406                 cpl.stats.sp = GetShort_String(data+i);
1407                 i += 2;
1408                 break;
1409             case CS_STAT_MAXSP:
1410                 cpl.stats.maxsp = GetShort_String(data+i);
1411                 i += 2;
1412                 break;
1413             case CS_STAT_GRACE:
1414                 cpl.stats.grace = GetShort_String(data+i);
1415                 i += 2;
1416                 break;
1417             case CS_STAT_MAXGRACE:
1418                 cpl.stats.maxgrace = GetShort_String(data+i);
1419                 i += 2;
1420                 break;
1421             case CS_STAT_STR:
1422                 cpl.stats.Str = GetShort_String(data+i);
1423                 i += 2;
1424                 break;
1425             case CS_STAT_INT:
1426                 cpl.stats.Int = GetShort_String(data+i);
1427                 i += 2;
1428                 break;
1429             case CS_STAT_POW:
1430                 cpl.stats.Pow = GetShort_String(data+i);
1431                 i += 2;
1432                 break;
1433             case CS_STAT_WIS:
1434                 cpl.stats.Wis = GetShort_String(data+i);
1435                 i += 2;
1436                 break;
1437             case CS_STAT_DEX:
1438                 cpl.stats.Dex = GetShort_String(data+i);
1439                 i += 2;
1440                 break;
1441             case CS_STAT_CON:
1442                 cpl.stats.Con = GetShort_String(data+i);
1443                 i += 2;
1444                 break;
1445             case CS_STAT_CHA:
1446                 cpl.stats.Cha = GetShort_String(data+i);
1447                 i += 2;
1448                 break;
1449             case CS_STAT_EXP:
1450                 cpl.stats.exp = GetInt_String(data+i);
1451                 i += 4;
1452                 break;
1453             case CS_STAT_EXP64:
1454                 cpl.stats.exp = GetInt64_String(data+i);
1455                 i += 8;
1456                 break;
1457             case CS_STAT_LEVEL:
1458                 cpl.stats.level = GetShort_String(data+i);
1459                 i += 2;
1460                 break;
1461             case CS_STAT_WC:
1462                 cpl.stats.wc = GetShort_String(data+i);
1463                 i += 2;
1464                 break;
1465             case CS_STAT_AC:
1466                 cpl.stats.ac = GetShort_String(data+i);
1467                 i += 2;
1468                 break;
1469             case CS_STAT_DAM:
1470                 cpl.stats.dam = GetShort_String(data+i);
1471                 i += 2;
1472                 break;
1473             case CS_STAT_ARMOUR:
1474                 cpl.stats.resists[0] = GetShort_String(data+i);
1475                 i += 2;
1476                 break;
1477             case CS_STAT_SPEED:
1478                 cpl.stats.speed = GetInt_String(data+i);
1479                 i += 4;
1480                 break;
1481             case CS_STAT_FOOD:
1482                 cpl.stats.food = GetShort_String(data+i);
1483                 i += 2;
1484                 break;
1485             case CS_STAT_WEAP_SP:
1486                 cpl.stats.weapon_sp = GetInt_String(data+i);
1487                 i += 4;
1488                 break;
1489             case CS_STAT_SPELL_ATTUNE:
1490                 cpl.stats.attuned = GetInt_String(data+i);
1491                 i += 4;
1492                 cpl.spells_updated = 1;
1493                 break;
1494             case CS_STAT_SPELL_REPEL:
1495                 cpl.stats.repelled = GetInt_String(data+i);
1496                 i += 4;
1497                 cpl.spells_updated = 1;
1498                 break;
1499             case CS_STAT_SPELL_DENY:
1500                 cpl.stats.denied = GetInt_String(data+i);
1501                 i += 4;
1502                 cpl.spells_updated = 1;
1503                 break;
1504 
1505             case CS_STAT_FLAGS:
1506                 cpl.stats.flags = GetShort_String(data+i);
1507                 i += 2;
1508                 break;
1509             case CS_STAT_WEIGHT_LIM:
1510                 set_weight_limit(cpl.stats.weight_limit = GetInt_String(data+i));
1511                 i += 4;
1512                 /* Mark weight limit changes to update the client inventory window */
1513                 cpl.ob->inv_updated = 1;
1514                 break;
1515 
1516             case CS_STAT_RANGE: {
1517                 int rlen = data[i++];
1518                 strncpy(cpl.range, (const char*)data+i, rlen);
1519                 cpl.range[rlen] = '\0';
1520                 i += rlen;
1521                 break;
1522             }
1523 
1524             case CS_STAT_TITLE: {
1525                 int rlen = data[i++];
1526                 strncpy(cpl.title, (const char*)data+i, rlen);
1527                 cpl.title[rlen] = '\0';
1528                 i += rlen;
1529                 break;
1530             }
1531 
1532             default:
1533                 LOG(LOG_WARNING, "common::StatsCmd", "Unknown stat number %d", c);
1534                 break;
1535             }
1536         }
1537     }
1538 
1539     if (i > len) {
1540         LOG(LOG_WARNING, "common::StatsCmd", "got stats overflow, processed %d bytes out of %d", i, len);
1541     }
1542     draw_stats(redraw);
1543     draw_message_window(0);
1544 #ifdef HAVE_LUA
1545     script_lua_stats();
1546 #endif
1547 }
1548 
1549 /**
1550  * Prompts the user for input.
1551  *
1552  * @param data
1553  * @param len
1554  */
handle_query(char * data,int len)1555 void handle_query(char *data, int len)
1556 {
1557     char *buf, *cp;
1558     guint8 flags = atoi(data);
1559 
1560     (void)len; /* __UNUSED__ */
1561 
1562     if (flags&CS_QUERY_HIDEINPUT) { /* No echo */
1563         cpl.no_echo = 1;
1564     } else {
1565         cpl.no_echo = 0;
1566     }
1567 
1568     /* Let the window system know this may have changed */
1569     x_set_echo();
1570 
1571     /* The actual text is optional */
1572     buf = strchr(data, ' ');
1573     if (buf) {
1574         buf++;
1575     }
1576 
1577     /* If we just get passed an empty string, why draw this? */
1578     if (buf) {
1579         cp = buf;
1580         while ((buf = strchr(buf, '\n')) != NULL) {
1581             *buf++ = '\0';
1582             draw_ext_info(
1583                 NDI_BLACK, MSG_TYPE_CLIENT, MSG_TYPE_CLIENT_QUERY, cp);
1584             cp = buf;
1585         }
1586         /* Yes/no - don't do anything with it now */
1587         if (flags&CS_QUERY_YESNO) {
1588         }
1589 
1590         /* One character response expected */
1591         if (flags&CS_QUERY_SINGLECHAR) {
1592             cpl.input_state = Reply_One;
1593         } else {
1594             cpl.input_state = Reply_Many;
1595         }
1596 
1597         if (cp) {
1598             draw_prompt(cp);
1599         }
1600     }
1601 
1602     LOG(LOG_DEBUG, "common::handle_query", "Received query.  Input state now %d", cpl.input_state);
1603 }
1604 
1605 /**
1606  * Sends a reply to the server.  This function basically just packs the stuff
1607  * up.
1608  *
1609  * @param text contains the null terminated string of text to send.
1610  */
send_reply(const char * text)1611 void send_reply(const char *text)
1612 {
1613     cs_print_string(csocket.fd, "reply %s", text);
1614 
1615     /* Let the window system know that the (possibly hidden) query is over. */
1616     cpl.no_echo = 0;
1617     x_set_echo();
1618 }
1619 
1620 /**
1621  * Gets the player information.  This function copies relevant data from the
1622  * archetype to the object.  Only copies data that was not set in the object
1623  * structure.
1624  *
1625  * @param data
1626  * @param len
1627  */
PlayerCmd(unsigned char * data,int len)1628 void PlayerCmd(unsigned char *data, int len)
1629 {
1630     char name[MAX_BUF];
1631     int tag, weight, face, i = 0, nlen;
1632 
1633     reset_player_data();
1634     tag = GetInt_String(data);
1635     i += 4;
1636     weight = GetInt_String(data+i);
1637     i += 4;
1638     face = GetInt_String(data+i);
1639     i += 4;
1640     nlen = data[i++];
1641     memcpy(name, (const char*)data+i, nlen);
1642     name[nlen] = '\0';
1643     i += nlen;
1644 
1645     if (i != len) {
1646         LOG(LOG_WARNING, "common::PlayerCmd", "lengths do not match (%d!=%d)", len, i);
1647     }
1648     new_player(tag, name, weight, face);
1649 }
1650 
1651 /**
1652  *
1653  * @param op
1654  */
item_actions(item * op)1655 void item_actions(item *op)
1656 {
1657     if (!op) {
1658         return;
1659     }
1660 
1661     if (op->open) {
1662         open_container(op);
1663         cpl.container = op;
1664     } else if (op->was_open) {
1665         close_container(op);
1666         cpl.container = NULL;
1667     }
1668 }
1669 
1670 /**
1671  * Parses the data sent to us from the server.  revision is what item command
1672  * the data came from - newer ones have addition fields.
1673  * @param data
1674  * @param len
1675  */
Item2Cmd(unsigned char * data,int len)1676 void Item2Cmd(unsigned char *data, int len) {
1677     int weight, loc, tag, face, flags, pos = 0, nlen, anim, nrof, type;
1678     guint8 animspeed;
1679     char name[MAX_BUF];
1680 
1681     loc = GetInt_String(data);
1682     pos += 4;
1683 
1684     if (pos == len) {
1685         LOG(LOG_WARNING, "common::common_item_command", "Got location with no other data");
1686         return;
1687     } else if (loc < 0) { /* Delete following items */
1688         LOG(LOG_WARNING, "common::common_item_command", "Got location with negative value (%d)", loc);
1689         return;
1690     } else {
1691         while (pos < len) {
1692             tag = GetInt_String(data+pos);
1693             pos += 4;
1694             flags = GetInt_String(data+pos);
1695             pos += 4;
1696             weight = GetInt_String(data+pos);
1697             pos += 4;
1698             face = GetInt_String(data+pos);
1699             pos += 4;
1700             nlen = data[pos++];
1701             memcpy(name, (char*)data+pos, nlen);
1702             pos += nlen;
1703             name[nlen] = '\0';
1704             anim = GetShort_String(data+pos);
1705             pos += 2;
1706             animspeed = data[pos++];
1707             nrof = GetInt_String(data+pos);
1708             pos += 4;
1709             type = GetShort_String(data+pos);
1710             pos += 2;
1711             update_item(tag, loc, name, weight, face, flags, anim, animspeed, nrof, type);
1712             item_actions(locate_item(tag));
1713         }
1714         if (pos > len) {
1715             LOG(LOG_WARNING, "common::common_item_cmd", "Overread buffer: %d > %d", pos, len);
1716         }
1717     }
1718 }
1719 
1720 /**
1721  * Updates some attributes of an item
1722  *
1723  * @param data
1724  * @param len
1725  */
UpdateItemCmd(unsigned char * data,int len)1726 void UpdateItemCmd(unsigned char *data, int len)
1727 {
1728     int weight, loc, tag, face, sendflags, flags, pos = 0, nlen, anim;
1729     guint32 nrof;
1730     char name[MAX_BUF];
1731     item *ip;
1732     guint8 animspeed;
1733 
1734     sendflags = data[0];
1735     pos += 1;
1736     tag = GetInt_String(data+pos);
1737     pos += 4;
1738     ip = locate_item(tag);
1739     if (!ip) {
1740         /*
1741                 fprintf(stderr, "Got update_item command for item we don't have (%d)\n", tag);
1742         */
1743         return;
1744     }
1745 
1746     /* Copy all of these so we can pass the values to update_item and don't
1747      * need to figure out which ones were modified by this function.
1748      */
1749     *name = '\0';
1750     loc = ip->env ? ip->env->tag : 0;
1751     weight = ip->weight*1000;
1752     face = ip->face;
1753     flags = ip->flagsval;
1754     anim = ip->animation_id;
1755     animspeed = ip->anim_speed;
1756     nrof = ip->nrof;
1757 
1758     if (sendflags&UPD_LOCATION) {
1759         loc = GetInt_String(data+pos);
1760         LOG(LOG_WARNING, "common::UpdateItemCmd", "Got tag of unknown object (%d) for new location", loc);
1761         pos += 4;
1762     }
1763     if (sendflags&UPD_FLAGS) {
1764         flags = GetInt_String(data+pos);
1765         pos += 4;
1766     }
1767     if (sendflags&UPD_WEIGHT) {
1768         weight = GetInt_String(data+pos);
1769         pos += 4;
1770     }
1771     if (sendflags&UPD_FACE) {
1772         face = GetInt_String(data+pos);
1773         pos += 4;
1774     }
1775     if (sendflags&UPD_NAME) {
1776         nlen = data[pos++];
1777         memcpy(name, (char*)data+pos, nlen);
1778         pos += nlen;
1779         name[nlen] = '\0';
1780     }
1781     if (pos > len) {
1782         LOG(LOG_WARNING, "common::UpdateItemCmd", "Overread buffer: %d > %d", pos, len);
1783         return; /* We have bad data, probably don't want to store it then */
1784     }
1785     if (sendflags&UPD_ANIM) {
1786         anim = GetShort_String(data+pos);
1787         pos += 2;
1788     }
1789     if (sendflags&UPD_ANIMSPEED) {
1790         animspeed = data[pos++];
1791     }
1792     if (sendflags&UPD_NROF) {
1793         nrof = (guint32)GetInt_String(data+pos);
1794         pos += 4;
1795     }
1796     /* update_item calls set_item_values which will then set the list redraw
1797      * flag, so we don't need to do an explicit redraw here.  Actually,
1798      * calling update_item is a little bit of overkill, since we already
1799      * determined some of the values in this function.
1800      */
1801     update_item(tag, loc, name, weight, face, flags, anim, animspeed, nrof, ip->type);
1802     item_actions(locate_item(tag));
1803 }
1804 
1805 /**
1806  *
1807  * @param data
1808  * @param len
1809  */
DeleteItem(unsigned char * data,int len)1810 void DeleteItem(unsigned char *data, int len)
1811 {
1812     int pos = 0, tag;
1813 
1814     while (pos < len) {
1815         item *op;
1816 
1817         tag = GetInt_String(data+pos);
1818         pos += 4;
1819         op = locate_item(tag);
1820         if (op != NULL) {
1821             remove_item(op);
1822         } else {
1823             LOG(LOG_WARNING, "common::DeleteItem", "Cannot find tag %d", tag);
1824         }
1825     }
1826     if (pos > len) {
1827         LOG(LOG_WARNING, "common::DeleteItem", "Overread buffer: %d > %d", pos, len);
1828     }
1829 }
1830 
1831 /**
1832  *
1833  * @param data
1834  * @param len
1835  */
DeleteInventory(unsigned char * data,int len)1836 void DeleteInventory(unsigned char *data, int len)
1837 {
1838     int tag;
1839     item *op;
1840 
1841     (void)len; /* __UNUSED__ */
1842 
1843     tag = atoi((const char*)data);
1844     op = locate_item(tag);
1845     if (op != NULL) {
1846         remove_item_inventory(op);
1847     } else {
1848         LOG(LOG_WARNING, "common::DeleteInventory", "Invalid tag: %d", tag);
1849     }
1850 }
1851 
1852 /**
1853  * Remove trailing newlines from the given C string in-place.
1854  */
rstrip(char buf[static1],size_t len)1855 static void rstrip(char buf[static 1], size_t len) {
1856     for (size_t i = len - 1; i > 0; i--) {
1857         if (buf[i] == '\n' || buf[i] == ' ') {
1858             buf[i] = '\0';
1859         } else {
1860             return;
1861         }
1862     }
1863 }
1864 
1865 /****************************************************************************/
1866 
1867 /**
1868  * @defgroup SCSpellCommands Server->Client spell command functions.
1869  * @{
1870  */
1871 
1872 /**
1873  *
1874  * @param data
1875  * @param len
1876  */
AddspellCmd(unsigned char * data,int len)1877 void AddspellCmd(unsigned char *data, int len)
1878 {
1879     guint8 nlen;
1880     guint16 mlen, pos = 0;
1881     Spell *newspell, *tmp;
1882 
1883     while (pos < len) {
1884         newspell = calloc(1, sizeof(Spell));
1885 
1886         /* Get standard spell information (spellmon 1)
1887          */
1888         newspell->tag = GetInt_String(data+pos);
1889         pos += 4;
1890         newspell->level = GetShort_String(data+pos);
1891         pos += 2;
1892         newspell->time = GetShort_String(data+pos);
1893         pos += 2;
1894         newspell->sp = GetShort_String(data+pos);
1895         pos += 2;
1896         newspell->grace = GetShort_String(data+pos);
1897         pos += 2;
1898         newspell->dam = GetShort_String(data+pos);
1899         pos += 2;
1900         newspell->skill_number = GetChar_String(data+pos);
1901         pos += 1;
1902         newspell->path = GetInt_String(data+pos);
1903         pos += 4;
1904         newspell->face = GetInt_String(data+pos);
1905         pos += 4;
1906         nlen = GetChar_String(data+pos);
1907         pos += 1;
1908         strncpy(newspell->name, (char*)data+pos, nlen);
1909         pos += nlen;
1910         newspell->name[nlen] = '\0'; /* To ensure we are null terminated */
1911         mlen = GetShort_String(data+pos);
1912         pos += 2;
1913         strncpy(newspell->message, (char*)data+pos, mlen);
1914         pos += mlen;
1915         newspell->message[mlen] = '\0'; /* To ensure we are null terminated */
1916         rstrip(newspell->message, mlen);
1917 
1918         if (spellmon_level < 2) {
1919 
1920             /* The server is not sending spellmon 2 extended information, so
1921              * initialize the spell data fields as unused/empty.
1922              */
1923             newspell->usage = 0;
1924             newspell->requirements[0] = '\0';
1925 
1926         } else if (pos < len) {
1927 
1928             /* The server is sending extended spell information (spellmon 2) so
1929              * process it.
1930              */
1931             newspell->usage = GetChar_String(data+pos);
1932             pos += 1;
1933             nlen = GetChar_String(data+pos);
1934             pos += 1;
1935             strncpy(newspell->requirements, (char*) data+pos, nlen);
1936             pos += nlen;
1937             newspell->requirements[nlen] = '\0'; /* Ensure null-termination */
1938         }
1939 
1940         /* Compute the derived spell information.
1941          */
1942         newspell->skill = skill_names[newspell->skill_number-CS_STAT_SKILLINFO];
1943 
1944         /* Add the spell to the player struct.
1945          */
1946         if (!cpl.spelldata) {
1947             cpl.spelldata = newspell;
1948         } else {
1949             for (tmp = cpl.spelldata; tmp->next; tmp = tmp->next)
1950                 ;
1951             tmp->next = newspell;
1952         }
1953         /* Check to see if there are more spells to add.
1954          */
1955     }
1956     if (pos > len) {
1957         LOG(LOG_WARNING, "common::AddspellCmd", "Overread buffer: %d > %d", pos, len);
1958     }
1959     cpl.spells_updated = 1;
1960 }
1961 
UpdspellCmd(unsigned char * data,int len)1962 void UpdspellCmd(unsigned char *data, int len) {
1963     int flags, pos = 0;
1964     guint32 tag;
1965     Spell *tmp;
1966 
1967     if (!cpl.spelldata) {
1968         LOG(LOG_WARNING, "common::UpdspellCmd", "I know no spells to update");
1969         return;
1970     }
1971 
1972     flags = GetChar_String(data+pos);
1973     pos += 1;
1974     tag = GetInt_String(data+pos);
1975     pos += 4;
1976     for (tmp = cpl.spelldata; tmp && tmp->tag != tag; tmp = tmp->next)
1977         ;
1978     if (!tmp) {
1979         LOG(LOG_WARNING, "common::UpdspellCmd", "Invalid tag: %d", tag);
1980         return;
1981     }
1982     if (flags&UPD_SP_MANA) {
1983         tmp->sp = GetShort_String(data+pos);
1984         pos += 2;
1985     }
1986     if (flags&UPD_SP_GRACE) {
1987         tmp->grace = GetShort_String(data+pos);
1988         pos += 2;
1989     }
1990     if (flags&UPD_SP_DAMAGE) {
1991         tmp->dam = GetShort_String(data+pos);
1992         pos += 2;
1993     }
1994     if (pos > len) {
1995         LOG(LOG_WARNING, "common::UpdspellCmd", "Overread buffer: %d > %d", pos, len);
1996     }
1997     cpl.spells_updated = 1;
1998 }
1999 
DeleteSpell(unsigned char * data,int len)2000 void DeleteSpell(unsigned char *data, int len) {
2001     guint32 tag;
2002     Spell *tmp, *target;
2003 
2004     if (!cpl.spelldata) {
2005         LOG(LOG_WARNING, "common::DeleteSpell", "I know no spells to delete");
2006         return;
2007     }
2008 
2009     tag = GetInt_String(data);
2010     /* Special case: the first spell is the one removed */
2011     if (cpl.spelldata->tag == tag) {
2012         target = cpl.spelldata;
2013         if (target->next) {
2014             cpl.spelldata = target->next;
2015         } else {
2016             cpl.spelldata = NULL;
2017         }
2018         free(target);
2019         return;
2020     }
2021 
2022     for (tmp = cpl.spelldata; tmp->next && tmp->next->tag != tag; tmp = tmp->next)
2023         ;
2024     if (!tmp->next) {
2025         LOG(LOG_WARNING, "common::DeleteSpell", "Invalid tag: %d", tag);
2026         return;
2027     }
2028     target = tmp->next;
2029     if (target->next) {
2030         tmp->next = target->next;
2031     } else {
2032         tmp->next = NULL;
2033     }
2034     free(target);
2035     cpl.spells_updated = 1;
2036 }
2037 
2038 /****************************************************************************/
2039 
2040 /**
2041  * @} */ /* EndOf SCSpellCommands
2042  */
2043 
2044 /**
2045  * @defgroup SCMapCommands Server->Client map command functions.
2046  * @{
2047  */
2048 
2049 /**
2050  *
2051  * @param data
2052  * @param len
2053  */
NewmapCmd(unsigned char * data,int len)2054 void NewmapCmd(unsigned char *data, int len)
2055 {
2056     (void)data; /* __UNUSED__ */
2057     (void)len; /* __UNUSED__ */
2058 
2059     mapdata_newmap();
2060 }
2061 
2062 /* This is the common processing block for the map1 and map1a protocol
2063  * commands.  The map1a mieks minor extensions and are easy to deal with
2064  * inline (in fact, this code doesn't even care what rev is - just certain
2065  * bits will only bet set when using the map1a command.  rev is 0 for map1, 1
2066  * for map1a.  It conceivable that there could be future revisions.
2067  */
2068 
2069 /* NUM_LAYERS should only be used for the map1{a} which only has a few layers.
2070  * Map2 has 10 layers.  However, some of the map1 logic requires this to be
2071  * set right.
2072  */
2073 #define NUM_LAYERS (MAP1_LAYERS-1)
2074 
2075 /**
2076  *
2077  * @param data
2078  * @param len
2079  */
Map2Cmd(unsigned char * data,int len)2080 void Map2Cmd(unsigned char *data, int len)
2081 {
2082     int mask, x, y, pos = 0, space_len, value;
2083     guint8 type;
2084 
2085     /* Not really using map1 protocol, but some draw logic differs from the
2086      * original draw logic, and map2 is closest.
2087      */
2088     while (pos < len) {
2089         mask = GetShort_String(data+pos);
2090         pos += 2;
2091         x = ((mask>>10)&0x3f)-MAP2_COORD_OFFSET;
2092         y = ((mask>>4)&0x3f)-MAP2_COORD_OFFSET;
2093 
2094         /* This is a scroll then.  Go back and fetch another coordinate */
2095         if (mask&0x1) {
2096             mapdata_scroll(x, y);
2097             continue;
2098         }
2099 
2100         if (x<0) {
2101             LOG(LOG_WARNING, "commands.c::Map2Cmd", "got negative x!");
2102             x = 0;
2103         } else if (x >= MAX_VIEW) {
2104             LOG(LOG_WARNING, "commands.c::Map2Cmd", "got x >= MAX_VIEW!");
2105             x = MAX_VIEW - 1;
2106         }
2107 
2108         if (y<0) {
2109             LOG(LOG_WARNING, "commands.c::Map2Cmd", "got negative y!");
2110             y = 0;
2111         } else if (y >= MAX_VIEW) {
2112             LOG(LOG_WARNING, "commands.c::Map2Cmd", "got y >= MAX_VIEW!");
2113             y = MAX_VIEW - 1;
2114         }
2115 
2116         assert(0 <= x && x < MAX_VIEW);
2117         assert(0 <= y && y < MAX_VIEW);
2118         /* Clearing old cell data as needed (was in mapdata_set_face_layer()
2119          * before however that caused darkness to only work if sent after the
2120          * layers).
2121          */
2122         mapdata_clear_old(x, y);
2123 
2124         /* Inner loop is for the data on the space itself */
2125         while (pos < len) {
2126             type = data[pos++];
2127             /* type == 255 means nothing more for this space */
2128             if (type == 255) {
2129                 mapdata_set_check_space(x, y);
2130                 break;
2131             }
2132             space_len = type>>5;
2133             type &= 0x1f;
2134             /* Clear the space */
2135             if (type == MAP2_TYPE_CLEAR) {
2136                 mapdata_clear_space(x, y);
2137                 continue;
2138             } else if (type == MAP2_TYPE_DARKNESS) {
2139                 value = data[pos++];
2140                 mapdata_set_darkness(x, y, value);
2141                 continue;
2142             } else if (type >= MAP2_LAYER_START && type < MAP2_LAYER_START+MAXLAYERS) {
2143                 int layer, opt;
2144 
2145                 /* This is face information for a layer. */
2146                 layer = type&0xf;
2147 
2148                 if (layer < 0) {
2149                     LOG(LOG_WARNING, "commands.c::Map2Cmd", "got negative layer!");
2150                     layer = 0;
2151                 } else if (layer >= MAXLAYERS) {
2152                     LOG(LOG_WARNING, "commands.c::Map2Cmd", "got layer >= MAXLAYERS!");
2153                     layer = MAXLAYERS - 1;
2154                 }
2155                 assert(0 <= layer && layer < MAXLAYERS);
2156 
2157                 /* This is the face */
2158                 value = GetShort_String(data+pos);
2159                 pos += 2;
2160                 if (!(value&FACE_IS_ANIM)) {
2161                     mapdata_set_face_layer(x, y, value, layer);
2162                 }
2163 
2164                 if (space_len > 2) {
2165                     opt = data[pos++];
2166                     if (value&FACE_IS_ANIM) {
2167                         /* Animation speed */
2168                         mapdata_set_anim_layer(x, y, value, opt, layer);
2169                     } else {
2170                         /* Smooth info */
2171                         mapdata_set_smooth(x, y, opt, layer);
2172                     }
2173                 }
2174                 /* Currently, if 4 bytes, must be a smooth byte */
2175                 if (space_len > 3) {
2176                     opt = data[pos++];
2177                     mapdata_set_smooth(x, y, opt, layer);
2178                 }
2179                 continue;
2180             } /* if image layer */
2181         } /* while pos<len inner loop for space */
2182     } /* While pos<len outer loop */
2183     mapupdatesent = 0;
2184     display_map_doneupdate(FALSE, FALSE);
2185 }
2186 
2187 /**
2188  * Scrolls the map on the client by some amount.
2189  *
2190  * @param data
2191  * @param len
2192  */
map_scrollCmd(char * data,int len)2193 void map_scrollCmd(char *data, int len)
2194 {
2195     int dx, dy;
2196     char *buf;
2197 
2198     (void)len; /* __UNUSED__ */
2199 
2200     dx = atoi(data);
2201     buf = strchr(data, ' ');
2202     if (!buf) {
2203         LOG(LOG_WARNING, "common::map_scrollCmd", "Got short packet.");
2204         return;
2205     }
2206     buf++;
2207     dy = atoi(buf);
2208 
2209     mapdata_scroll(dx, dy);
2210     display_map_doneupdate(FALSE, TRUE);
2211 }
2212 
2213 /**
2214  * Extract smoothing infos from an extendedmapinfo packet part data is located
2215  * at the beginning of the smooth datas
2216  *
2217  * @param data
2218  * @param len
2219  * @param x
2220  * @param y
2221  * @param layer
2222  */
ExtSmooth(unsigned char * data,int len,int x,int y,int layer)2223 int ExtSmooth(unsigned char *data, int len, int x, int y, int layer)
2224 {
2225     static int dx[8] = { 0, 1, 1, 1, 0, -1, -1, -1, };
2226     static int dy[8] = { -1, -1, 0, 1, 1, 1, 0, -1, };
2227     int i, rx, ry;
2228     int newsm;
2229 
2230     if (len < 1) {
2231         return 0;
2232     }
2233 
2234     x += pl_pos.x;
2235     y += pl_pos.y;
2236     newsm = GetChar_String(data);
2237 
2238     if (mapdata_cell(x, y)->smooth[layer] != newsm) {
2239         for (i = 0; i < 8; i++) {
2240             rx = x+dx[i];
2241             ry = y+dy[i];
2242             if (!mapdata_contains(rx, ry)) {
2243                 continue;
2244             }
2245             mapdata_cell(x, y)->need_resmooth = 1;
2246         }
2247     }
2248     mapdata_cell(x, y)->smooth[layer] = newsm;
2249     return 1;/*Cause smooth infos only use 1 byte*/
2250 }
2251 
2252 /**
2253  * Handle MapExtended command
2254  * Warning! if you add commands to extended, take care that the 'layer'
2255  * argument of main loop is the opposite of the layer of the map so if you
2256  * reference a layer, use NUM_LAYERS-layer.
2257  *
2258  * @param data
2259  * @param len
2260  */
MapExtendedCmd(unsigned char * data,int len)2261 void MapExtendedCmd(unsigned char *data, int len)
2262 {
2263     int mask, x, y, pos = 0, layer;
2264     int noredraw = 0;
2265     int hassmooth = 0;
2266     int entrysize;
2267     int startpackentry;
2268 
2269     mapupdatesent = 1;
2270     mask = GetChar_String(data+pos);
2271     pos += 1;
2272     if (mask&EMI_NOREDRAW) {
2273         noredraw = 1;
2274     }
2275     if (mask&EMI_SMOOTH) {
2276         hassmooth = 1;
2277     }
2278     while (mask&EMI_HASMOREBITS) {
2279         /*There may be bits we ignore about*/
2280         mask = GetChar_String(data+pos);
2281         pos += 1;
2282     }
2283     entrysize = GetChar_String(data+pos);
2284     pos = pos+1;
2285 
2286     while (pos+entrysize+2 <= len) {
2287         mask = GetShort_String(data+pos);
2288         pos += 2;
2289         x = (mask>>10)&0x3f;
2290         y = (mask>>4)&0x3f;
2291         for (layer = NUM_LAYERS; layer >= 0; layer--) {
2292             if (mask&(1<<layer)) {
2293                 /*handle an entry*/
2294                 if (pos+entrysize > len) { /*erroneous packet*/
2295                     break;
2296                 }
2297                 startpackentry = pos;
2298                 /* If you had extended infos to the server, this is where, in
2299                  * the client, you may add your code
2300                  */
2301                 if (hassmooth) {
2302                     pos = pos+ExtSmooth(data+pos, len-pos, x, y, NUM_LAYERS-layer);
2303                 }
2304                 /* Continue with other if you add new extended infos to server
2305                  *
2306                  * Now point to the next data
2307                  */
2308                 pos = startpackentry+entrysize;
2309             }
2310         }
2311     }
2312     if (!noredraw) {
2313         display_map_doneupdate(FALSE, FALSE);
2314         mapupdatesent = 0;
2315     }
2316 }
2317 
2318 /**
2319  *
2320  * @param data
2321  * @param len
2322  */
MagicMapCmd(unsigned char * data,int len)2323 void MagicMapCmd(unsigned char *data, int len)
2324 {
2325     unsigned char *cp;
2326     int i;
2327 
2328     /* First, extract the size/position information. */
2329     if (sscanf((const char*)data, "%hd %hd %hd %hd", &cpl.mmapx, &cpl.mmapy, &cpl.pmapx, &cpl.pmapy) != 4) {
2330         LOG(LOG_WARNING, "common::MagicMapCmd", "Was not able to properly extract magic map size, pos");
2331         return;
2332     }
2333 
2334     if (cpl.mmapx == 0 || cpl.mmapy == 0) {
2335         LOG(LOG_WARNING, "common::MagicMapCmd", "empty map");
2336         return;
2337     }
2338 
2339     /* Now we need to find the start of the actual data.  There are 4 space
2340      * characters we need to skip over.
2341      */
2342     for (cp = data, i = 0; i < 4 && cp < data+len; cp++) {
2343         if (*cp == ' ') {
2344             i++;
2345         }
2346     }
2347     if (i != 4) {
2348         LOG(LOG_WARNING, "common::MagicMapCmd", "Was unable to find start of magic map data");
2349         return;
2350     }
2351     i = len-(cp-data); /* This should be the number of bytes left */
2352     if (i != cpl.mmapx*cpl.mmapy) {
2353         LOG(LOG_WARNING, "common::MagicMapCmd", "Magic map size mismatch.  Have %d bytes, should have %d",
2354             i, cpl.mmapx*cpl.mmapy);
2355         return;
2356     }
2357     free(cpl.magicmap);
2358     cpl.magicmap = g_malloc(cpl.mmapx*cpl.mmapy);
2359     /* Order the server puts it in should be just fine.  Note that the only
2360      * requirement that this works is that magicmap by 8 bits, being that is
2361      * the size specified in the protocol and what the server sends us.
2362      */
2363     memcpy(cpl.magicmap, cp, cpl.mmapx*cpl.mmapy);
2364     draw_magic_map();
2365 }
2366 
2367 /**
2368  * @} */ /* EndOf SCMapCommands
2369  */
2370 
2371 /**
2372  *
2373  * @param data
2374  * @param len
2375  */
SinkCmd(unsigned char * data,int len)2376 void SinkCmd(unsigned char *data, int len)
2377 {
2378 }
2379 
2380 /**
2381  * Got a tick from the server.  We currently don't care what tick number it
2382  * is, but just have the code in case at some time we do.
2383  *
2384  * @param data
2385  * @param len
2386  */
TickCmd(guint8 * data,int len)2387 void TickCmd(guint8 *data, int len)
2388 {
2389     /* Up to the specific client to decide what to do */
2390     client_tick(GetInt_String(data));
2391 }
2392 
2393 /**
2394  * Server gives us current player's pickup.
2395  *
2396  * @param data
2397  * buffer sent by server.
2398  * @param len
2399  * length of data.
2400  */
PickupCmd(guint8 * data,int len)2401 void PickupCmd(guint8 *data, int len)
2402 {
2403     guint32 pickup = GetInt_String(data);
2404     client_pickup(pickup);
2405 }
2406 
2407 /**
2408  * Handles a failure return from the server.
2409  *
2410  * @param buf
2411  * buffer sent by server.
2412  * @param len
2413  * length of data.
2414  */
FailureCmd(char * buf,int len)2415 void FailureCmd(char *buf, int len)
2416 {
2417     char *cp;
2418 
2419     /* The format of the buffer is 'command error message'.  We need to
2420      * extract the failed command, and then pass in the error message to the
2421      * appropriate handler.  So find the space, set it to null.  in that way,
2422      * buf is now just the failure command, and cp is the message.
2423      */
2424     cp = strchr(buf,' ');
2425     if (!cp) {
2426         return;
2427     }
2428 
2429     *cp = 0;
2430     cp++;
2431 
2432     if (!strcmp(buf,"accountlogin")) {
2433         account_login_failure(cp);
2434     } else if (!strcmp(buf,"accountnew")) {
2435         account_creation_failure(cp);
2436     } else if (!strcmp(buf,"accountaddplayer")) {
2437         account_add_character_failure(cp);
2438     } else if (!strcmp(buf,"createplayer")) {
2439         create_new_character_failure(cp);
2440     } else if (!strcmp(buf, "accountpw")) {
2441         account_change_password_failure(cp);
2442     } else if (!strcmp(buf, "accountplay")) {
2443         // This creates a dialog that says the failure message.
2444         // It should suffice for what we want here anyway.
2445         create_new_character_failure(cp);
2446     } else
2447         /* This really is an error - if this happens it menas the server
2448          * failed to process a request that the client made - the client
2449          * should be able to handle failures for all request types it makes.
2450          * But this is also a problem in that it means that the server is
2451          * waiting for a correct response, and if we do not display anything,
2452          * the player is unlikely to know this.
2453          */
2454         LOG(LOG_ERROR, "common::FailureCmd", "Got a failure response we can not handle: %s:%s",
2455             buf, cp);
2456 }
2457 
2458 /**
2459  * This handles the accountplayers command
2460  */
AccountPlayersCmd(char * buf,int len)2461 void AccountPlayersCmd(char *buf, int len) {
2462     int level, pos, faceno;
2463     guint8 flen;
2464     char name[MAX_BUF], class[MAX_BUF], race[MAX_BUF],
2465             face[MAX_BUF], party[MAX_BUF], map[MAX_BUF];
2466 
2467     /* This is called first so it can clear out the existing data store.
2468      */
2469     choose_character_init();
2470 
2471     level=0;
2472     name[0]=0;
2473     class[0]=0;
2474     race[0]=0;
2475     face[0]=0;
2476     party[0]=0;
2477     map[0]=0;
2478     faceno=0;
2479 
2480     pos=1;
2481     while (pos < len) {
2482         flen = buf[pos];
2483         /* flen == 0 is to note that we got end of character data */
2484         if (flen == 0) {
2485             update_character_choose(name, class, race, face, party, map, level, faceno);
2486             /* Blank all the values - it is no sure thing that the next
2487              * character will fill all these in.
2488              */
2489             level=0;
2490             name[0]=0;
2491             class[0]=0;
2492             race[0]=0;
2493             face[0]=0;
2494             party[0]=0;
2495             map[0]=0;
2496             faceno=0;
2497             pos++;
2498             continue;
2499         }
2500         pos++;
2501         if ((pos +flen) > len || flen>=MAX_BUF) {
2502             LOG(LOG_ERROR,"commands.c:AccountPlayerCmd", "data overran buffer");
2503             return;
2504         }
2505         switch (buf[pos]) {
2506         case ACL_NAME:
2507             strncpy(name, buf + pos +1, flen-1);
2508             name[flen-1] = 0;
2509             break;
2510 
2511         case ACL_CLASS:
2512             strncpy(class, buf + pos +1, flen-1);
2513             class[flen-1] = 0;
2514             break;
2515 
2516         case ACL_RACE:
2517             strncpy(race, buf + pos +1, flen-1);
2518             race[flen-1] = 0;
2519             break;
2520 
2521         case ACL_FACE:
2522             strncpy(face, buf + pos +1, flen-1);
2523             face[flen-1] = 0;
2524             break;
2525 
2526         case ACL_PARTY:
2527             strncpy(party, buf + pos +1, flen-1);
2528             party[flen-1] = 0;
2529             break;
2530 
2531         case ACL_MAP:
2532             strncpy(map, buf + pos +1, flen-1);
2533             map[flen-1] = 0;
2534             break;
2535 
2536         case ACL_LEVEL:
2537             level = GetShort_String((unsigned char *)buf + pos + 1);
2538             break;
2539         case ACL_FACE_NUM:
2540             faceno = GetShort_String((unsigned char *)buf + pos + 1);
2541             break;
2542         }
2543         pos += flen;
2544     }
2545 }
2546 
2547