FIXUP: WIP: verification_trailer
[wireshark-wip.git] / epan / dissectors / packet-quakeworld.c
blob377cbaa5d974df7ff1a97cca9a95c7aec638ae65
1 /* packet-quakeworld.c
2 * Routines for QuakeWorld packet dissection
4 * Uwe Girlich <uwe@planetquake.com>
5 * http://www.idsoftware.com/q1source/q1source.zip
7 * $Id$
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * Copied from packet-quake.c
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include "config.h"
32 #include <glib.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <epan/packet.h>
37 #include <epan/prefs.h>
38 #include <epan/strutil.h>
39 #include <epan/wmem/wmem.h>
41 static int proto_quakeworld = -1;
43 static int hf_quakeworld_s2c = -1;
44 static int hf_quakeworld_c2s = -1;
45 static int hf_quakeworld_connectionless = -1;
46 static int hf_quakeworld_game = -1;
47 static int hf_quakeworld_connectionless_marker = -1;
48 static int hf_quakeworld_connectionless_text = -1;
49 static int hf_quakeworld_connectionless_command = -1;
50 static int hf_quakeworld_connectionless_arguments = -1;
51 static int hf_quakeworld_connectionless_connect_version = -1;
52 static int hf_quakeworld_connectionless_connect_qport = -1;
53 static int hf_quakeworld_connectionless_connect_challenge = -1;
54 static int hf_quakeworld_connectionless_connect_infostring = -1;
55 static int hf_quakeworld_connectionless_connect_infostring_key_value = -1;
56 static int hf_quakeworld_connectionless_connect_infostring_key = -1;
57 static int hf_quakeworld_connectionless_connect_infostring_value = -1;
58 static int hf_quakeworld_connectionless_rcon_password = -1;
59 static int hf_quakeworld_connectionless_rcon_command = -1;
60 static int hf_quakeworld_game_seq1 = -1;
61 static int hf_quakeworld_game_rel1 = -1;
62 static int hf_quakeworld_game_seq2 = -1;
63 static int hf_quakeworld_game_rel2 = -1;
64 static int hf_quakeworld_game_qport = -1;
66 static gint ett_quakeworld = -1;
67 static gint ett_quakeworld_connectionless = -1;
68 static gint ett_quakeworld_connectionless_text = -1;
69 static gint ett_quakeworld_connectionless_arguments = -1;
70 static gint ett_quakeworld_connectionless_connect_infostring = -1;
71 static gint ett_quakeworld_connectionless_connect_infostring_key_value = -1;
72 static gint ett_quakeworld_game = -1;
73 static gint ett_quakeworld_game_seq1 = -1;
74 static gint ett_quakeworld_game_seq2 = -1;
75 static gint ett_quakeworld_game_clc = -1;
76 static gint ett_quakeworld_game_svc = -1;
78 static dissector_handle_t data_handle;
81 helper functions, they may ave to go somewhere else
82 they are mostly copied without change from
83 quakeworldsource/client/cmd.c
84 quakeworldsource/client/common.c
87 #define MAX_TEXT_SIZE 2048
89 static char com_token[MAX_TEXT_SIZE+1];
90 static int com_token_start;
91 static int com_token_length;
93 static char *
94 COM_Parse (char *data)
96 int c;
97 int len;
99 len = 0;
100 com_token[0] = '\0';
101 com_token_start = 0;
102 com_token_length = 0;
104 if (data == NULL)
105 return NULL;
107 /* skip whitespace */
108 skipwhite:
109 while (TRUE) {
110 c = *data;
111 if (c == '\0')
112 return NULL; /* end of file; */
113 if ((c != ' ') && (!g_ascii_iscntrl(c)))
114 break;
115 data++;
116 com_token_start++;
119 /* skip // comments */
120 if ((c=='/') && (data[1]=='/')) {
121 while (*data && *data != '\n')
122 data++;
123 com_token_start++;
124 goto skipwhite;
127 /* handle quoted strings specially */
128 if (c == '\"') {
129 data++;
130 com_token_start++;
131 while (TRUE) {
132 c = *data++;
133 if ((c=='\"') || (c=='\0')) {
134 com_token[len] = '\0';
135 return data;
137 com_token[len] = c;
138 len++;
139 com_token_length++;
143 /* parse a regular word */
144 do {
145 com_token[len] = c;
146 data++;
147 len++;
148 com_token_length++;
149 c = *data;
150 } while (( c != ' ') && (!g_ascii_iscntrl(c)));
152 com_token[len] = '\0';
153 return data;
157 #define MAX_ARGS 80
158 static int cmd_argc = 0;
159 static char *cmd_argv[MAX_ARGS];
160 static const char *cmd_null_string = "";
161 static int cmd_argv_start[MAX_ARGS];
162 static int cmd_argv_length[MAX_ARGS];
166 static int
167 Cmd_Argc(void)
169 return cmd_argc;
173 static const char*
174 Cmd_Argv(int arg)
176 if ( arg >= cmd_argc )
177 return cmd_null_string;
178 return cmd_argv[arg];
182 static int
183 Cmd_Argv_start(int arg)
185 if ( arg >= cmd_argc )
186 return 0;
187 return cmd_argv_start[arg];
191 static int
192 Cmd_Argv_length(int arg)
194 if ( arg >= cmd_argc )
195 return 0;
196 return cmd_argv_length[arg];
200 static void
201 Cmd_TokenizeString(char* text)
203 int start;
205 cmd_argc = 0;
207 start = 0;
208 while (TRUE) {
210 /* skip whitespace up to a \n */
211 while (*text && *text <= ' ' && *text != '\n') {
212 text++;
213 start++;
216 if (*text == '\n') {
217 /* a newline seperates commands in the buffer */
218 text++;
219 break;
222 if (!*text)
223 return;
225 text = COM_Parse (text);
226 if (!text)
227 return;
229 if (cmd_argc < MAX_ARGS) {
230 cmd_argv[cmd_argc] = wmem_strdup(wmem_packet_scope(), com_token);
231 cmd_argv_start[cmd_argc] = start + com_token_start;
232 cmd_argv_length[cmd_argc] = com_token_length;
233 cmd_argc++;
236 start += com_token_start + com_token_length;
241 static void
242 dissect_id_infostring(tvbuff_t *tvb, proto_tree* tree,
243 int offset, char* infostring,
244 gint ett_key_value, int hf_key_value, int hf_key, int hf_value)
246 char *newpos = infostring;
247 gboolean end_of_info = FALSE;
249 /* to look at all the key/value pairs, we destroy infostring */
250 while(!end_of_info) {
251 char* keypos;
252 char* valuepos;
253 int keylength;
254 char* keyvaluesep;
255 int valuelength;
256 char* valueend;
258 keypos = newpos;
259 if (*keypos == '\0') break;
260 if (*keypos == '\\') keypos++;
262 for (keylength = 0
264 *(keypos + keylength) != '\\' &&
265 *(keypos + keylength) != '\0'
267 keylength++) ;
268 keyvaluesep = keypos + keylength;
269 if (*keyvaluesep == '\0') break;
270 valuepos = keyvaluesep+1;
271 for (valuelength = 0
273 *(valuepos + valuelength) != '\\' &&
274 *(valuepos + valuelength) != '\0'
276 valuelength++) ;
277 valueend = valuepos + valuelength;
278 if (*valueend == '\0') {
279 end_of_info = TRUE;
281 *(keyvaluesep) = '=';
282 *(valueend) = '\0';
284 if (tree) {
285 proto_item* sub_item;
286 proto_tree* sub_tree;
288 sub_item = proto_tree_add_string(tree,
289 hf_key_value,
290 tvb,
291 offset + (gint)(keypos-infostring),
292 keylength + 1 + valuelength, keypos);
293 sub_tree = proto_item_add_subtree(
294 sub_item,
295 ett_key_value);
296 *(keyvaluesep) = '\0';
297 proto_tree_add_string(sub_tree,
298 hf_key,
299 tvb,
300 offset + (gint)(keypos-infostring),
301 keylength, keypos);
302 proto_tree_add_string(sub_tree,
303 hf_value,
304 tvb,
305 offset + (gint)(valuepos-infostring),
306 valuelength, valuepos);
308 newpos = valueend + 1;
313 static const value_string names_direction[] = {
314 #define DIR_C2S 0
315 { DIR_C2S, "Client to Server" },
316 #define DIR_S2C 1
317 { DIR_S2C, "Server to Client" },
318 { 0, NULL }
322 /* I took this name and value directly out of the QW source. */
323 #define PORT_MASTER 27500
324 static guint gbl_quakeworldServerPort=PORT_MASTER;
327 /* out of band message id bytes (taken out of quakeworldsource/client/protocol.h */
329 /* M = master, S = server, C = client, A = any */
330 /* the second character will allways be \n if the message isn't a single */
331 /* byte long (?? not true anymore?) */
333 #define S2C_CHALLENGE 'c'
334 #define S2C_CONNECTION 'j'
335 #define A2A_PING 'k' /* respond with an A2A_ACK */
336 #define A2A_ACK 'l' /* general acknowledgement without info */
337 #define A2A_NACK 'm' /* [+ comment] general failure */
338 #define A2A_ECHO 'e' /* for echoing */
339 #define A2C_PRINT 'n' /* print a message on client */
341 #define S2M_HEARTBEAT 'a' /* + serverinfo + userlist + fraglist */
342 #define A2C_CLIENT_COMMAND 'B' /* + command line */
343 #define S2M_SHUTDOWN 'C'
346 static void
347 dissect_quakeworld_ConnectionlessPacket(tvbuff_t *tvb, packet_info *pinfo,
348 proto_tree *tree, int direction)
350 proto_tree *cl_tree = NULL;
351 proto_tree *text_tree = NULL;
352 guint8 text[MAX_TEXT_SIZE+1];
353 int len;
354 int offset;
355 guint32 marker;
356 int command_len;
357 const char *command = "";
358 gboolean command_finished = FALSE;
360 marker = tvb_get_ntohl(tvb, 0);
361 if (tree) {
362 proto_item *cl_item;
363 cl_item = proto_tree_add_text(tree, tvb, 0, -1, "Connectionless");
364 cl_tree = proto_item_add_subtree(cl_item, ett_quakeworld_connectionless);
366 proto_tree_add_uint(cl_tree, hf_quakeworld_connectionless_marker,
367 tvb, 0, 4, marker);
370 /* all the rest of the packet is just text */
371 offset = 4;
373 len = tvb_get_nstringz0(tvb, offset, sizeof(text), text);
374 /* actually, we should look for a eol char and stop already there */
376 if (cl_tree) {
377 proto_item *text_item;
378 text_item = proto_tree_add_string(cl_tree, hf_quakeworld_connectionless_text,
379 tvb, offset, len + 1, text);
380 text_tree = proto_item_add_subtree(text_item, ett_quakeworld_connectionless_text);
383 if (direction == DIR_C2S) {
384 /* client to server commands */
385 const char *c;
387 Cmd_TokenizeString(text);
388 c = Cmd_Argv(0);
390 /* client to sever commands */
391 if (strcmp(c,"ping") == 0) {
392 command = "Ping";
393 command_len = 4;
394 } else if (strcmp(c,"status") == 0) {
395 command = "Status";
396 command_len = 6;
397 } else if (strcmp(c,"log") == 0) {
398 command = "Log";
399 command_len = 3;
400 } else if (strcmp(c,"connect") == 0) {
401 int version;
402 int qport;
403 int challenge;
404 const char *infostring;
405 proto_tree *argument_tree = NULL;
406 command = "Connect";
407 command_len = Cmd_Argv_length(0);
408 if (text_tree) {
409 proto_item *argument_item;
410 proto_tree_add_string(text_tree, hf_quakeworld_connectionless_command,
411 tvb, offset, command_len, command);
412 argument_item = proto_tree_add_string(text_tree,
413 hf_quakeworld_connectionless_arguments,
414 tvb, offset + Cmd_Argv_start(1), len + 1 - Cmd_Argv_start(1),
415 text + Cmd_Argv_start(1));
416 argument_tree = proto_item_add_subtree(argument_item,
417 ett_quakeworld_connectionless_arguments);
418 command_finished=TRUE;
420 version = atoi(Cmd_Argv(1));
421 qport = atoi(Cmd_Argv(2));
422 challenge = atoi(Cmd_Argv(3));
423 infostring = Cmd_Argv(4);
424 if (argument_tree) {
425 proto_item *info_item;
426 proto_tree *info_tree;
427 proto_tree_add_uint(argument_tree,
428 hf_quakeworld_connectionless_connect_version,
429 tvb,
430 offset + Cmd_Argv_start(1),
431 Cmd_Argv_length(1), version);
432 proto_tree_add_uint(argument_tree,
433 hf_quakeworld_connectionless_connect_qport,
434 tvb,
435 offset + Cmd_Argv_start(2),
436 Cmd_Argv_length(2), qport);
437 proto_tree_add_int(argument_tree,
438 hf_quakeworld_connectionless_connect_challenge,
439 tvb,
440 offset + Cmd_Argv_start(3),
441 Cmd_Argv_length(3), challenge);
442 info_item = proto_tree_add_string(argument_tree,
443 hf_quakeworld_connectionless_connect_infostring,
444 tvb,
445 offset + Cmd_Argv_start(4),
446 Cmd_Argv_length(4), infostring);
447 info_tree = proto_item_add_subtree(
448 info_item, ett_quakeworld_connectionless_connect_infostring);
449 dissect_id_infostring(tvb, info_tree, offset + Cmd_Argv_start(4),
450 wmem_strdup(wmem_packet_scope(), infostring),
451 ett_quakeworld_connectionless_connect_infostring_key_value,
452 hf_quakeworld_connectionless_connect_infostring_key_value,
453 hf_quakeworld_connectionless_connect_infostring_key,
454 hf_quakeworld_connectionless_connect_infostring_value);
456 } else if (strcmp(c,"getchallenge") == 0) {
457 command = "Get Challenge";
458 command_len = Cmd_Argv_length(0);
459 } else if (strcmp(c,"rcon") == 0) {
460 const char* password;
461 int i;
462 char remaining[MAX_TEXT_SIZE+1];
463 proto_tree *argument_tree = NULL;
464 command = "Remote Command";
465 command_len = Cmd_Argv_length(0);
466 if (text_tree) {
467 proto_item *argument_item;
468 proto_tree_add_string(text_tree, hf_quakeworld_connectionless_command,
469 tvb, offset, command_len, command);
470 argument_item = proto_tree_add_string(text_tree,
471 hf_quakeworld_connectionless_arguments,
472 tvb, offset + Cmd_Argv_start(1), len + 1 - Cmd_Argv_start(1),
473 text + Cmd_Argv_start(1));
474 argument_tree = proto_item_add_subtree(argument_item,
475 ett_quakeworld_connectionless_arguments);
476 command_finished=TRUE;
478 password = Cmd_Argv(1);
479 if (argument_tree) {
480 proto_tree_add_string(argument_tree,
481 hf_quakeworld_connectionless_rcon_password,
482 tvb,
483 offset + Cmd_Argv_start(1),
484 Cmd_Argv_length(1), password);
486 remaining[0] = '\0';
487 for (i=2; i<Cmd_Argc() ; i++) {
488 g_strlcat (remaining, Cmd_Argv(i), MAX_TEXT_SIZE+1);
489 g_strlcat (remaining, " ", MAX_TEXT_SIZE+1);
491 if (text_tree) {
492 proto_tree_add_string(argument_tree,
493 hf_quakeworld_connectionless_rcon_command,
494 tvb, offset + Cmd_Argv_start(2),
495 Cmd_Argv_start(Cmd_Argc()-1) + Cmd_Argv_length(Cmd_Argc()-1) -
496 Cmd_Argv_start(2),
497 remaining);
499 } else if (c[0]==A2A_PING && ( c[1]=='\0' || c[1]=='\n')) {
500 command = "Ping";
501 command_len = 1;
502 } else if (c[0]==A2A_ACK && ( c[1]=='\0' || c[1]=='\n')) {
503 command = "Ack";
504 command_len = 1;
505 } else {
506 command = "Unknown";
507 command_len = len;
510 else {
511 /* server to client commands */
512 if (text[0] == S2C_CONNECTION) {
513 command = "Connected";
514 command_len = 1;
515 } else if (text[0] == A2C_CLIENT_COMMAND) {
516 command = "Client Command";
517 command_len = 1;
518 /* stringz (command), stringz (localid) */
519 } else if (text[0] == A2C_PRINT) {
520 command = "Print";
521 command_len = 1;
522 /* string */
523 } else if (text[0] == A2A_PING) {
524 command = "Ping";
525 command_len = 1;
526 } else if (text[0] == S2C_CHALLENGE) {
527 command = "Challenge";
528 command_len = 1;
529 /* string, atoi */
530 } else {
531 command = "Unknown";
532 command_len = len;
536 col_append_fstr(pinfo->cinfo, COL_INFO, " %s", command);
538 if (!command_finished) {
539 proto_tree_add_string(text_tree, hf_quakeworld_connectionless_command,
540 tvb, offset, command_len, command);
542 /*offset += len + 1;*/
546 static void
547 dissect_quakeworld_client_commands(tvbuff_t *tvb, packet_info *pinfo,
548 proto_tree *tree)
550 /* If I have too much time at hand, I'll fill it with all
551 the information from my QWD specs:
552 http://www.planetquake.com/demospecs/qwd/
554 call_dissector(data_handle,tvb, pinfo, tree);
558 static void
559 dissect_quakeworld_server_commands(tvbuff_t *tvb, packet_info *pinfo,
560 proto_tree *tree)
562 /* If I have too much time at hand, I'll fill it with all
563 the information from my QWD specs:
564 http://www.planetquake.com/demospecs/qwd/
566 call_dissector(data_handle,tvb, pinfo, tree);
570 static const value_string names_reliable[] = {
571 { 0, "Non Reliable" },
572 { 1, "Reliable" },
573 { 0, NULL }
577 static void
578 dissect_quakeworld_GamePacket(tvbuff_t *tvb, packet_info *pinfo,
579 proto_tree *tree, int direction)
581 proto_tree *game_tree = NULL;
582 guint32 seq1;
583 guint32 seq2;
584 int rel1;
585 int rel2;
586 int offset;
587 guint rest_length;
589 direction = (pinfo->destport == gbl_quakeworldServerPort) ?
590 DIR_C2S : DIR_S2C;
592 if (tree) {
593 proto_item *game_item;
594 game_item = proto_tree_add_text(tree, tvb, 0, -1, "Game");
595 game_tree = proto_item_add_subtree(game_item, ett_quakeworld_game);
598 offset = 0;
600 seq1 = tvb_get_letohl(tvb, offset);
601 rel1 = seq1 & 0x80000000 ? 1 : 0;
602 seq1 &= ~0x80000000;
603 if (game_tree) {
604 proto_item *seq1_item = proto_tree_add_text(game_tree,
605 tvb, offset, 4, "Current Sequence: %u (%s)",
606 seq1, val_to_str(rel1,names_reliable,"%u"));
607 proto_tree *seq1_tree = proto_item_add_subtree(
608 seq1_item, ett_quakeworld_game_seq1);
609 proto_tree_add_uint(seq1_tree, hf_quakeworld_game_seq1,
610 tvb, offset, 4, seq1);
611 proto_tree_add_boolean(seq1_tree, hf_quakeworld_game_rel1,
612 tvb, offset+3, 1, rel1);
614 offset += 4;
616 seq2 = tvb_get_letohl(tvb, offset);
617 rel2 = seq2 & 0x80000000 ? 1 : 0;
618 seq2 &= ~0x80000000;
619 if (game_tree) {
620 proto_item *seq2_item = proto_tree_add_text(game_tree,
621 tvb, offset, 4, "Acknowledge Sequence: %u (%s)",
622 seq2, val_to_str(rel2,names_reliable,"%u"));
623 proto_tree *seq2_tree = proto_item_add_subtree(seq2_item, ett_quakeworld_game_seq2);
624 proto_tree_add_uint(seq2_tree, hf_quakeworld_game_seq2, tvb, offset, 4, seq2);
625 proto_tree_add_boolean(seq2_tree, hf_quakeworld_game_rel2, tvb, offset+3, 1, rel2);
627 offset += 4;
629 if (direction == DIR_C2S) {
630 /* client to server */
631 guint16 qport = tvb_get_letohs(tvb, offset);
632 if (game_tree) {
633 proto_tree_add_uint(game_tree, hf_quakeworld_game_qport, tvb, offset, 2, qport);
635 offset +=2;
638 /* all the rest is pure game data */
639 rest_length = tvb_reported_length(tvb) - offset;
640 if (rest_length) {
641 tvbuff_t *next_tvb =
642 tvb_new_subset(tvb, offset, rest_length , rest_length);
644 if (direction == DIR_C2S) {
645 proto_tree *c_tree = NULL;
646 if (tree) {
647 proto_item *c_item;
648 c_item = proto_tree_add_text(game_tree, next_tvb,
649 0, -1, "Client Commands");
650 c_tree = proto_item_add_subtree(c_item, ett_quakeworld_game_clc);
652 dissect_quakeworld_client_commands(next_tvb, pinfo, c_tree);
654 else {
655 proto_tree *c_tree = NULL;
656 if (tree) {
657 proto_item *c_item;
658 c_item = proto_tree_add_text(game_tree, next_tvb,
659 0, -1, "Server Commands");
660 c_tree = proto_item_add_subtree(c_item, ett_quakeworld_game_svc);
662 dissect_quakeworld_server_commands(next_tvb, pinfo, c_tree);
668 static void
669 dissect_quakeworld(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
671 proto_tree *quakeworld_tree = NULL;
672 int direction;
674 direction = (pinfo->destport == gbl_quakeworldServerPort) ?
675 DIR_C2S : DIR_S2C;
677 col_set_str(pinfo->cinfo, COL_PROTOCOL, "QUAKEWORLD");
678 col_add_str(pinfo->cinfo, COL_INFO, val_to_str(direction,
679 names_direction, "%u"));
681 if (tree) {
682 proto_item *quakeworld_item;
683 quakeworld_item = proto_tree_add_item(tree, proto_quakeworld,
684 tvb, 0, -1, ENC_NA);
685 quakeworld_tree = proto_item_add_subtree(quakeworld_item, ett_quakeworld);
686 proto_tree_add_uint_format(quakeworld_tree,
687 direction == DIR_S2C ?
688 hf_quakeworld_s2c :
689 hf_quakeworld_c2s,
690 tvb, 0, 0, 1,
691 "Direction: %s", val_to_str(direction, names_direction, "%u"));
694 if (tvb_get_ntohl(tvb, 0) == 0xffffffff) {
695 col_append_str(pinfo->cinfo, COL_INFO, " Connectionless");
696 proto_tree_add_uint_format(quakeworld_tree,
697 hf_quakeworld_connectionless,
698 tvb, 0, 0, 1,
699 "Type: Connectionless");
700 dissect_quakeworld_ConnectionlessPacket(
701 tvb, pinfo, quakeworld_tree, direction);
703 else {
704 col_append_str(pinfo->cinfo, COL_INFO, " Game");
705 proto_tree_add_uint_format(quakeworld_tree,
706 hf_quakeworld_game,
707 tvb, 0, 0, 1,
708 "Type: Game");
709 dissect_quakeworld_GamePacket(
710 tvb, pinfo, quakeworld_tree, direction);
715 void proto_reg_handoff_quakeworld(void);
717 void
718 proto_register_quakeworld(void)
720 static hf_register_info hf[] = {
721 { &hf_quakeworld_c2s,
722 { "Client to Server", "quakeworld.c2s",
723 FT_UINT32, BASE_DEC, NULL, 0x0,
724 NULL, HFILL }},
725 { &hf_quakeworld_s2c,
726 { "Server to Client", "quakeworld.s2c",
727 FT_UINT32, BASE_DEC, NULL, 0x0,
728 NULL, HFILL }},
729 { &hf_quakeworld_connectionless,
730 { "Connectionless", "quakeworld.connectionless",
731 FT_UINT32, BASE_DEC, NULL, 0x0,
732 NULL, HFILL }},
733 { &hf_quakeworld_game,
734 { "Game", "quakeworld.game",
735 FT_UINT32, BASE_DEC, NULL, 0x0,
736 NULL, HFILL }},
737 { &hf_quakeworld_connectionless_marker,
738 { "Marker", "quakeworld.connectionless.marker",
739 FT_UINT32, BASE_HEX, NULL, 0x0,
740 NULL, HFILL }},
741 { &hf_quakeworld_connectionless_text,
742 { "Text", "quakeworld.connectionless.text",
743 FT_STRING, BASE_NONE, NULL, 0x0,
744 NULL, HFILL }},
745 { &hf_quakeworld_connectionless_command,
746 { "Command", "quakeworld.connectionless.command",
747 FT_STRING, BASE_NONE, NULL, 0x0,
748 NULL, HFILL }},
749 { &hf_quakeworld_connectionless_arguments,
750 { "Arguments", "quakeworld.connectionless.arguments",
751 FT_STRING, BASE_NONE, NULL, 0x0,
752 NULL, HFILL }},
753 { &hf_quakeworld_connectionless_connect_version,
754 { "Version", "quakeworld.connectionless.connect.version",
755 FT_UINT32, BASE_DEC, NULL, 0x0,
756 "Protocol Version", HFILL }},
757 { &hf_quakeworld_connectionless_connect_qport,
758 { "QPort", "quakeworld.connectionless.connect.qport",
759 FT_UINT32, BASE_DEC, NULL, 0x0,
760 "QPort of the client", HFILL }},
761 { &hf_quakeworld_connectionless_connect_challenge,
762 { "Challenge", "quakeworld.connectionless.connect.challenge",
763 FT_INT32, BASE_DEC, NULL, 0x0,
764 "Challenge from the server", HFILL }},
765 { &hf_quakeworld_connectionless_connect_infostring,
766 { "Infostring", "quakeworld.connectionless.connect.infostring",
767 FT_STRING, BASE_NONE, NULL, 0x0,
768 "Infostring with additional variables", HFILL }},
769 { &hf_quakeworld_connectionless_connect_infostring_key_value,
770 { "Key/Value", "quakeworld.connectionless.connect.infostring.key_value",
771 FT_STRING, BASE_NONE, NULL, 0x0,
772 "Key and Value", HFILL }},
773 { &hf_quakeworld_connectionless_connect_infostring_key,
774 { "Key", "quakeworld.connectionless.connect.infostring.key",
775 FT_STRING, BASE_NONE, NULL, 0x0,
776 "Infostring Key", HFILL }},
777 { &hf_quakeworld_connectionless_connect_infostring_value,
778 { "Value", "quakeworld.connectionless.connect.infostring.value",
779 FT_STRING, BASE_NONE, NULL, 0x0,
780 "Infostring Value", HFILL }},
781 { &hf_quakeworld_connectionless_rcon_password,
782 { "Password", "quakeworld.connectionless.rcon.password",
783 FT_STRING, BASE_NONE, NULL, 0x0,
784 "Rcon Password", HFILL }},
785 { &hf_quakeworld_connectionless_rcon_command,
786 { "Command", "quakeworld.connectionless.rcon.command",
787 FT_STRING, BASE_NONE, NULL, 0x0,
788 NULL, HFILL }},
789 { &hf_quakeworld_game_seq1,
790 { "Sequence Number", "quakeworld.game.seq1",
791 FT_UINT32, BASE_DEC, NULL, 0x0,
792 "Sequence number of the current packet", HFILL }},
793 { &hf_quakeworld_game_rel1,
794 { "Reliable", "quakeworld.game.rel1",
795 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
796 "Packet is reliable and may be retransmitted", HFILL }},
797 { &hf_quakeworld_game_seq2,
798 { "Sequence Number", "quakeworld.game.seq2",
799 FT_UINT32, BASE_DEC, NULL, 0x0,
800 "Sequence number of the last received packet", HFILL }},
801 { &hf_quakeworld_game_rel2,
802 { "Reliable", "quakeworld.game.rel2",
803 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
804 "Packet was reliable and may be retransmitted", HFILL }},
805 { &hf_quakeworld_game_qport,
806 { "QPort", "quakeworld.game.qport",
807 FT_UINT32, BASE_DEC, NULL, 0x0,
808 "QuakeWorld Client Port", HFILL }}
810 static gint *ett[] = {
811 &ett_quakeworld,
812 &ett_quakeworld_connectionless,
813 &ett_quakeworld_connectionless_text,
814 &ett_quakeworld_connectionless_arguments,
815 &ett_quakeworld_connectionless_connect_infostring,
816 &ett_quakeworld_connectionless_connect_infostring_key_value,
817 &ett_quakeworld_game,
818 &ett_quakeworld_game_seq1,
819 &ett_quakeworld_game_seq2,
820 &ett_quakeworld_game_clc,
821 &ett_quakeworld_game_svc
823 module_t *quakeworld_module;
825 proto_quakeworld = proto_register_protocol("QuakeWorld Network Protocol",
826 "QUAKEWORLD", "quakeworld");
827 proto_register_field_array(proto_quakeworld, hf, array_length(hf));
828 proto_register_subtree_array(ett, array_length(ett));
830 /* Register a configuration option for port */
831 quakeworld_module = prefs_register_protocol(proto_quakeworld,
832 proto_reg_handoff_quakeworld);
833 prefs_register_uint_preference(quakeworld_module, "udp.port",
834 "QuakeWorld Server UDP Port",
835 "Set the UDP port for the QuakeWorld Server",
836 10, &gbl_quakeworldServerPort);
840 void
841 proto_reg_handoff_quakeworld(void)
843 static gboolean Initialized=FALSE;
844 static dissector_handle_t quakeworld_handle;
845 static guint ServerPort;
847 if (!Initialized) {
848 quakeworld_handle = create_dissector_handle(dissect_quakeworld,
849 proto_quakeworld);
850 data_handle = find_dissector("data");
851 Initialized=TRUE;
852 } else {
853 dissector_delete_uint("udp.port", ServerPort, quakeworld_handle);
856 /* set port for future deletes */
857 ServerPort=gbl_quakeworldServerPort;
859 dissector_add_uint("udp.port", gbl_quakeworldServerPort, quakeworld_handle);