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