2 * Routines for QuakeWorld packet dissection
4 * Uwe Girlich <uwe@planetquake.com>
5 * http://www.idsoftware.com/q1source/q1source.zip
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.
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
;
94 COM_Parse (char *data
)
102 com_token_length
= 0;
107 /* skip whitespace */
112 return NULL
; /* end of file; */
113 if ((c
!= ' ') && (!g_ascii_iscntrl(c
)))
119 /* skip // comments */
120 if ((c
=='/') && (data
[1]=='/')) {
121 while (*data
&& *data
!= '\n')
127 /* handle quoted strings specially */
133 if ((c
=='\"') || (c
=='\0')) {
134 com_token
[len
] = '\0';
143 /* parse a regular word */
150 } while (( c
!= ' ') && (!g_ascii_iscntrl(c
)));
152 com_token
[len
] = '\0';
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
];
176 if ( arg
>= cmd_argc
)
177 return cmd_null_string
;
178 return cmd_argv
[arg
];
183 Cmd_Argv_start(int arg
)
185 if ( arg
>= cmd_argc
)
187 return cmd_argv_start
[arg
];
192 Cmd_Argv_length(int arg
)
194 if ( arg
>= cmd_argc
)
196 return cmd_argv_length
[arg
];
201 Cmd_TokenizeString(char* text
)
210 /* skip whitespace up to a \n */
211 while (*text
&& *text
<= ' ' && *text
!= '\n') {
217 /* a newline seperates commands in the buffer */
225 text
= COM_Parse (text
);
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
;
236 start
+= com_token_start
+ com_token_length
;
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
) {
259 if (*keypos
== '\0') break;
260 if (*keypos
== '\\') keypos
++;
264 *(keypos
+ keylength
) != '\\' &&
265 *(keypos
+ keylength
) != '\0'
268 keyvaluesep
= keypos
+ keylength
;
269 if (*keyvaluesep
== '\0') break;
270 valuepos
= keyvaluesep
+1;
273 *(valuepos
+ valuelength
) != '\\' &&
274 *(valuepos
+ valuelength
) != '\0'
277 valueend
= valuepos
+ valuelength
;
278 if (*valueend
== '\0') {
281 *(keyvaluesep
) = '=';
285 proto_item
* sub_item
;
286 proto_tree
* sub_tree
;
288 sub_item
= proto_tree_add_string(tree
,
291 offset
+ (gint
)(keypos
-infostring
),
292 keylength
+ 1 + valuelength
, keypos
);
293 sub_tree
= proto_item_add_subtree(
296 *(keyvaluesep
) = '\0';
297 proto_tree_add_string(sub_tree
,
300 offset
+ (gint
)(keypos
-infostring
),
302 proto_tree_add_string(sub_tree
,
305 offset
+ (gint
)(valuepos
-infostring
),
306 valuelength
, valuepos
);
308 newpos
= valueend
+ 1;
313 static const value_string names_direction
[] = {
315 { DIR_C2S
, "Client to Server" },
317 { DIR_S2C
, "Server to Client" },
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'
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];
357 const char *command
= "";
358 gboolean command_finished
= FALSE
;
360 marker
= tvb_get_ntohl(tvb
, 0);
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
,
370 /* all the rest of the packet is just text */
373 len
= tvb_get_nstringz0(tvb
, offset
, sizeof(text
), text
);
374 /* actually, we should look for a eol char and stop already there */
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 */
387 Cmd_TokenizeString(text
);
390 /* client to sever commands */
391 if (strcmp(c
,"ping") == 0) {
394 } else if (strcmp(c
,"status") == 0) {
397 } else if (strcmp(c
,"log") == 0) {
400 } else if (strcmp(c
,"connect") == 0) {
404 const char *infostring
;
405 proto_tree
*argument_tree
= NULL
;
407 command_len
= Cmd_Argv_length(0);
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);
425 proto_item
*info_item
;
426 proto_tree
*info_tree
;
427 proto_tree_add_uint(argument_tree
,
428 hf_quakeworld_connectionless_connect_version
,
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
,
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
,
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
,
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
;
462 char remaining
[MAX_TEXT_SIZE
+1];
463 proto_tree
*argument_tree
= NULL
;
464 command
= "Remote Command";
465 command_len
= Cmd_Argv_length(0);
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);
480 proto_tree_add_string(argument_tree
,
481 hf_quakeworld_connectionless_rcon_password
,
483 offset
+ Cmd_Argv_start(1),
484 Cmd_Argv_length(1), password
);
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);
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) -
499 } else if (c
[0]==A2A_PING
&& ( c
[1]=='\0' || c
[1]=='\n')) {
502 } else if (c
[0]==A2A_ACK
&& ( c
[1]=='\0' || c
[1]=='\n')) {
511 /* server to client commands */
512 if (text
[0] == S2C_CONNECTION
) {
513 command
= "Connected";
515 } else if (text
[0] == A2C_CLIENT_COMMAND
) {
516 command
= "Client Command";
518 /* stringz (command), stringz (localid) */
519 } else if (text
[0] == A2C_PRINT
) {
523 } else if (text
[0] == A2A_PING
) {
526 } else if (text
[0] == S2C_CHALLENGE
) {
527 command
= "Challenge";
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;*/
547 dissect_quakeworld_client_commands(tvbuff_t
*tvb
, packet_info
*pinfo
,
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
);
559 dissect_quakeworld_server_commands(tvbuff_t
*tvb
, packet_info
*pinfo
,
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" },
578 dissect_quakeworld_GamePacket(tvbuff_t
*tvb
, packet_info
*pinfo
,
579 proto_tree
*tree
, int direction
)
581 proto_tree
*game_tree
= NULL
;
589 direction
= (pinfo
->destport
== gbl_quakeworldServerPort
) ?
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
);
600 seq1
= tvb_get_letohl(tvb
, offset
);
601 rel1
= seq1
& 0x80000000 ? 1 : 0;
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
);
616 seq2
= tvb_get_letohl(tvb
, offset
);
617 rel2
= seq2
& 0x80000000 ? 1 : 0;
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
);
629 if (direction
== DIR_C2S
) {
630 /* client to server */
631 guint16 qport
= tvb_get_letohs(tvb
, offset
);
633 proto_tree_add_uint(game_tree
, hf_quakeworld_game_qport
, tvb
, offset
, 2, qport
);
638 /* all the rest is pure game data */
639 rest_length
= tvb_reported_length(tvb
) - offset
;
642 tvb_new_subset(tvb
, offset
, rest_length
, rest_length
);
644 if (direction
== DIR_C2S
) {
645 proto_tree
*c_tree
= NULL
;
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
);
655 proto_tree
*c_tree
= NULL
;
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
);
669 dissect_quakeworld(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
671 proto_tree
*quakeworld_tree
= NULL
;
674 direction
= (pinfo
->destport
== gbl_quakeworldServerPort
) ?
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"));
682 proto_item
*quakeworld_item
;
683 quakeworld_item
= proto_tree_add_item(tree
, proto_quakeworld
,
685 quakeworld_tree
= proto_item_add_subtree(quakeworld_item
, ett_quakeworld
);
686 proto_tree_add_uint_format(quakeworld_tree
,
687 direction
== DIR_S2C
?
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
,
699 "Type: Connectionless");
700 dissect_quakeworld_ConnectionlessPacket(
701 tvb
, pinfo
, quakeworld_tree
, direction
);
704 col_append_str(pinfo
->cinfo
, COL_INFO
, " Game");
705 proto_tree_add_uint_format(quakeworld_tree
,
709 dissect_quakeworld_GamePacket(
710 tvb
, pinfo
, quakeworld_tree
, direction
);
715 void proto_reg_handoff_quakeworld(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,
725 { &hf_quakeworld_s2c
,
726 { "Server to Client", "quakeworld.s2c",
727 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
729 { &hf_quakeworld_connectionless
,
730 { "Connectionless", "quakeworld.connectionless",
731 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
733 { &hf_quakeworld_game
,
734 { "Game", "quakeworld.game",
735 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
737 { &hf_quakeworld_connectionless_marker
,
738 { "Marker", "quakeworld.connectionless.marker",
739 FT_UINT32
, BASE_HEX
, NULL
, 0x0,
741 { &hf_quakeworld_connectionless_text
,
742 { "Text", "quakeworld.connectionless.text",
743 FT_STRING
, BASE_NONE
, NULL
, 0x0,
745 { &hf_quakeworld_connectionless_command
,
746 { "Command", "quakeworld.connectionless.command",
747 FT_STRING
, BASE_NONE
, NULL
, 0x0,
749 { &hf_quakeworld_connectionless_arguments
,
750 { "Arguments", "quakeworld.connectionless.arguments",
751 FT_STRING
, BASE_NONE
, NULL
, 0x0,
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,
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
[] = {
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
);
841 proto_reg_handoff_quakeworld(void)
843 static gboolean Initialized
=FALSE
;
844 static dissector_handle_t quakeworld_handle
;
845 static guint ServerPort
;
848 quakeworld_handle
= create_dissector_handle(dissect_quakeworld
,
850 data_handle
= find_dissector("data");
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
);