Don't prime the display filter unless we're re-applying it; otherwise,
[obnox/wireshark/wip.git] / packet-quakeworld.c
1 /* packet-quakeworld.c
2  * Routines for QuakeWorld packet dissection
3  *
4  * Uwe Girlich <uwe@planetquake.com>
5  *      http://www.idsoftware.com/q1source/q1source.zip
6  *
7  * $Id: packet-quakeworld.c,v 1.13 2002/04/14 23:04:04 guy Exp $
8  *
9  * Ethereal - Network traffic analyzer
10  * By Gerald Combs <gerald@ethereal.com>
11  * Copyright 1998 Gerald Combs
12  *
13  * Copied from packet-quake.c
14  * 
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  * 
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  * 
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28  */
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #ifdef HAVE_SYS_TYPES_H
35 # include <sys/types.h>
36 #endif
37
38 #include <glib.h>
39 #include <string.h>
40 #include <epan/packet.h>
41 #include "prefs.h"
42
43 static int proto_quakeworld = -1;
44
45 static int hf_quakeworld_s2c = -1;
46 static int hf_quakeworld_c2s = -1;
47 static int hf_quakeworld_connectionless = -1;
48 static int hf_quakeworld_game = -1;
49 static int hf_quakeworld_connectionless_marker = -1;
50 static int hf_quakeworld_connectionless_text = -1;
51 static int hf_quakeworld_connectionless_command = -1;
52 static int hf_quakeworld_connectionless_arguments = -1;
53 static int hf_quakeworld_connectionless_connect_version = -1;
54 static int hf_quakeworld_connectionless_connect_qport = -1;
55 static int hf_quakeworld_connectionless_connect_challenge = -1;
56 static int hf_quakeworld_connectionless_connect_infostring = -1;
57 static int hf_quakeworld_connectionless_connect_infostring_key_value = -1;
58 static int hf_quakeworld_connectionless_connect_infostring_key = -1;
59 static int hf_quakeworld_connectionless_connect_infostring_value = -1;
60 static int hf_quakeworld_connectionless_rcon_password = -1;
61 static int hf_quakeworld_connectionless_rcon_command = -1;
62 static int hf_quakeworld_game_seq1 = -1;
63 static int hf_quakeworld_game_rel1 = -1;
64 static int hf_quakeworld_game_seq2 = -1;
65 static int hf_quakeworld_game_rel2 = -1;
66 static int hf_quakeworld_game_qport = -1;
67
68 static gint ett_quakeworld = -1;
69 static gint ett_quakeworld_connectionless = -1;
70 static gint ett_quakeworld_connectionless_text = -1;
71 static gint ett_quakeworld_connectionless_arguments = -1;
72 static gint ett_quakeworld_connectionless_connect_infostring = -1;
73 static gint ett_quakeworld_connectionless_connect_infostring_key_value = -1;
74 static gint ett_quakeworld_game = -1;
75 static gint ett_quakeworld_game_seq1 = -1;
76 static gint ett_quakeworld_game_seq2 = -1;
77 static gint ett_quakeworld_game_clc = -1;
78 static gint ett_quakeworld_game_svc = -1;
79
80 static dissector_handle_t data_handle;
81
82 /*
83    helper functions, they may ave to go somewhere else
84    they are mostly copied without change from
85         quakeworldsource/client/cmd.c
86         quakeworldsource/client/common.c
87 */
88
89 static  char            com_token[1024];
90 static  int             com_token_start;
91 static  int             com_token_length;
92
93 static char *
94 COM_Parse (char *data)
95 {
96         int     c;
97         int     len;
98
99         len = 0;
100         com_token[0] = 0;
101         com_token_start = 0;
102         com_token_length = 0;
103
104         if (data == NULL)
105                 return NULL;
106
107         /* skip whitespace */
108 skipwhite:
109         while ( (c = *data) <= ' ') {
110                 if (c == 0)
111                         return NULL;    /* end of file; */
112                 data++;
113                 com_token_start++;
114         }
115
116         /* skip // comments */
117         if (c=='/' && data[1] == '/') {
118                 while (*data && *data != '\n')
119                         data++;
120                         com_token_start++;
121                 goto skipwhite;
122         }
123
124         /* handle quoted strings specially */
125         if (c == '\"') {
126                 data++;
127                 com_token_start++;
128                 while (1) {
129                         c = *data++;
130                         if (c=='\"' || c==0) {
131                                 com_token[len] = 0;
132                                 return data;
133                         }
134                         com_token[len] = c;
135                         len++;
136                         com_token_length++;
137                 }
138         }
139
140         /* parse a regular word */
141         do {
142                 com_token[len] = c;
143                 data++;
144                 len++;
145                 com_token_length++;
146                 c = *data;
147         } while (c>32);
148
149         com_token[len] = 0;
150         return data;
151 }
152
153
154 #define                 MAX_ARGS 80
155 static  int             cmd_argc = 0;
156 static  char            *cmd_argv[MAX_ARGS];
157 static  char            *cmd_null_string = "";
158 static  int             cmd_argv_start[MAX_ARGS];
159 static  int             cmd_argv_length[MAX_ARGS];
160
161
162
163 static int
164 Cmd_Argc(void)
165 {
166         return cmd_argc;
167 }
168
169
170 static char*
171 Cmd_Argv(int arg)
172 {
173         if ( arg >= cmd_argc )
174                 return cmd_null_string;
175         return cmd_argv[arg];
176 }
177
178
179 static int
180 Cmd_Argv_start(int arg)
181 {
182         if ( arg >= cmd_argc )
183                 return 0;
184         return cmd_argv_start[arg];
185 }
186
187
188 static int
189 Cmd_Argv_length(int arg)
190 {
191         if ( arg >= cmd_argc )
192                 return 0;
193         return cmd_argv_length[arg];
194 }
195
196
197 static void
198 Cmd_TokenizeString(char* text)
199 {
200         int i;
201         int start;
202         int length;
203         
204
205         /* clear the args from the last string */
206         for (i=0 ; i<cmd_argc ; i++)
207                 g_free(cmd_argv[i]);
208
209         cmd_argc = 0;
210
211         start = 0;
212         while (TRUE) {
213
214                 /* skip whitespace up to a \n */
215                 while (*text && *text <= ' ' && *text != '\n') {
216                         text++;
217                         start++;
218                 }
219
220                 length = 0;
221
222                 if (*text == '\n') {
223                         /* a newline seperates commands in the buffer */
224                         text++;
225                         break;
226                 }
227
228                 if (!*text)
229                         return;
230                         
231                 text = COM_Parse (text);
232                 if (!text)
233                         return;
234
235                 if (cmd_argc < MAX_ARGS) {
236                         cmd_argv[cmd_argc] = g_strdup(com_token);
237                         cmd_argv_start[cmd_argc] = start + com_token_start;
238                         cmd_argv_length[cmd_argc] = com_token_length;
239                         cmd_argc++;
240                 }
241
242                 start += com_token_start + com_token_length;
243         }
244 }
245
246                         
247 static void
248 dissect_id_infostring(tvbuff_t *tvb, proto_tree* tree, 
249         int offset, char* infostring,
250         gint ett_key_value, int hf_key_value, int hf_key, int hf_value)
251 {
252         char * newpos = infostring;
253         int end_of_info = FALSE;
254
255         /* to look at all the key/value pairs, we destroy infostring */
256         while(!end_of_info) {
257                 char* keypos;
258                 char* valuepos;
259                 int keylength;
260                 char* keyvaluesep;
261                 int valuelength;
262                 char* valueend;
263
264                 keypos = newpos;
265                 if (*keypos == 0) break;
266                 if (*keypos == '\\') keypos++;
267
268                 for (keylength = 0
269                         ;
270                         *(keypos + keylength) != '\\' && 
271                         *(keypos + keylength) != 0
272                         ;
273                         keylength++) ;
274                 keyvaluesep = keypos + keylength;
275                 if (*keyvaluesep == 0) break;
276                 valuepos = keyvaluesep+1;
277                 for (valuelength = 0
278                         ;
279                         *(valuepos + valuelength) != '\\' && 
280                         *(valuepos + valuelength) != 0
281                         ;
282                         valuelength++) ;
283                 valueend = valuepos + valuelength;
284                 if (*valueend == 0) {
285                         end_of_info = TRUE;
286                 }
287                 *(keyvaluesep) = '=';
288                 *(valueend) = 0;
289                 
290                 if (tree) {
291                         proto_item* sub_item = NULL;
292                         proto_tree* sub_tree = NULL;
293
294                         sub_item = proto_tree_add_string(tree,
295                                 hf_key_value,
296                                 tvb,
297                                 offset + (keypos-infostring),
298                                 keylength + 1 + valuelength, keypos);
299                         if (sub_item) 
300                                 sub_tree =
301                                         proto_item_add_subtree(
302                                         sub_item,
303                                         ett_key_value);
304                         *(keyvaluesep) = 0;
305                         if (sub_tree) {
306                                 proto_tree_add_string(sub_tree,
307                                         hf_key,
308                                         tvb,
309                                         offset + (keypos-infostring),
310                                         keylength, keypos);
311                                 proto_tree_add_string(sub_tree,
312                                         hf_value,
313                                         tvb,
314                                         offset + (valuepos-infostring),
315                                         valuelength, valuepos);
316                         }
317                 }
318                 newpos = valueend + 1;
319         }
320 }
321
322
323 static const value_string names_direction[] = {
324 #define DIR_C2S 0
325         { DIR_C2S, "Client to Server" },
326 #define DIR_S2C 1
327         { DIR_S2C, "Server to Client" },
328         { 0, NULL }
329 };
330
331
332 /* I took this name and value directly out of the QW source. */
333 #define PORT_MASTER 27500
334 static unsigned int gbl_quakeworldServerPort=PORT_MASTER;
335
336
337 /* out of band message id bytes (taken out of quakeworldsource/client/protocol.h */
338  
339 /* M = master, S = server, C = client, A = any */
340 /* the second character will allways be \n if the message isn't a single */
341 /* byte long (?? not true anymore?) */
342  
343 #define S2C_CHALLENGE           'c'
344 #define S2C_CONNECTION          'j'
345 #define A2A_PING                'k'     /* respond with an A2A_ACK */
346 #define A2A_ACK                 'l'     /* general acknowledgement without info */
347 #define A2A_NACK                'm'     /* [+ comment] general failure */
348 #define A2A_ECHO                'e'     /* for echoing */
349 #define A2C_PRINT               'n'     /* print a message on client */
350  
351 #define S2M_HEARTBEAT           'a'     /* + serverinfo + userlist + fraglist */
352 #define A2C_CLIENT_COMMAND      'B'     /* + command line */
353 #define S2M_SHUTDOWN            'C'
354
355
356 static void
357 dissect_quakeworld_ConnectionlessPacket(tvbuff_t *tvb, packet_info *pinfo,
358         proto_tree *tree, int direction)
359 {
360         proto_tree      *cl_tree = NULL;
361         proto_item      *cl_item = NULL;
362         proto_item      *text_item = NULL;
363         proto_tree      *text_tree = NULL;
364         guint8          text[2048];
365         int             maxbufsize = 0;
366         int             len;
367         int             offset;
368         guint32 marker;
369         int command_len;
370         char command[2048];
371         int command_finished = FALSE;
372
373         marker = tvb_get_ntohl(tvb, 0);
374         if (tree) {
375                 cl_item = proto_tree_add_text(tree, tvb,
376                                 0, -1, "Connectionless");
377                 if (cl_item)
378                         cl_tree = proto_item_add_subtree(
379                                 cl_item, ett_quakeworld_connectionless);
380         }
381
382         if (cl_tree) {
383                 proto_tree_add_uint(cl_tree, hf_quakeworld_connectionless_marker,
384                                 tvb, 0, 4, marker);
385         }
386
387         /* all the rest of the packet is just text */
388         offset = 4;
389
390         maxbufsize = MIN((gint)sizeof(text), tvb_length_remaining(tvb, offset));
391         len = tvb_get_nstringz0(tvb, offset, maxbufsize, text);
392         /* actually, we should look for a eol char and stop already there */
393
394         if (cl_tree) {
395                 text_item = proto_tree_add_string(cl_tree, hf_quakeworld_connectionless_text,
396                         tvb, offset, len + 1, text);
397                 if (text_item) {
398                         text_tree = proto_item_add_subtree(
399                                 text_item, ett_quakeworld_connectionless_text);
400                 }
401         }
402
403         if (direction == DIR_C2S) {
404                 /* client to sever commands */
405                 char *c;
406
407                 Cmd_TokenizeString(text);
408                 c = Cmd_Argv(0);
409                 
410                 /* client to sever commands */
411                 if (strcmp(c,"ping") == 0) {
412                         strcpy(command, "Ping");
413                         command_len = 4;
414                 } else if (strcmp(c,"status") == 0) {
415                         strcpy(command, "Status");
416                         command_len = 6;
417                 } else if (strcmp(c,"log") == 0) {
418                         strcpy(command, "Status");
419                         command_len = 3;
420                 } else if (strcmp(c,"connect") == 0) {
421                         int version;
422                         int qport;
423                         int challenge;
424                         char *infostring;
425                         proto_item *argument_item = NULL;
426                         proto_tree *argument_tree = NULL;
427                         proto_item *info_item = NULL;
428                         proto_tree *info_tree = NULL;
429                         strcpy(command, "Connect");
430                         command_len = Cmd_Argv_length(0);
431                         if (text_tree) {
432                                 proto_tree_add_string(text_tree, hf_quakeworld_connectionless_command,
433                                         tvb, offset, command_len, command);
434                                 argument_item = proto_tree_add_string(text_tree,
435                                         hf_quakeworld_connectionless_arguments,
436                                         tvb, offset + Cmd_Argv_start(1), len + 1 - Cmd_Argv_start(1), 
437                                         text + Cmd_Argv_start(1));
438                                 if (argument_item) {
439                                         argument_tree =
440                                                 proto_item_add_subtree(argument_item,   
441                                                         ett_quakeworld_connectionless_arguments);
442                                 }
443                                 command_finished=TRUE;
444                         }
445                         version = atoi(Cmd_Argv(1));
446                         qport = atoi(Cmd_Argv(2));
447                         challenge = atoi(Cmd_Argv(3));
448                         infostring = Cmd_Argv(4);
449                         if (argument_tree) {
450                                 proto_tree_add_uint(argument_tree,
451                                         hf_quakeworld_connectionless_connect_version,
452                                         tvb,
453                                         offset + Cmd_Argv_start(1),
454                                         Cmd_Argv_length(1), version);
455                                 proto_tree_add_uint(argument_tree,
456                                         hf_quakeworld_connectionless_connect_qport,
457                                         tvb,
458                                         offset + Cmd_Argv_start(2),
459                                         Cmd_Argv_length(2), qport);
460                                 proto_tree_add_int(argument_tree,
461                                         hf_quakeworld_connectionless_connect_challenge,
462                                         tvb,
463                                         offset + Cmd_Argv_start(3),
464                                         Cmd_Argv_length(3), challenge);
465                                 info_item = proto_tree_add_string(argument_tree,
466                                         hf_quakeworld_connectionless_connect_infostring,
467                                         tvb,
468                                         offset + Cmd_Argv_start(4),
469                                         Cmd_Argv_length(4), infostring);
470                                 if (info_item)
471                                         info_tree = proto_item_add_subtree(
472                                                 info_item, ett_quakeworld_connectionless_connect_infostring);
473                                 dissect_id_infostring(tvb, info_tree, offset + Cmd_Argv_start(4),
474                                         infostring,
475                                         ett_quakeworld_connectionless_connect_infostring_key_value,
476                                         hf_quakeworld_connectionless_connect_infostring_key_value,
477                                         hf_quakeworld_connectionless_connect_infostring_key,
478                                         hf_quakeworld_connectionless_connect_infostring_value);
479                         }
480                 } else if (strcmp(c,"getchallenge") == 0) {
481                         strcpy(command, "Get Challenge");
482                         command_len = Cmd_Argv_length(0);
483                 } else if (strcmp(c,"rcon") == 0) {
484                         char* password;
485                         int i;
486                         char remaining[1024];
487                         proto_item *argument_item = NULL;
488                         proto_tree *argument_tree = NULL;
489                         strcpy(command, "Remote Command");
490                         command_len = Cmd_Argv_length(0);
491                         if (text_tree) {
492                                 proto_tree_add_string(text_tree, hf_quakeworld_connectionless_command,
493                                         tvb, offset, command_len, command);
494                                 argument_item = proto_tree_add_string(text_tree,
495                                         hf_quakeworld_connectionless_arguments,
496                                         tvb, offset + Cmd_Argv_start(1), len + 1 - Cmd_Argv_start(1), 
497                                         text + Cmd_Argv_start(1));
498                                 if (argument_item) {
499                                         argument_tree =
500                                                 proto_item_add_subtree(argument_item,   
501                                                         ett_quakeworld_connectionless_arguments);
502                                 }
503                                 command_finished=TRUE;
504                         }
505                         password = Cmd_Argv(1);
506                         if (argument_tree) {
507                                 proto_tree_add_string(argument_tree,
508                                         hf_quakeworld_connectionless_rcon_password,
509                                         tvb,
510                                         offset + Cmd_Argv_start(1),
511                                         Cmd_Argv_length(1), password);
512                         }
513                         for (i=2; i<Cmd_Argc() ; i++) {
514                                 remaining[0] = 0;
515                                 strcat (remaining, Cmd_Argv(i) );
516                                 strcat (remaining, " ");
517                         }
518                         if (text_tree) {
519                                 proto_tree_add_string(argument_tree,
520                                         hf_quakeworld_connectionless_rcon_command,
521                                         tvb, offset + Cmd_Argv_start(2),
522                                         Cmd_Argv_start(Cmd_Argc()-1) + Cmd_Argv_length(Cmd_Argc()-1) -
523                                         Cmd_Argv_start(2),
524                                         remaining);
525                         }
526                 } else if (c[0]==A2A_PING && ( c[1]==0 || c[1]=='\n')) {
527                         strcpy(command, "Ping");
528                         command_len = 1;
529                 } else if (c[0]==A2A_ACK && ( c[1]==0 || c[1]=='\n')) {
530                         strcpy(command, "Ack");
531                         command_len = 1;
532                 } else {
533                         strcpy(command, "Unknown");
534                         command_len = len;
535                 }
536         }
537         else {
538                 /* server to client commands */
539                 if (text[0] == S2C_CONNECTION) {
540                         strcpy(command, "Connected");
541                         command_len = 1;
542                 } else if (text[0] == A2C_CLIENT_COMMAND) {
543                         strcpy(command, "Client Command");
544                         command_len = 1;
545                         /* stringz (command), stringz (localid) */
546                 } else if (text[0] == A2C_PRINT) {
547                         strcpy(command, "Print");
548                         command_len = 1;
549                         /* string */
550                 } else if (text[0] == A2A_PING) {
551                         strcpy(command, "Ping");
552                         command_len = 1;
553                 } else if (text[0] == S2C_CHALLENGE) {
554                         strcpy(command, "Challenge");
555                         command_len = 1;
556                         /* string, atoi */
557                 } else {
558                         strcpy(command, "Unknown");
559                         command_len = len;
560                 }
561         }
562                 
563         if (check_col(pinfo->cinfo, COL_INFO)) {
564                 col_append_fstr(pinfo->cinfo, COL_INFO, " %s", command);
565         }
566                 
567         if (text_tree && !command_finished) {
568                 proto_tree_add_string(text_tree, hf_quakeworld_connectionless_command,
569                         tvb, offset, command_len, command);
570         }
571         offset += len + 1;
572 }
573
574
575 static void
576 dissect_quakeworld_client_commands(tvbuff_t *tvb, packet_info *pinfo,
577         proto_tree *tree)
578 {
579         /* If I have too much time at hand, I'll fill it with all
580            the information from my QWD specs:
581                 http://www.planetquake.com/demospecs/qwd/
582         */
583         call_dissector(data_handle,tvb, pinfo, tree);
584 }
585
586
587 static void
588 dissect_quakeworld_server_commands(tvbuff_t *tvb, packet_info *pinfo,
589         proto_tree *tree)
590 {
591         /* If I have too much time at hand, I'll fill it with all
592            the information from my QWD specs:
593                 http://www.planetquake.com/demospecs/qwd/
594         */
595         call_dissector(data_handle,tvb, pinfo, tree);
596 }
597
598
599 static const value_string names_reliable[] = {
600         { 0, "Non Reliable" },
601         { 1, "Reliable" },
602         { 0, NULL }
603 };
604
605
606 static void
607 dissect_quakeworld_GamePacket(tvbuff_t *tvb, packet_info *pinfo,
608         proto_tree *tree, int direction)
609 {
610         proto_tree      *game_tree = NULL;
611         proto_item      *game_item = NULL;
612         guint32 seq1;
613         guint32 seq2;
614         int rel1;
615         int rel2;
616         int offset;
617         guint           rest_length;
618
619         direction = (pinfo->destport == gbl_quakeworldServerPort) ?
620                         DIR_C2S : DIR_S2C;
621
622         if (tree) {
623                 game_item = proto_tree_add_text(tree, tvb,
624                                 0, -1, "Game");
625                 if (game_item)
626                         game_tree = proto_item_add_subtree(
627                                 game_item, ett_quakeworld_game);
628         }
629
630         offset = 0;
631
632         seq1 = tvb_get_letohl(tvb, offset);
633         rel1 = seq1 & 0x80000000 ? 1 : 0;
634         seq1 &= ~0x80000000;
635         if (game_tree) {
636                 proto_item *seq1_item = proto_tree_add_text(game_tree,
637                         tvb, offset, 4, "Current Sequence: %u (%s)",
638                         seq1, val_to_str(rel1,names_reliable,"%u"));
639                 if (seq1_item) {
640                         proto_tree *seq1_tree = proto_item_add_subtree(
641                                 seq1_item, ett_quakeworld_game_seq1);
642                         proto_tree_add_uint(seq1_tree, hf_quakeworld_game_seq1,
643                                         tvb, offset, 4, seq1);
644                         proto_tree_add_boolean(seq1_tree, hf_quakeworld_game_rel1,
645                                         tvb, offset+3, 1, rel1);
646                 }
647         }
648         offset += 4;
649
650         seq2 = tvb_get_letohl(tvb, offset);
651         rel2 = seq2 & 0x80000000 ? 1 : 0;
652         seq2 &= ~0x80000000;
653         if (game_tree) {
654                 proto_item *seq2_item = proto_tree_add_text(game_tree,
655                         tvb, offset, 4, "Acknowledge Sequence: %u (%s)",
656                         seq2, val_to_str(rel2,names_reliable,"%u"));;
657                 if (seq2_item) {
658                         proto_tree *seq2_tree = proto_item_add_subtree(
659                                 seq2_item, ett_quakeworld_game_seq2);
660                         proto_tree_add_uint(seq2_tree, hf_quakeworld_game_seq2,
661                                         tvb, offset, 4, seq2);
662                         proto_tree_add_boolean(seq2_tree, hf_quakeworld_game_rel2,
663                                         tvb, offset+3, 1, rel2);
664                 }
665         }
666         offset += 4;
667
668         if (direction == DIR_C2S) {
669                 /* client to server */
670                 guint16 qport = tvb_get_letohs(tvb, offset);
671                 if (game_tree) {
672                         proto_tree_add_uint(game_tree, hf_quakeworld_game_qport, 
673                                 tvb, offset, 2, qport);
674                 }
675                 offset +=2;
676         }
677
678         /* all the rest is pure game data */
679         rest_length = tvb_reported_length(tvb) - offset;
680         if (rest_length) {
681                 tvbuff_t *next_tvb =
682                 tvb_new_subset(tvb, offset, rest_length , rest_length);
683
684                 if (direction == DIR_C2S) {
685                         proto_item *c_item = NULL;
686                         proto_tree *c_tree = NULL;
687                         if (tree) {
688                                 c_item = proto_tree_add_text(game_tree, next_tvb,
689                                 0, -1, "Client Commands");
690                                 if (c_item) {
691                                         c_tree = proto_item_add_subtree(
692                                                 c_item, ett_quakeworld_game_clc);
693                                 }
694                         }
695                         dissect_quakeworld_client_commands(next_tvb, pinfo, c_tree);
696                 }
697                 else {
698                         proto_item *c_item = NULL;
699                         proto_tree *c_tree = NULL;
700                         if (tree) {
701                                 c_item = proto_tree_add_text(game_tree, next_tvb,
702                                 0, -1, "Server Commands");
703                                 if (c_item) {
704                                         c_tree = proto_item_add_subtree(
705                                         c_item, ett_quakeworld_game_svc);
706                                 }
707                         }
708                         dissect_quakeworld_server_commands(next_tvb, pinfo, c_tree);
709                 }
710         }
711 }
712
713
714 static void
715 dissect_quakeworld(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
716 {
717         proto_tree      *quakeworld_tree = NULL;
718         proto_item      *quakeworld_item = NULL;
719         int             direction;
720
721         direction = (pinfo->destport == gbl_quakeworldServerPort) ?
722                         DIR_C2S : DIR_S2C;
723
724         if (check_col(pinfo->cinfo, COL_PROTOCOL))
725                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "QUAKEWORLD");
726         if (check_col(pinfo->cinfo, COL_INFO))
727                 col_set_str(pinfo->cinfo, COL_INFO, val_to_str(direction,
728                         names_direction, "%u"));
729
730         if (tree) {
731                 quakeworld_item = proto_tree_add_item(tree, proto_quakeworld,
732                                 tvb, 0, -1, FALSE);
733                 if (quakeworld_item)
734                         quakeworld_tree = proto_item_add_subtree(
735                                 quakeworld_item, ett_quakeworld);
736                         if (quakeworld_tree) {
737                                 proto_tree_add_uint_format(quakeworld_tree,
738                                         direction == DIR_S2C ?
739                                         hf_quakeworld_s2c :
740                                         hf_quakeworld_c2s,
741                                         tvb, 0, 0, 1,
742                                         "Direction: %s", val_to_str(direction, names_direction, "%u"));
743                         }
744         }
745
746         if (tvb_get_ntohl(tvb, 0) == 0xffffffff) {
747                 if (check_col(pinfo->cinfo, COL_INFO)) {
748                         col_append_str(pinfo->cinfo, COL_INFO, " Connectionless");
749                 }
750                 if (quakeworld_tree)
751                         proto_tree_add_uint_format(quakeworld_tree,
752                                 hf_quakeworld_connectionless,
753                                 tvb, 0, 0, 1,
754                                 "Type: Connectionless");
755                 dissect_quakeworld_ConnectionlessPacket(
756                         tvb, pinfo, quakeworld_tree, direction);
757         }
758         else {
759                 if (check_col(pinfo->cinfo, COL_INFO)) {
760                         col_append_str(pinfo->cinfo, COL_INFO, " Game");
761                 }
762                 if (quakeworld_tree)
763                         proto_tree_add_uint_format(quakeworld_tree,
764                                 hf_quakeworld_game,
765                                 tvb, 0, 0, 1,
766                                 "Type: Game");
767                 dissect_quakeworld_GamePacket(
768                         tvb, pinfo, quakeworld_tree, direction);
769         }
770 }
771
772
773 void
774 proto_reg_handoff_quakeworld(void)
775 {
776         static int Initialized=FALSE;
777         static dissector_handle_t quakeworld_handle;
778         static int ServerPort=0;
779  
780         if (!Initialized) {
781                 quakeworld_handle = create_dissector_handle(dissect_quakeworld,
782                                 proto_quakeworld);
783                 Initialized=TRUE;
784         } else {
785                 dissector_delete("udp.port", ServerPort, quakeworld_handle);
786         }
787  
788         /* set port for future deletes */
789         ServerPort=gbl_quakeworldServerPort;
790  
791         dissector_add("udp.port", gbl_quakeworldServerPort, quakeworld_handle);
792         data_handle = find_dissector("data");
793 }
794
795
796 void
797 proto_register_quakeworld(void)
798 {
799         static hf_register_info hf[] = {
800                 { &hf_quakeworld_c2s,
801                         { "Client to Server", "quakeworld.c2s",
802                         FT_UINT32, BASE_DEC, NULL, 0x0,
803                         "Client to Server", HFILL }},
804                 { &hf_quakeworld_s2c,
805                         { "Server to Client", "quakeworld.s2c",
806                         FT_UINT32, BASE_DEC, NULL, 0x0,
807                         "Server to Client", HFILL }},
808                 { &hf_quakeworld_connectionless,
809                         { "Connectionless", "quakeworld.connectionless",
810                         FT_UINT32, BASE_DEC, NULL, 0x0,
811                         "Connectionless", HFILL }},
812                 { &hf_quakeworld_game,
813                         { "Game", "quakeworld.game",
814                         FT_UINT32, BASE_DEC, NULL, 0x0,
815                         "Game", HFILL }},
816                 { &hf_quakeworld_connectionless_marker,
817                         { "Marker", "quakeworld.connectionless.marker",
818                         FT_UINT32, BASE_HEX, NULL, 0x0,
819                         "Marker", HFILL }},
820                 { &hf_quakeworld_connectionless_text,
821                         { "Text", "quakeworld.connectionless.text",
822                         FT_STRING, BASE_DEC, NULL, 0x0,
823                         "Text", HFILL }},
824                 { &hf_quakeworld_connectionless_command,
825                         { "Command", "quakeworld.connectionless.command",
826                         FT_STRING, BASE_DEC, NULL, 0x0,
827                         "Command", HFILL }},
828                 { &hf_quakeworld_connectionless_arguments,
829                         { "Arguments", "quakeworld.connectionless.arguments",
830                         FT_STRING, BASE_DEC, NULL, 0x0,
831                         "Arguments", HFILL }},
832                 { &hf_quakeworld_connectionless_connect_version,
833                         { "Version", "quakeworld.connectionless.connect.version",
834                         FT_UINT32, BASE_DEC, NULL, 0x0,
835                         "Protocol Version", HFILL }},
836                 { &hf_quakeworld_connectionless_connect_qport,
837                         { "QPort", "quakeworld.connectionless.connect.qport",
838                         FT_UINT32, BASE_DEC, NULL, 0x0,
839                         "QPort of the client", HFILL }},
840                 { &hf_quakeworld_connectionless_connect_challenge,
841                         { "Challenge", "quakeworld.connectionless.connect.challenge",
842                         FT_INT32, BASE_DEC, NULL, 0x0,
843                         "Challenge from the server", HFILL }},
844                 { &hf_quakeworld_connectionless_connect_infostring,
845                         { "Infostring", "quakeworld.connectionless.connect.infostring",
846                         FT_STRING, BASE_DEC, NULL, 0x0,
847                         "Infostring with additional variables", HFILL }},
848                 { &hf_quakeworld_connectionless_connect_infostring_key_value,
849                         { "Key/Value", "quakeworld.connectionless.connect.infostring.key_value",
850                         FT_STRING, BASE_DEC, NULL, 0x0,
851                         "Key and Value", HFILL }},
852                 { &hf_quakeworld_connectionless_connect_infostring_key,
853                         { "Key", "quakeworld.connectionless.connect.infostring.key",
854                         FT_STRING, BASE_DEC, NULL, 0x0,
855                         "Infostring Key", HFILL }},
856                 { &hf_quakeworld_connectionless_connect_infostring_value,
857                         { "Value", "quakeworld.connectionless.connect.infostring.value",
858                         FT_STRING, BASE_DEC, NULL, 0x0,
859                         "Infostring Value", HFILL }},
860                 { &hf_quakeworld_connectionless_rcon_password,
861                         { "Password", "quakeworld.connectionless.rcon.password",
862                         FT_STRING, BASE_DEC, NULL, 0x0,
863                         "Rcon Password", HFILL }},
864                 { &hf_quakeworld_connectionless_rcon_command,
865                         { "Command", "quakeworld.connectionless.rcon.command",
866                         FT_STRING, BASE_DEC, NULL, 0x0,
867                         "Command", HFILL }},
868                 { &hf_quakeworld_game_seq1,
869                         { "Sequence Number", "quakeworld.game.seq1",
870                         FT_UINT32, BASE_DEC, NULL, 0x0,
871                         "Sequence number of the current packet", HFILL }},
872                 { &hf_quakeworld_game_rel1,
873                         { "Reliable", "quakeworld.game.rel1",
874                         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
875                         "Packet is reliable and may be retransmitted", HFILL }},
876                 { &hf_quakeworld_game_seq2,
877                         { "Sequence Number", "quakeworld.game.seq2",
878                         FT_UINT32, BASE_DEC, NULL, 0x0,
879                         "Sequence number of the last received packet", HFILL }},
880                 { &hf_quakeworld_game_rel2,
881                         { "Reliable", "quakeworld.game.rel2",
882                         FT_BOOLEAN, BASE_DEC, NULL, 0x0,
883                         "Packet was reliable and may be retransmitted", HFILL }},
884                 { &hf_quakeworld_game_qport,
885                         { "QPort", "quakeworld.game.qport",
886                         FT_UINT32, BASE_DEC, NULL, 0x0,
887                         "QuakeWorld Client Port", HFILL }}
888         };
889         static gint *ett[] = {
890                 &ett_quakeworld,
891                 &ett_quakeworld_connectionless,
892                 &ett_quakeworld_connectionless_text,
893                 &ett_quakeworld_connectionless_arguments,
894                 &ett_quakeworld_connectionless_connect_infostring,
895                 &ett_quakeworld_connectionless_connect_infostring_key_value,
896                 &ett_quakeworld_game,
897                 &ett_quakeworld_game_seq1,
898                 &ett_quakeworld_game_seq2,
899                 &ett_quakeworld_game_clc,
900                 &ett_quakeworld_game_svc
901         };
902         module_t *quakeworld_module;
903
904         proto_quakeworld = proto_register_protocol("QuakeWorld Network Protocol",
905                                                 "QUAKEWORLD", "quakeworld");
906         proto_register_field_array(proto_quakeworld, hf, array_length(hf));
907         proto_register_subtree_array(ett, array_length(ett));
908
909         /* Register a configuration option for port */
910         quakeworld_module = prefs_register_protocol(proto_quakeworld,
911                 proto_reg_handoff_quakeworld);
912         prefs_register_uint_preference(quakeworld_module, "udp.port",
913                                         "QuakeWorld Server UDP Port",
914                                         "Set the UDP port for the QuakeWorld Server",
915                                         10, &gbl_quakeworldServerPort);
916 }
917