#include <string.h> and/or #include <stdio.h> not needed.
[obnox/wireshark/wip.git] / epan / dissectors / packet-dis-fields.c
1 /* packet-dis-fields.c
2  * Routines and definitions for DIS field parsing.
3  * Copyright 2005, Scientific Research Corporation
4  * Initial implementation by Jeremy Ouellette <jouellet@scires.com>
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <epan/packet.h>
32 #include "packet-dis-fields.h"
33 #include "packet-dis-enums.h"
34 #include "packet-dis-pdus.h"
35
36 /* all of these variables are assigned by reference */
37 /* *(parserNode.outputVar) = value                  */
38 guint32 disProtocolVersion;
39 guint32 pduType;
40 guint32 protocolFamily;
41 guint32 persistentObjectPduType;
42 guint32 entityKind;
43 guint32 entityDomain;
44 guint32 radioID;
45 guint32 disRadioTransmitState;
46 guint32 encodingScheme;
47 guint32 numSamples;
48 guint32 numFixed;
49 guint32 numVariable;
50 guint32 variableDatumLength;
51 guint32 variableParameterType;
52 guint32 variableRecordLength;
53 guint32 variableRecordType;
54 guint32 majorModulation;
55 guint32 systemModulation;
56 guint32 modulationParamLength;
57 guint32 disAntennaPattern;
58
59 /* Headers
60  */
61 DIS_ParserNode DIS_FIELDS_PDU_HEADER[] =
62 {
63     { DIS_FIELDTYPE_PROTOCOL_VERSION, "Protocol Version",0,0,0,&disProtocolVersion },
64     { DIS_FIELDTYPE_EXERCISE_ID,      "Exercise ID",0,0,0,0 },
65     { DIS_FIELDTYPE_PDU_TYPE,         "PDU Type",0,0,0,&pduType },
66     { DIS_FIELDTYPE_PROTOCOL_FAMILY,  "Protocol Family",0,0,0,&protocolFamily },
67     { DIS_FIELDTYPE_TIMESTAMP,        "Timestamp",0,0,0,0 },
68     { DIS_FIELDTYPE_PDU_LENGTH,       "Length",0,0,0,0 },
69     { DIS_FIELDTYPE_PAD16,            "Padding",0,0,0,0 },
70     { DIS_FIELDTYPE_END,              NULL,0,0,0,0 }
71 };
72
73 DIS_ParserNode DIS_FIELDS_PERSISTENT_OBJECT_HEADER[] =
74 {
75     { DIS_FIELDTYPE_UINT8,                  "Protocol Version",0,0,0,0 },
76     { DIS_FIELDTYPE_PERSISTENT_OBJECT_TYPE, "PO PDU Type",0,0,0,&persistentObjectPduType },
77     { DIS_FIELDTYPE_UINT8,                  "Exercise ID",0,0,0,0 },
78     { DIS_FIELDTYPE_UINT8,                  "PO Database ID",0,0,0,0 },
79     { DIS_FIELDTYPE_UINT16,                 "Length",0,0,0,0 },
80     { DIS_FIELDTYPE_UINT16,                 "PDU Count",0,0,0,0 },
81     { DIS_FIELDTYPE_END,                    NULL,0,0,0,0 }
82 };
83
84 /* Composite types
85  */
86
87 DIS_ParserNode DIS_FIELDS_BURST_DESCRIPTOR[] =
88 {
89     { DIS_FIELDTYPE_ENTITY_TYPE, "Munition",0,0,0,0 },
90     { DIS_FIELDTYPE_WARHEAD,     "Warhead",0,0,0,0 },
91     { DIS_FIELDTYPE_FUSE,        "Fuse",0,0,0,0 },
92     { DIS_FIELDTYPE_UINT16,      "Quantity",0,0,0,0 },
93     { DIS_FIELDTYPE_UINT16,      "Rate",0,0,0,0 },
94     { DIS_FIELDTYPE_END,         NULL,0,0,0,0 }
95 };
96
97 DIS_ParserNode DIS_FIELDS_CLOCK_TIME[] =
98 {
99     { DIS_FIELDTYPE_UINT32,                 "Hour",0,0,0,0 },
100     { DIS_FIELDTYPE_TIMESTAMP,              "Time Past The Hour",0,0,0,0 },
101     { DIS_FIELDTYPE_END,                    NULL,0,0,0,0 }
102 };
103
104 DIS_ParserNode DIS_FIELDS_ENTITY_ID[] =
105 {
106     { DIS_FIELDTYPE_SITE,        "Site",0,0,0,0 },
107     { DIS_FIELDTYPE_APPLICATION, "Application",0,0,0,0 },
108     { DIS_FIELDTYPE_ENTITY,      "Entity",0,0,0,0 },
109     { DIS_FIELDTYPE_END,         NULL,0,0,0,0 }
110 };
111
112 DIS_ParserNode DIS_FIELDS_ENTITY_TYPE[] =
113 {
114     { DIS_FIELDTYPE_ENTITY_KIND, "Entity Kind",0,0,0,&entityKind },
115     { DIS_FIELDTYPE_DOMAIN,      "Domain",0,0,0,&entityDomain },
116     { DIS_FIELDTYPE_COUNTRY,     "Country",0,0,0,0 },
117     { DIS_FIELDTYPE_CATEGORY,    "Category",0,0,0,0 },
118     { DIS_FIELDTYPE_SUBCATEGORY, "Subcategory",0,0,0,0 },
119     { DIS_FIELDTYPE_SPECIFIC,    "Specific",0,0,0,0 },
120     { DIS_FIELDTYPE_EXTRA,       "Extra",0,0,0,0 },
121     { DIS_FIELDTYPE_END,         NULL,0,0,0,0 }
122 };
123
124 DIS_ParserNode DIS_FIELDS_RADIO_ENTITY_TYPE[] =
125 {
126     { DIS_FIELDTYPE_ENTITY_KIND,          "Entity Kind",0,0,0,&entityKind },
127     { DIS_FIELDTYPE_DOMAIN,               "Domain",0,0,0,&entityDomain },
128     { DIS_FIELDTYPE_COUNTRY,              "Country",0,0,0,0 },
129     { DIS_FIELDTYPE_RADIO_CATEGORY,       "Radio Category",0,0,0,0 },
130     { DIS_FIELDTYPE_NOMENCLATURE_VERSION, "Nomenclature Version",0,0,0,0 },
131     { DIS_FIELDTYPE_NOMENCLATURE,         "Nomenclature",0,0,0,0 },
132     { DIS_FIELDTYPE_END,                  NULL,0,0,0,0 }
133 };
134
135 DIS_ParserNode DIS_FIELDS_MODULATION_TYPE[] =
136 {
137     { DIS_FIELDTYPE_SPREAD_SPECTRUM,        "Spread Spectrum",0,0,0,0 },
138     { DIS_FIELDTYPE_MODULATION_MAJOR,       "Major",0,0,0,&majorModulation },
139     { DIS_FIELDTYPE_MODULATION_DETAIL,      "Detail",0,0,0,0 },
140     { DIS_FIELDTYPE_MODULATION_SYSTEM,      "System",0,0,0,&systemModulation },
141     { DIS_FIELDTYPE_END,                    NULL,0,0,0,0 }
142 };
143
144 DIS_ParserNode DIS_FIELDS_EVENT_ID[] =
145 {
146     { DIS_FIELDTYPE_UINT16, "Site",0,0,0,0 },
147     { DIS_FIELDTYPE_UINT16, "Application",0,0,0,0 },
148     { DIS_FIELDTYPE_UINT16, "Event Number",0,0,0,0 },
149     { DIS_FIELDTYPE_END,    NULL,0,0,0,0 }
150 };
151
152 DIS_ParserNode DIS_FIELDS_ORIENTATION[] =
153 {
154     { DIS_FIELDTYPE_FLOAT32, "Psi",0,0,0,0 },
155     { DIS_FIELDTYPE_FLOAT32, "Theta",0,0,0,0 },
156     { DIS_FIELDTYPE_FLOAT32, "Phi",0,0,0,0 },
157     { DIS_FIELDTYPE_END,     NULL,0,0,0,0 }
158 };
159
160 DIS_ParserNode DIS_FIELDS_SIMULATION_ADDRESS[] =
161 {
162     { DIS_FIELDTYPE_UINT16, "Site",0,0,0,0 },
163     { DIS_FIELDTYPE_UINT16, "Application",0,0,0,0 },
164     { DIS_FIELDTYPE_END,    NULL,0,0,0,0 }
165 };
166
167 DIS_ParserNode DIS_FIELDS_VECTOR_FLOAT_32[] =
168 {
169     { DIS_FIELDTYPE_FLOAT32, "X",0,0,0,0 },
170     { DIS_FIELDTYPE_FLOAT32, "Y",0,0,0,0 },
171     { DIS_FIELDTYPE_FLOAT32, "Z",0,0,0,0 },
172     { DIS_FIELDTYPE_END,     NULL,0,0,0,0 }
173 };
174
175 DIS_ParserNode DIS_FIELDS_VECTOR_FLOAT_64[] =
176 {
177     { DIS_FIELDTYPE_FLOAT64, "X",0,0,0,0 },
178     { DIS_FIELDTYPE_FLOAT64, "Y",0,0,0,0 },
179     { DIS_FIELDTYPE_FLOAT64, "Z",0,0,0,0 },
180     { DIS_FIELDTYPE_END,     NULL,0,0,0,0 }
181 };
182
183 DIS_ParserNode DIS_FIELDS_MOD_PARAMS_CCTT_SINCGARS[] =
184 {
185     { DIS_FIELDTYPE_FH_NETWORK_ID,        "Frequency Hopping Network ID",0,0,0,0 },
186     { DIS_FIELDTYPE_FH_SET_ID,            "Frequency Set ID",0,0,0,0 },
187     { DIS_FIELDTYPE_LO_SET_ID,            "Lockout Set ID",0,0,0,0 },
188     { DIS_FIELDTYPE_FH_MSG_START,         "Frequency Hopping Message Start",0,0,0,0 },
189     { DIS_FIELDTYPE_RESERVED,             "Reserved",0,0,0,0 },
190     { DIS_FIELDTYPE_FH_SYNC_TIME_OFFSET,  "FH Synchronization Time Offset",0,0,0,0 },
191     { DIS_FIELDTYPE_FH_SECURITY_KEY,      "Transmission Security Key",0,0,0,0 },
192     { DIS_FIELDTYPE_FH_CLEAR_CHANNEL,     "Clear Channel",0,0,0,0 },
193     { DIS_FIELDTYPE_PAD8,                 "Padding",0,0,0,0 },
194     { DIS_FIELDTYPE_END,                  NULL,0,0,0,0 }
195 };
196
197 DIS_ParserNode DIS_FIELDS_MOD_PARAMS_JTIDS_MIDS[] =
198 {
199     { DIS_FIELDTYPE_TS_ALLOCATION_MODE,           "Time Slot Allocaton Mode",0,0,0,0 },
200     { DIS_FIELDTYPE_TRANSMITTER_PRIMARY_MODE,     "Transmitter Primary Mode",0,0,0,0 },
201     { DIS_FIELDTYPE_TRANSMITTER_SECONDARY_MODE,   "Transmitter Secondary Mode",0,0,0,0 },
202     { DIS_FIELDTYPE_JTIDS_SYNC_STATE,             "Synchronization State",0,0,0,0 },
203     { DIS_FIELDTYPE_NETWORK_SYNC_ID,              "Network Sync ID",0,0,0,0 },
204     { DIS_FIELDTYPE_END,                          NULL,0,0,0,0 }
205 };
206
207 /* Array records
208  */
209 DIS_ParserNode DIS_FIELDS_FIXED_DATUM[] =
210 {
211     { DIS_FIELDTYPE_DATUM_ID,                "Datum ID",0,0,0,0 },
212     { DIS_FIELDTYPE_FIXED_DATUM_VALUE,       "Datum value",0,0,0,0 },
213     { DIS_FIELDTYPE_END,                     NULL,0,0,0,0 }
214 };
215
216 DIS_ParserNode DIS_FIELDS_VARIABLE_DATUM[] =
217 {
218     { DIS_FIELDTYPE_DATUM_ID,                "Datum ID",0,0,0,0 },
219     { DIS_FIELDTYPE_DATUM_LENGTH,            "Datum length",0,0,0,&variableDatumLength },
220     { DIS_FIELDTYPE_VARIABLE_DATUM_VALUE,    "Datum value",0,0,0,0 },
221     { DIS_FIELDTYPE_END,                     NULL,0,0,0,0 }
222 };
223
224 DIS_ParserNode DIS_FIELDS_DATUM_IDS[] =
225 {
226     { DIS_FIELDTYPE_DATUM_ID,                "Datum ID",0,0,0,0 },
227     { DIS_FIELDTYPE_END,                     NULL,0,0,0,0 }
228 };
229
230 /* Variable Parameters
231  */
232 DIS_ParserNode DIS_FIELDS_VP_TYPE[] =
233 {
234     { DIS_FIELDTYPE_PARAMETER_TYPE_DESIGNATOR,   "Variable Parameter Type",0,0,0,&variableParameterType },
235     { DIS_FIELDTYPE_END,                         NULL,0,0,0,0 }
236 };
237
238 /* Array record contents - variable parameter records
239  */
240 DIS_ParserNode DIS_FIELDS_VP_GENERIC[] =
241 {
242     { DIS_FIELDTYPE_FIXED_LEN_STR,               "Data",15,0,0,0 },
243     { DIS_FIELDTYPE_END,                         NULL,0,0,0,0 }
244 };
245
246 DIS_ParserNode DIS_FIELDS_VP_ARTICULATED_PART[] =
247 {
248     { DIS_FIELDTYPE_UINT8,                       "Change",0,0,0,0 },
249     { DIS_FIELDTYPE_UINT16,                      "Part Attached To ID",0,0,0,0 },
250     { DIS_FIELDTYPE_ARTIC_PARAM_TYPE,            "Parameter Type",0,0,0,0 },
251     { DIS_FIELDTYPE_UINT64,                      "Parameter Value",0,0,0,0 },
252     { DIS_FIELDTYPE_END,                         NULL,0,0,0,0 }
253 };
254
255 DIS_ParserNode DIS_FIELDS_VP_ATTACHED_PART[] =
256 {
257     { DIS_FIELDTYPE_UINT8,                       "Attached Indicator",0,0,0,0 },
258     { DIS_FIELDTYPE_UINT16,                      "Part Attached To ID",0,0,0,0 },
259     { DIS_FIELDTYPE_ARTIC_PARAM_TYPE,            "Parameter Type",0,0,0,0 },
260     { DIS_FIELDTYPE_ENTITY_TYPE,                 "Part Type",0,0,0,0 },
261     { DIS_FIELDTYPE_END,                         NULL,0,0,0,0 }
262 };
263
264 DIS_ParserNode DIS_FIELDS_VP_ENTITY_OFFSET[] =
265 {
266     { DIS_FIELDTYPE_UINT8,                       "Offset Type",0,0,0,0 },
267     { DIS_FIELDTYPE_PAD8,                        "Padding",2,0,0,0 },
268     { DIS_FIELDTYPE_VECTOR_32,                   "Offset",0,0,0,0 },
269     { DIS_FIELDTYPE_ORIENTATION,                 "Orientation",0,0,0,0 },
270     { DIS_FIELDTYPE_END,                         NULL,0,0,0,0 }
271 };
272
273 /* Variable Records
274  */
275 DIS_ParserNode DIS_FIELDS_VR_TYPE[] =
276 {
277     { DIS_FIELDTYPE_UINT32,   "Record Type",0,0,0,&variableRecordType },
278     { DIS_FIELDTYPE_UINT16,   "Record Length",0,0,0,&variableRecordLength },
279     { DIS_FIELDTYPE_END,      NULL,0,0,0,0 }
280 };
281
282 DIS_ParserNode DIS_FIELDS_VR_APPLICATION_HEALTH_STATUS[] =
283 {
284     { DIS_FIELDTYPE_PAD8,                       "Padding",2,0,0,0 },
285     { DIS_FIELDTYPE_APPLICATION_STATUS_TYPE,    "Status Type",0,0,0,0 },
286     { DIS_FIELDTYPE_APPLICATION_GENERAL_STATUS, "General Status",0,0,0,0 },
287     { DIS_FIELDTYPE_UINT8,                      "Specific Status",0,0,0,0 },
288     { DIS_FIELDTYPE_INT32,                      "Status Value Int",0,0,0,0 },
289     { DIS_FIELDTYPE_FLOAT64,                    "Status Value Float",0,0,0,0 },
290     { DIS_FIELDTYPE_END,                        NULL,0,0,0,0 }
291 };
292
293 DIS_ParserNode DIS_FIELDS_VR_APPLICATION_INITIALIZATION[] =
294 {
295     { DIS_FIELDTYPE_UINT8,                   "Exercise ID",0,0,0,0 },
296     { DIS_FIELDTYPE_PAD8,                    "Padding",0,0,0,0 },
297     { DIS_FIELDTYPE_FIXED_LEN_STR,           "Exercise File Path",256,0,0,0 },
298     { DIS_FIELDTYPE_FIXED_LEN_STR,           "Exercise File Name",128,0,0,0 },
299     { DIS_FIELDTYPE_FIXED_LEN_STR,           "Application Role",64,0,0,0 },
300     { DIS_FIELDTYPE_END,                     NULL,0,0,0,0 }
301 };
302
303 DIS_ParserNode DIS_FIELDS_VR_DATA_QUERY[] =
304 {
305     { DIS_FIELDTYPE_UINT16,                  "Num Records",0,0,0,&numFixed },
306     { DIS_FIELDTYPE_FIXED_DATUM_IDS,         "Record",0,0,0,0 },
307     { DIS_FIELDTYPE_END,                     NULL,0,0,0,0 }
308 };
309
310 /* Bit fields
311  */
312 DIS_ParserNode DIS_FIELDS_NONE[] =
313 {
314     { DIS_FIELDTYPE_END, NULL, 0,0,0,0 }
315 };
316
317 DIS_BitMask DIS_APPEARANCE_LANDPLATFORM[] =
318 {
319     { 0x00000001, 0, "Paint Scheme", {
320         { 0, "Uniform color" },
321         { 1, "Camouflage" },
322         { 0,0 }
323     } },
324     { 0x00000002, 1, "Mobility", {
325         { 0, "No mobility kill" },
326         { 1, "Mobility kill" },
327         { 0,0 }
328     } },
329     { 0x00000004, 2, "Fire Power", {
330         { 0, "No fire-power kill" },
331         { 1, "Fire-power kill" },
332         { 0,0 }
333     } },
334     { 0x00000018, 3, "Damage", {
335         { 0, "No damage" },
336         { 1, "Slight damage" },
337         { 2, "Moderate damage" },
338         { 3, "Destroyed" },
339         { 0,0 }
340     } },
341     { 0, 0, 0, {
342         { 0, 0 }
343     } }
344 };
345
346 DIS_BitMask DIS_APPEARANCE_LIFEFORM[] =
347 {
348     { 0x00000001, 0, "Paint Scheme", {
349         { 0, "Uniform color" },
350         { 1, "Camouflage" },
351         { 0,0 }
352     } },
353     { 0x00000018, 3, "Health", {
354         { 0, "No injury" },
355         { 1, "Slight injury" },
356         { 2, "Moderate injury" },
357         { 3, "Fatal injury" },
358         { 0,0 }
359     } },
360     { 0, 0, 0, {
361         { 0, 0 }
362     } }
363 };
364
365 /* Initialize the field parsers that are not explicitly included in any
366  * specific PDU.  These fields are only accessed and used if a variant
367  * field indicates they are to be used.
368  */
369 void initializeFieldParsers()
370 {
371     initializeParser(DIS_FIELDS_VP_GENERIC);
372     initializeParser(DIS_FIELDS_VP_ARTICULATED_PART);
373     initializeParser(DIS_FIELDS_VP_ATTACHED_PART);
374     initializeParser(DIS_FIELDS_VP_ENTITY_OFFSET);
375
376     initializeParser(DIS_FIELDS_VR_APPLICATION_HEALTH_STATUS);
377     initializeParser(DIS_FIELDS_VR_APPLICATION_INITIALIZATION);
378     initializeParser(DIS_FIELDS_VR_DATA_QUERY);
379     initializeParser(DIS_FIELDS_MOD_PARAMS_CCTT_SINCGARS);
380     initializeParser(DIS_FIELDS_MOD_PARAMS_JTIDS_MIDS);
381
382 }
383
384 /* Adjust an offset variable for proper alignment for a specified field length.
385  */
386 static gint alignOffset(gint offset, guint fieldLength)
387 {
388     gint remainder = offset % fieldLength;
389     if (remainder != 0)
390     {
391         offset += fieldLength - remainder;
392     }
393     return offset;
394 }
395
396 /* Parse a field consisting of a specified number of bytes.  This field parser
397  * doesn't perform any alignment.
398  */
399 gint parseField_Bytes(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode, guint numBytes)
400 {
401     proto_tree_add_text(tree, tvb, offset, numBytes, "%s (%d bytes)",
402         parserNode.fieldLabel, numBytes);
403     offset += numBytes;
404     return offset;
405 }
406
407 /* Parse a bitmask field.
408  */
409 gint parseField_Bitmask(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode, guint numBytes)
410 {
411     DIS_BitMask *bitMask = 0;
412     guint64 uintVal = 0;
413
414     offset = alignOffset(offset, numBytes);
415
416     switch(numBytes)
417     {
418     case 1:
419         uintVal = tvb_get_guint8(tvb, offset);
420         break;
421     case 2:
422         uintVal = tvb_get_ntohs(tvb, offset);
423         break;
424     case 4:
425         uintVal = tvb_get_ntohl(tvb, offset);
426         break;
427     case 8:
428         uintVal = tvb_get_ntoh64(tvb, offset);
429         break;
430     default:
431         /* assert */
432         break;
433     }
434
435     switch(parserNode.fieldType)
436     {
437     case DIS_FIELDTYPE_APPEARANCE:
438         if ((entityKind == DIS_ENTITYKIND_PLATFORM) &&
439             (entityDomain == DIS_DOMAIN_LAND))
440         {
441             bitMask = DIS_APPEARANCE_LANDPLATFORM;
442         }
443         else if (entityKind == DIS_ENTITYKIND_LIFE_FORM)
444         {
445             bitMask = DIS_APPEARANCE_LIFEFORM;
446         }
447         break;
448     default:
449         break;
450     }
451
452     if (bitMask != 0)
453     {
454         int maskIndex = 0;
455         while (bitMask[maskIndex].maskBits != 0)
456         {
457             int mapIndex = 0;
458             DIS_BitMaskMapping *bitMaskMap = bitMask[maskIndex].bitMappings;
459
460             while (bitMaskMap[mapIndex].label != 0)
461             {
462                 if (((bitMask[maskIndex].maskBits & uintVal) >> bitMask[maskIndex].shiftBits) ==
463                     bitMaskMap[mapIndex].value)
464                 {
465                     proto_tree_add_text(tree, tvb, offset, numBytes,
466                         "%s = %s", bitMask[maskIndex].label,
467                         bitMaskMap[mapIndex].label);
468                     break;
469                 }
470                 ++mapIndex;
471             }
472             ++maskIndex;
473         }
474     }
475     else
476     {
477         proto_tree_add_text(tree, tvb, offset, numBytes,
478             "Unknown Appearance Type (%" G_GINT64_MODIFIER "u)", uintVal);
479     }
480
481     offset += numBytes;
482
483     return offset;
484 }
485
486 /* Parse an unsigned integer field of a specified number of bytes.
487  */
488 gint parseField_UInt(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode, guint numBytes)
489 {
490     guint64 uintVal = 0;
491
492     offset = alignOffset(offset, numBytes);
493
494     switch(numBytes)
495     {
496     case 1:
497         uintVal = tvb_get_guint8(tvb, offset);
498         break;
499     case 2:
500         uintVal = tvb_get_ntohs(tvb, offset);
501         break;
502     case 4:
503         uintVal = tvb_get_ntohl(tvb, offset);
504         break;
505     case 8:
506         uintVal = tvb_get_ntoh64(tvb, offset);
507         break;
508     default:
509         /* assert */
510         break;
511     }
512
513     proto_tree_add_text(tree, tvb, offset, numBytes, "%s = %" G_GINT64_MODIFIER "u",
514         parserNode.fieldLabel, uintVal);
515
516     if (parserNode.outputVar != 0)
517     {
518         *(parserNode.outputVar) = (guint32)uintVal;
519     }
520
521     offset += numBytes;
522
523     return offset;
524 }
525
526 /* Parse a signed integer field of a specified number of bytes.
527  */
528 gint parseField_Int(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode, guint numBytes)
529 {
530     guint64 uintVal = 0;
531
532     offset = alignOffset(offset, numBytes);
533
534     switch(numBytes)
535     {
536     case 1:
537         uintVal = tvb_get_guint8(tvb, offset);
538         break;
539     case 2:
540         uintVal = tvb_get_ntohs(tvb, offset);
541         break;
542     case 4:
543         uintVal = tvb_get_ntohl(tvb, offset);
544         break;
545     case 8:
546         uintVal = tvb_get_ntoh64(tvb, offset);
547         break;
548     default:
549         /* assert */
550         break;
551     }
552
553     proto_tree_add_text(tree, tvb, offset, numBytes, "%s = %" G_GINT64_MODIFIER "d",
554         parserNode.fieldLabel, uintVal);
555
556     offset += numBytes;
557
558     return offset;
559 }
560
561 /* Parse a field that explicitly specified a number of pad bytes (vs implicit
562  * padding, which occurs whenever padding is inserted to properly align the
563  * field.
564  */
565 gint parseField_Pad(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode _U_, guint numBytes)
566 {
567     proto_tree_add_text(tree, tvb, offset, numBytes,
568         "Explicit Padding (%d bytes)", numBytes);
569
570     offset += numBytes;
571
572     return offset;
573 }
574
575 /* Parse an enumerated type field.
576  */
577 gint parseField_Enum(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode, guint numBytes)
578 {
579     const value_string *enumStrings = 0;
580     guint32 enumVal = 0;
581     const gchar *enumStr = 0;
582     proto_item *pi = NULL;
583     int dis_hf_id = -1;
584
585     offset = alignOffset(offset, numBytes);
586
587     switch(parserNode.fieldType)
588     {
589     case DIS_FIELDTYPE_ACKNOWLEDGE_FLAG:
590         enumStrings = DIS_PDU_AcknowledgeFlag_Strings;
591         break;
592     case DIS_FIELDTYPE_ACTION_ID:
593         enumStrings = DIS_PDU_ActionId_Strings;
594         break;
595     case DIS_FIELDTYPE_APPLICATION_GENERAL_STATUS:
596         enumStrings = DIS_PDU_ApplicationGeneralStatus_Strings;
597         break;
598     case DIS_FIELDTYPE_APPLICATION_STATUS_TYPE:
599         enumStrings = DIS_PDU_ApplicationStatusType_Strings;
600         break;
601     case DIS_FIELDTYPE_APPLICATION_TYPE:
602         enumStrings = DIS_PDU_ApplicationType_Strings;
603         break;
604     case DIS_FIELDTYPE_CONTROL_ID:
605         enumStrings = DIS_PDU_ControlId_Strings;
606         break;
607     case DIS_FIELDTYPE_PROTOCOL_VERSION:
608         enumStrings = DIS_PDU_ProtocolVersion_Strings;
609         dis_hf_id = hf_dis_proto_ver;
610         break;
611     case DIS_FIELDTYPE_PROTOCOL_FAMILY:
612         enumStrings = DIS_PDU_ProtocolFamily_Strings;
613         dis_hf_id = hf_dis_proto_fam;
614         break;
615     case DIS_FIELDTYPE_PDU_TYPE:
616         enumStrings = DIS_PDU_Type_Strings;
617         dis_hf_id = hf_dis_pdu_type;
618         break;
619     case DIS_FIELDTYPE_ENTITY_KIND:
620         enumStrings = DIS_PDU_EntityKind_Strings;
621         break;
622     case DIS_FIELDTYPE_DOMAIN:
623         enumStrings = DIS_PDU_Domain_Strings;
624         break;
625     case DIS_FIELDTYPE_DETONATION_RESULT:
626         enumStrings = DIS_PDU_DetonationResult_Strings;
627         break;
628     case DIS_FIELDTYPE_FROZEN_BEHAVIOR:
629         enumStrings = DIS_PDU_FrozenBehavior_Strings;
630         break;
631     case DIS_FIELDTYPE_RADIO_CATEGORY:
632         enumStrings = DIS_PDU_RadioCategory_Strings;
633         break;
634     case DIS_FIELDTYPE_NOMENCLATURE_VERSION:
635         enumStrings = DIS_PDU_NomenclatureVersion_Strings;
636         break;
637     case DIS_FIELDTYPE_NOMENCLATURE:
638         enumStrings = DIS_PDU_Nomenclature_Strings;
639         break;
640     case DIS_FIELDTYPE_CATEGORY:
641         if (entityKind == DIS_ENTITYKIND_PLATFORM)
642         {
643             switch(entityDomain)
644             {
645             case DIS_DOMAIN_LAND:
646                 enumStrings = DIS_PDU_Category_LandPlatform_Strings;
647                 break;
648             case DIS_DOMAIN_AIR:
649                 enumStrings = DIS_PDU_Category_AirPlatform_Strings;
650                 break;
651             case DIS_DOMAIN_SURFACE:
652                 enumStrings = DIS_PDU_Category_SurfacePlatform_Strings;
653                 break;
654             case DIS_DOMAIN_SUBSURFACE:
655                 enumStrings = DIS_PDU_Category_SubsurfacePlatform_Strings;
656                 break;
657             case DIS_DOMAIN_SPACE:
658                 enumStrings = DIS_PDU_Category_SpacePlatform_Strings;
659                 break;
660             default:
661                 enumStrings = 0;
662                 break;
663             }
664         }
665         break;
666     case DIS_FIELDTYPE_PARAMETER_TYPE_DESIGNATOR:
667         enumStrings = DIS_PDU_ParameterTypeDesignator_Strings;
668         break;
669     case DIS_FIELDTYPE_PERSISTENT_OBJECT_TYPE:
670         enumStrings = DIS_PDU_PersistentObjectType_Strings;
671         break;
672     case DIS_FIELDTYPE_PERSISTENT_OBJECT_CLASS:
673         enumStrings = DIS_PDU_PO_ObjectClass_Strings;
674         break;
675     case DIS_FIELDTYPE_REASON:
676         enumStrings = DIS_PDU_Reason_Strings;
677         break;
678     case DIS_FIELDTYPE_REQUEST_STATUS:
679         enumStrings = DIS_PDU_RequestStatus_Strings;
680         break;
681     case DIS_FIELDTYPE_REQUIRED_RELIABILITY_SERVICE:
682         enumStrings = DIS_PDU_RequiredReliabilityService_Strings;
683         break;
684     case DIS_FIELDTYPE_RESPONSE_FLAG:
685         enumStrings = DIS_PDU_DisResponseFlag_Strings;
686         break;
687     case DIS_FIELDTYPE_MODULATION_DETAIL:
688         switch (majorModulation) {
689         case DIS_MAJOR_MOD_AMPLITUDE:
690             enumStrings = DIS_PDU_DetailModulationAmplitude_Strings;
691             break;
692         case DIS_MAJOR_MOD_AMPLITUDE_AND_ANGLE:
693             enumStrings = DIS_PDU_DetailModulationAmpAndAngle_Strings;
694             break;
695         case DIS_MAJOR_MOD_ANGLE:
696             enumStrings = DIS_PDU_DetailModulationAngle_Strings;
697             break;
698         case DIS_MAJOR_MOD_COMBINATION:
699             enumStrings = DIS_PDU_DetailModulationCombination_Strings;
700             break;
701         case DIS_MAJOR_MOD_PULSE:
702             enumStrings = DIS_PDU_DetailModulationPulse_Strings;
703             break;
704         case DIS_MAJOR_MOD_UNMODULATED:
705             enumStrings = DIS_PDU_DetailModulationUnmodulated_Strings;
706             break;
707         case DIS_MAJOR_MOD_CPSM: /* CPSM only has "other" defined */
708         case DIS_MAJOR_MOD_OTHER:
709         default:
710             enumStrings = DIS_PDU_DetailModulationCPSM_Strings;
711             break;
712         }
713         break;
714     default:
715         enumStrings = 0;
716         break;
717     }
718
719     switch(numBytes)
720     {
721     case 1:
722         enumVal = tvb_get_guint8(tvb, offset);
723         break;
724     case 2:
725         enumVal = tvb_get_ntohs(tvb, offset);
726         break;
727     case 4:
728         enumVal = tvb_get_ntohl(tvb, offset);
729         break;
730     default:
731         /* assert */
732         break;
733     }
734
735     if (enumStrings != 0)
736     {
737         enumStr = val_to_str(enumVal, enumStrings, "Unknown Enum Value (%d)");
738     }
739     else
740     {
741         enumStr = "Unknown Enum Type";
742     }
743
744     if (dis_hf_id != -1) {
745        pi = proto_tree_add_item(tree, dis_hf_id, tvb, offset, 1, FALSE);
746 /*       proto_item_set_text(pi, "%s = %s", parserNode.fieldLabel, enumStr); */
747     }
748     else
749        proto_tree_add_text(tree, tvb, offset, numBytes, "%s = %s",
750            parserNode.fieldLabel, enumStr);
751
752     if (parserNode.outputVar != 0)
753     {
754         *(parserNode.outputVar) = enumVal;
755     }
756
757     offset += numBytes;
758
759     return offset;
760 }
761
762 /* Parse a 4-byte floating-point value.
763  */
764 gint parseField_Float(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode)
765 {
766     gfloat floatVal;
767
768     offset = alignOffset(offset, 4);
769     floatVal = tvb_get_ntohieee_float(tvb, offset);
770     proto_tree_add_text(tree, tvb, offset, 4, "%s = %f",
771         parserNode.fieldLabel, floatVal);
772
773     offset += 4;
774
775     return offset;
776 }
777
778 /* Parse a 4-byte floating-point value, given text label.
779  */
780 gint parseField_Float_Text(tvbuff_t *tvb, proto_tree *tree, gint offset, gchar *charStr)
781 {
782     gfloat floatVal;
783
784     offset = alignOffset(offset, 4);
785     floatVal = tvb_get_ntohieee_float(tvb, offset);
786     proto_tree_add_text(tree, tvb, offset, 4, "%s = %f",
787         charStr, floatVal);
788
789     offset += 4;
790
791     return offset;
792 }
793
794 /* Parse an 8-byte floating-point value.
795  */
796 gint parseField_Double(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode)
797 {
798     gdouble doubleVal;
799
800     offset = alignOffset(offset, 8);
801     doubleVal = tvb_get_ntohieee_double(tvb, offset);
802     proto_tree_add_text(tree, tvb, offset, 8, "%s = %f",
803         parserNode.fieldLabel, doubleVal);
804
805     offset += 8;
806
807     return offset;
808 }
809
810 /* Parse the Timestamp */
811 gint parseField_Timestamp(tvbuff_t *tvb, proto_tree *tree, gint offset, DIS_ParserNode parserNode)
812 {
813    /* some consts */
814    static double MSEC_PER_SECOND = 1000.0;
815    static double MSEC_PER_MINUTE = 60.0 * 1000.0 ;
816    static double MSEC_PER_HOUR = 60.0 * 60.0 * 1000.0;
817    static double FSV = 0x7fffffff;
818    /* variables */
819    guint isAbsolute = 0;
820    guint32 uintVal;
821    guint minutes;
822    guint seconds;
823    guint milliseconds;
824    double ms;
825
826    offset = alignOffset(offset, 4);
827
828    /* convert to host value */
829    uintVal = tvb_get_ntohl(tvb, offset);
830    /* determine absolute vis sim time */
831    if( uintVal & 1 )
832       isAbsolute = 1;
833
834    /* convert TS to MS */
835    ms = (uintVal >> 1) * MSEC_PER_HOUR / FSV;
836    ms += 0.5;
837
838    /* calc minutes and reduce ms */
839    minutes = (guint) (ms / MSEC_PER_MINUTE);
840    ms -= (minutes * MSEC_PER_MINUTE);
841
842    /* calc seconds and reduce ms */
843    seconds = (guint) (ms / MSEC_PER_SECOND);
844    ms -= (seconds * MSEC_PER_SECOND);
845
846    /* truncate milliseconds */
847    milliseconds = (guint) ms;
848
849    /* push out the values */
850    if( isAbsolute )
851    {
852       proto_tree_add_text(tree, tvb, offset, 4, "%s = %02d:%02d %03d absolute (UTM)",
853             parserNode.fieldLabel, minutes, seconds, milliseconds);
854    }
855    else
856    {
857       proto_tree_add_text(tree, tvb, offset, 4, "%s = %02d:%02d %03d relative",
858             parserNode.fieldLabel, minutes, seconds, milliseconds);
859    }
860
861    offset += 4;
862    return offset;
863 }
864
865 /* Parse a variable parameter field.
866  */
867 gint parseField_VariableParameter(tvbuff_t *tvb, proto_tree *tree, gint offset)
868 {
869     DIS_ParserNode *paramParser = 0;
870
871     /* Determine the parser to use based on the type */
872     switch (variableParameterType) {
873     case DIS_PARAM_TYPE_DESIG_ARTICULATED_PART:
874         paramParser = DIS_FIELDS_VP_ARTICULATED_PART;
875         break;
876     case DIS_PARAM_TYPE_DESIG_ATTACHED_PART:
877         paramParser = DIS_FIELDS_VP_ATTACHED_PART;
878         break;
879     case DIS_PARAM_TYPE_DESIG_ENTITY_OFFSET:
880         paramParser = DIS_FIELDS_VP_ENTITY_OFFSET;
881         break;
882     default:
883         paramParser = DIS_FIELDS_VP_GENERIC;
884         break;
885     }
886
887     /* Parse the variable parameter fields */
888     if (paramParser)
889     {
890         offset = parseFields(tvb, tree, offset, paramParser);
891     }
892
893     return offset;
894 }
895
896 /* Parse a variable record field.
897  */
898 gint parseField_VariableRecord(tvbuff_t *tvb, proto_tree *tree, gint offset)
899 {
900     DIS_ParserNode *paramParser = 0;
901
902     /* Determine the parser to use based on the type */
903     switch (variableRecordType) {
904     case 47200:
905         paramParser = DIS_FIELDS_VR_APPLICATION_HEALTH_STATUS;
906         break;
907     case 47300:
908         paramParser = DIS_FIELDS_VR_APPLICATION_INITIALIZATION;
909         break;
910     case 47600:
911         paramParser = DIS_FIELDS_VR_DATA_QUERY;
912         break;
913     default:
914         {
915             guint32 dataLength = variableRecordLength - 6;
916
917             if (dataLength > 0)
918             {
919                 proto_tree_add_text(tree, tvb, offset, dataLength,
920                     "Record Data (%d bytes)", dataLength);
921                 offset += dataLength;
922             }
923         }
924         break;
925     }
926
927     /* Parse the variable record fields */
928     if (paramParser)
929     {
930         offset = parseFields(tvb, tree, offset, paramParser);
931     }
932
933     /* Should alignment padding be added */
934     if (variableRecordLength % 8)
935     {
936         guint32 alignmentPadding = (8 - (variableRecordLength % 8));
937
938         proto_tree_add_text(tree, tvb, offset, alignmentPadding,
939             "Alignment Padding (%d bytes)", alignmentPadding);
940         offset += alignmentPadding;
941     }
942
943     return offset;
944 }