change the signature that asn2wrs generates for functions to marm all parameters...
[obnox/wireshark/wip.git] / epan / dissectors / packet-image-gif.c
1 /* packet-image-gif.c
2  *
3  * Routines for image/gif media dissection
4  * Copyright 2003, 2004, Olivier Biot.
5  *
6  * $Id$
7  *
8  * Refer to the AUTHORS file or the AUTHORS section in the man page
9  * for contacting the author(s) of this file.
10  *
11  * Wireshark - Network traffic analyzer
12  * By Gerald Combs <gerald@wireshark.org>
13  * Copyright 1998 Gerald Combs
14  *
15  * Compuserve GIF media decoding functionality provided by Olivier Biot.
16  * 
17  * The two GIF specifications are found at several locations, such as W3C:
18  * http://www.w3.org/Graphics/GIF/spec-gif87.txt
19  * http://www.w3.org/Graphics/GIF/spec-gif89a.txt
20  * 
21  * This program is free software; you can redistribute it and/or
22  * modify it under the terms of the GNU General Public License
23  * as published by the Free Software Foundation; either version 2
24  * of the License, or (at your option) any later version.
25  * 
26  * This program is distributed in the hope that it will be useful,
27  * but WITHOUT ANY WARRANTY; without even the implied warranty of
28  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
29  * GNU General Public License for more details.
30  * 
31  * You should have received a copy of the GNU General Public License
32  * along with this program; if not, write to the Free Software
33  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
34  */
35
36 /* Edit this file with 4-space tabulation */
37
38 #ifdef HAVE_CONFIG_H
39 # include "config.h"
40 #endif
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include <glib.h>
47
48 #include <epan/packet.h>
49
50 /* General-purpose debug logger.
51  * Requires double parentheses because of variable arguments of printf().
52  *
53  * Enable debug logging for GIF by defining AM_CFLAGS
54  * so that it contains "-DDEBUG_image_gif" or "-DDEBUG_image"
55  */
56 #if (defined(DEBUG_image_gif) || defined(DEBUG_image))
57 #define DebugLog(x) \
58         g_print("%s:%u: ", __FILE__, __LINE__); \
59         g_print x
60 #else
61 #define DebugLog(x) ;
62 #endif
63
64 #define IMG_GIF "image-gif"
65
66 /************************** Variable declarations **************************/
67
68 static const value_string vals_true_false[] = {
69         { 0, "False" },
70         { 1, "True" },
71         { 0, NULL },
72 };
73
74 static const value_string vals_extensions[] = {
75         { 0xF9, "Graphics Control" },
76         { 0xFE, "Comment" },
77         { 0xFF, "Application" },
78         { 0x01, "Plain Text" },
79         { 0x00, NULL, },
80 };
81
82 enum {
83         GIF_UNKNOWN = 0,
84         GIF_87a = 0x87,
85         GIF_89a = 0x89,
86         GIF_ERROR = 0xFF
87 };
88
89 /* Initialize the protocol and registered fields */
90 static int proto_gif = -1;
91
92 /* header fields */
93 /* GIF signature */
94 static gint hf_version = -1;
95 /* Screen descriptor */
96 static gint hf_screen_width = -1;
97 static gint hf_screen_height = -1;
98 static gint hf_global_color_map_present = -1;
99 static gint hf_global_color_resolution = -1;
100 static gint hf_global_color_map_ordered = -1; /* GIF89a */
101 static gint hf_global_image_bpp = -1;
102 /* Only makes sense if the global color map is present: */
103 static gint hf_background_color = -1;
104 static gint hf_pixel_aspect_ratio = -1; /* GIF89a */
105 static gint hf_global_color_map = -1;
106
107 /* Image descriptor */
108 static gint hf_image_left = -1;
109 static gint hf_image_top = -1;
110 static gint hf_image_width = -1;
111 static gint hf_image_height = -1;
112 static gint hf_local_color_map_present = -1;
113 static gint hf_local_color_resolution = -1;
114 static gint hf_local_color_map_ordered = -1; /* GIF89a */
115 static gint hf_local_image_bpp = -1;
116 static gint hf_local_color_map = -1;
117
118 static gint hf_extension = -1;
119 static gint hf_extension_label = -1;
120 static gint hf_image = -1;
121 static gint hf_image_code_size = -1;
122
123 /* Trailer (end of GIF data stream) */
124 static gint hf_trailer = -1;
125
126 /* Initialize the subtree pointers */
127 static gint ett_gif = -1;
128 static gint ett_global_flags = -1;
129 static gint ett_local_flags = -1;
130 static gint ett_extension = -1;
131 static gint ett_image = -1;
132
133
134 /**************** GIF related declarations and definitions ****************/
135
136
137 /************************** Function prototypes **************************/
138
139
140 static void
141 dissect_gif(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
142
143 void
144 proto_register_gif(void);
145
146
147 /****************** GIF protocol dissection functions ******************/
148
149 /* There are two Compuserve GIF standards: GIF87a and GIF89a. GIF image files
150  * always convey their version in the first 6 bytes, written as an US-ASCII
151  * string representation of the version: "GIF87a" or "GIF89a".
152  */
153
154 static void
155 dissect_gif(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
156 {
157         proto_item *ti;
158         proto_tree *gif_tree; /* Main GIF tree */
159         proto_tree *subtree; /* Main GIF tree */
160         guint offset = 0, len = 0;
161         guint8 peek;
162         gboolean color_map_present;
163         guint8 color_resolution;
164         guint8 image_bpp;
165         guint tvb_len = tvb_reported_length(tvb);
166         char *str = tvb_get_ephemeral_string(tvb, 0, 6);
167         guint8 version;
168
169         /* Check whether we're processing a GIF object */
170         if (strcmp(str, "GIF87a") == 0) {
171                 version = GIF_87a;
172         } else if (strcmp(str, "GIF89a") == 0) {
173                 version = GIF_89a;
174         } else if (strncmp(str,"GIF", 3) == 0) {
175                 version = GIF_UNKNOWN;
176         } else {
177                 /* Not a GIF image! */
178                 version = GIF_ERROR;
179                 return;
180         }
181         /* Add summary to INFO column if it is enabled */
182         if (check_col(pinfo->cinfo, COL_INFO))
183                 col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)", str);
184
185         /* In order to speed up dissection, do not add items to the protocol tree
186          * if it is not visible. However, compute the values that are needed for
187          * correct protocol dissection if they have more meaning than just adding
188          * items to the protocol tree.
189          */
190         if (tree) {
191                 ti = proto_tree_add_item(tree, proto_gif, tvb, 0, -1, TRUE);
192                 proto_item_append_text(ti, ", Version: %s", str);
193                 gif_tree = proto_item_add_subtree(ti, ett_gif);
194                 /* GIF signature */
195                 ti = proto_tree_add_item(gif_tree, hf_version, tvb, 0, 6, TRUE);
196                 if (version == GIF_UNKNOWN) {
197                         proto_item_append_text(ti, " <Error: unknown GIF version>");
198                 }
199                 /* Screen descriptor */
200                 proto_tree_add_item(gif_tree, hf_screen_width, tvb, 6, 2, TRUE);
201                 proto_tree_add_item(gif_tree, hf_screen_height, tvb, 8, 2, TRUE);
202
203                 peek = tvb_get_guint8(tvb, 10);
204                 /* Bitfield gccc 0ppp
205                  *                      g... .... : global color map present
206                  *                      .ccc .... : color resolution in bits (add one)
207                  *                      .... 0... : GIF87a - reserved (no use)
208                  *                                              GIF89a - ordered (most important color 1st)
209                  *                      .... .ppp : bits per pixel in image (add one)
210                  */
211                 color_map_present = peek & 0x80;
212                 color_resolution = 1 + ((peek & 0x60) >> 4);
213                 image_bpp = 1 + (peek & 0x07);
214         
215                 ti = proto_tree_add_text(gif_tree, tvb, 10, 1,
216                                 "Global settings:");
217                 if (color_map_present)
218                         proto_item_append_text(ti, " (Global color table present)");
219                 proto_item_append_text(ti,
220                                 " (%u bit%s per color) (%u bit%s per pixel)",
221                                 color_resolution, plurality(color_resolution, "", "s"),
222                                 image_bpp, plurality(image_bpp, "", "s"));
223                 subtree = proto_item_add_subtree(ti, ett_global_flags);
224                 proto_tree_add_item(subtree, hf_global_color_map_present,
225                                 tvb, 10, 1, TRUE);
226                 proto_tree_add_item(subtree, hf_global_color_resolution,
227                                 tvb, 10, 1, TRUE);
228                 if (version == GIF_89a) {
229                         proto_tree_add_item(subtree, hf_global_color_map_ordered,
230                                         tvb, 10, 1, TRUE);
231                 }
232                 proto_tree_add_item(subtree, hf_global_image_bpp,
233                                 tvb, 10, 1, TRUE);
234
235                 /* Background color */
236                 proto_tree_add_item(gif_tree, hf_background_color,
237                                 tvb, 11, 1, TRUE);
238
239                 /* byte at offset 12 is 0x00 - reserved in GIF87a but encodes the
240                  * pixel aspect ratio in GIF89a as:
241                  *              aspect-ratio = (15 + pixel-aspect-ratio) / 64
242                  * where the aspect-ratio is not computed if pixel-aspect-ratio == 0
243                  */
244                 if (version == GIF_89a) {
245                         peek = tvb_get_guint8(tvb, 12);
246                         if (peek) {
247                                 /* Only display if different from 0 */
248                                 proto_tree_add_uint_format(gif_tree, hf_pixel_aspect_ratio,
249                                                 tvb, 12, 1, peek,
250                                                 "%u, yields an aspect ratio of (15 + %u) / 64 = %.2f",
251                                                 peek, peek, (float)(15 + peek) / 64.0);
252                         }
253                 }
254
255                 /* Global color map
256                  * If present, it takes 2 ^ (image_bpp) byte tuples (R, G, B)
257                  * that contain the Red, Green and Blue intensity of the colors
258                  * in the Global Color Map */
259                 if (color_map_present) {
260                         len = 3 * (1 << image_bpp);
261                         proto_tree_add_item(gif_tree, hf_global_color_map,
262                                         tvb, 13, len, TRUE);
263                 } else {
264                         len = 0;
265                 }
266                 offset = 13 + len;
267                 /* From now on, a set of images prefixed with the image separator
268                  * character 0x2C (',') will appear in the byte stream. Each image
269                  * hence consists of:
270                  * - The image separator character 0x2C
271                  * - Image left (16 bits LSB first): pixels from left border
272                  * - Image top (16 bits LSB first): pixels from to border
273                  * - Image width (16 bits LSB first)
274                  * - Image height (16 bits LSB first)
275                  * - A bitfield MI00 0ppp
276                  *                              M... .... : Use global color map if unset (ignore ppp);
277                  *                                                      if set a local color map will be defined.
278                  *                              .I.. .... : Image formatted in interlaced order if set;
279                  *                                                      otherwise it is plain sequential order
280                  *                              ..0. .... : GIF87a - Reserved
281                  *                              ..s. ....       GIF89a - Set if local color map is ordered
282                  *                              ...0 0... : Reserved
283                  *                              .... .ppp : bits per pixel in image (add one)
284                  * - If the local color map bit is set, then a local color table follows
285                  *   with length = 3 x 2 ^ (1 + bits per pixel)
286                  * - The raster data
287                  *
288                  * NOTE that the GIF specification only requires that:
289                  *              image left + image width  <= screen width
290                  *              image top  + image height <= screen height
291                  *
292                  * The Raster Data is encoded as follows:
293                  * - Code size (1 byte)
294                  * - Blocks consisting of
295                  *              o Byte count (1 byte): number of bytes in the block
296                  *              o Data bytes: as many as specified in the byte count
297                  *       End of data is given with an empty block (byte count == 0).
298                  *
299                  *
300                  * GIF terminator
301                  * This is a synchronization method, based on the final character 0xB3
302                  * (';') at the end of an image
303                  *
304                  *
305                  * GIF extension
306                  * This is a block of data encoded as:
307                  * - The GIF extension block introducer 0x21 ('!')
308                  * - The extension function code (1 byte)
309                  * - Blocks consisting of
310                  *              o Byte count (1 byte): number of bytes in the block
311                  *              o Data bytes: as many as specified in the byte count
312                  *       End of data is given with an empty block (byte count == 0).
313                  *
314                  * NOTE that the GIF extension block can only appear at the following
315                  * locations:
316                  * - Immediately before an Image Descriptor
317                  * - Before the GIF termination character
318                  */
319                 while (offset < tvb_len) {
320                         peek = tvb_get_guint8(tvb, offset);
321                         if (peek == 0x21) { /* GIF extension block */
322                                 guint32 item_len = 2;   /* Fixed header consisting of:
323                                                                                  *      1 byte : 0x21
324                                                                                  *      1 byte : extension_label
325                                                                                  */
326
327                                 ti = proto_tree_add_item(gif_tree, hf_extension,
328                                                 tvb, offset, 1, TRUE);
329                                 subtree = proto_item_add_subtree(ti, ett_extension);
330                                 offset++;
331                                 proto_tree_add_item(subtree, hf_extension_label,
332                                                 tvb, offset, 1, TRUE);
333                                 peek = tvb_get_guint8(tvb, offset);
334                                 proto_item_append_text(ti, ": %s",
335                                                 val_to_str(peek, vals_extensions,
336                                                         "<Warning: Unknown extension 0x%02X>"));
337                                 offset++;
338                                 do {
339                                         /* Read length of data block */
340                                         len = tvb_get_guint8(tvb, offset);
341                                         proto_tree_add_text(subtree, tvb,
342                                                         offset, 1 + len,
343                                                         "Data block (length = %u)", len);
344                                         offset += (1 + len);
345                                         item_len += (1 + len);
346                                 } while (len > 0);
347                                 proto_item_set_len(ti, item_len);
348                         } else if (peek == 0x2C) { /* Image separator */
349                                 proto_tree *subtree2;
350                                 proto_item *ti2;
351                                 guint32 item_len = 11;  /* Fixed header consisting of:
352                                                                                  *      1 byte : 0x2C
353                                                                                  *      2 bytes: image_left
354                                                                                  *      2 bytes: image_top
355                                                                                  *      2 bytes: image_width
356                                                                                  *      2 bytes: image height
357                                                                                  *      1 byte : packed bit field
358                                                                                  *      1 byte : image code size
359                                                                                  */
360
361                                 ti = proto_tree_add_item(gif_tree, hf_image,
362                                                 tvb, offset, 1, TRUE);
363                                 subtree = proto_item_add_subtree(ti, ett_image);
364                                 offset++;
365                                 /* Screen descriptor */
366                                 proto_tree_add_item(subtree, hf_image_left,
367                                                 tvb, offset, 2, TRUE); offset += 2;
368                                 proto_tree_add_item(subtree, hf_image_top,
369                                                 tvb, offset, 2, TRUE); offset += 2;
370                                 proto_tree_add_item(subtree, hf_image_width,
371                                                 tvb, offset, 2, TRUE); offset += 2;
372                                 proto_tree_add_item(subtree, hf_image_height,
373                                                 tvb, offset, 2, TRUE); offset += 2;
374                                 /* bit field */
375                                 peek = tvb_get_guint8(tvb, offset);
376                                 color_map_present = peek & 0x80;
377                                 color_resolution = 1 + ((peek & 0x60) >> 4);
378                                 image_bpp = 1 + (peek & 0x07);
379         
380                                 ti2 = proto_tree_add_text(subtree, tvb, offset, 1,
381                                                 "Local settings:");
382                                 if (color_map_present)
383                                         proto_item_append_text(ti2, " (Local color table present)");
384                                 proto_item_append_text(ti2,
385                                                 " (%u bit%s per color) (%u bit%s per pixel)",
386                                                 color_resolution, plurality(color_resolution, "", "s"),
387                                                 image_bpp, plurality(image_bpp, "", "s"));
388                                 subtree2 = proto_item_add_subtree(ti2, ett_local_flags);
389                                 proto_tree_add_item(subtree2, hf_local_color_map_present,
390                                                 tvb, offset, 1, TRUE);
391                                 proto_tree_add_item(subtree2, hf_local_color_resolution,
392                                                 tvb, offset, 1, TRUE);
393                                 if (version == GIF_89a) {
394                                         proto_tree_add_item(subtree2, hf_local_color_map_ordered,
395                                                         tvb, offset, 1, TRUE);
396                                 }
397                                 proto_tree_add_item(subtree2, hf_global_image_bpp,
398                                                 tvb, offset, 1, TRUE);
399                                 offset++;
400
401                                 /* Local color map
402                                  * If present, it takes 2 ^ (image_bpp) byte tuples (R, G, B)
403                                  * that contain the Red, Green and Blue intensity of the colors
404                                  * in the Local Color Map */
405                                 if (color_map_present) {
406                                         len = 3 * (1 << image_bpp);
407                                         proto_tree_add_item(subtree, hf_local_color_map,
408                                                         tvb, offset, len, TRUE);
409                                 } else {
410                                         len = 0;
411                                 }
412                                 offset += len;
413                                 item_len += len;
414
415                                 proto_tree_add_item(subtree, hf_image_code_size,
416                                                 tvb, offset, 1, TRUE);
417                                 offset++;
418                                 do {
419                                         /* Read length of data block */
420                                         len = tvb_get_guint8(tvb, offset);
421                                         proto_tree_add_text(subtree, tvb,
422                                                         offset, 1 + len,
423                                                         "Data block (length = %u)", len);
424                                         offset += 1 + len;
425                                         item_len += (1 + len);
426                                 } while (len > 0);
427                                 proto_item_set_len(ti, item_len);
428                         } else {
429                                 /* GIF processing stops at this very byte */
430                                 proto_tree_add_item(gif_tree, hf_trailer,
431                                                 tvb, offset, 1, TRUE);
432                                 break;
433                         }
434                 } /* while */
435         }
436 }
437
438
439 /****************** Register the protocol with Wireshark ******************/
440
441
442 /* This format is required because a script is used to build the C function
443  * that calls the protocol registration. */
444
445 void
446 proto_register_gif(void)
447 {
448         /*
449          * Setup list of header fields.
450          */
451         static hf_register_info hf[] = {
452                 /*
453                  * GIF signature and version
454                  */
455                 { &hf_version,
456                         {       "Version",
457                                 IMG_GIF ".version",
458                                 FT_STRING, BASE_NONE, NULL, 0x00,
459                                 "GIF Version",
460                                 HFILL
461                         }
462                 },
463
464                 /*
465                  * Logical screen descriptor
466                  */
467                 { &hf_screen_width,
468                         {       "Screen width",
469                                 IMG_GIF ".screen.width",
470                                 FT_UINT16, BASE_DEC, NULL, 0x00,
471                                 "Screen width",
472                                 HFILL
473                         }
474                 },
475                 { &hf_screen_height,
476                         {       "Screen height",
477                                 IMG_GIF ".screen.height",
478                                 FT_UINT16, BASE_DEC, NULL, 0x00,
479                                 "Screen height",
480                                 HFILL
481                         }
482                 },
483                 { &hf_global_color_map_present,
484                         {       "Global color map is present",
485                                 IMG_GIF ".global.color_map.present",
486                                 FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x80,
487                                 "Indicates if the global color map is present",
488                                 HFILL
489                         }
490                 },
491                 { &hf_global_color_resolution,
492                         {       "Bits per color minus 1",
493                                 IMG_GIF ".global.color_bpp",
494                                 FT_UINT8, BASE_DEC, NULL, 0x70,
495                                 "The number of bits per color is one plus the field value.",
496                                 HFILL
497                         }
498                 },
499                 { &hf_global_color_map_ordered,
500                         {       "Global color map is ordered",
501                                 IMG_GIF ".global.color_map.ordered",
502                                 FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x08,
503                                 "Indicates whether the global color map is ordered.",
504                                 HFILL
505                         }
506                 },
507                 { &hf_global_image_bpp,
508                         {       "Image bits per pixel minus 1",
509                                 IMG_GIF ".global.bpp",
510                                 FT_UINT8, BASE_DEC, NULL, 0x07,
511                                 "The number of bits per pixel is one plus the field value.",
512                                 HFILL
513                         }
514                 },
515                 { &hf_background_color,
516                         {       "Background color index",
517                                 IMG_GIF ".image_background_index",
518                                 FT_UINT8, BASE_DEC, NULL, 0x00,
519                                 "Index of the background color in the color map.",
520                                 HFILL
521                         }
522                 },
523                 { &hf_pixel_aspect_ratio,
524                         {       "Global pixel aspect ratio",
525                                 IMG_GIF ".global.pixel_aspect_ratio",
526                                 FT_UINT8, BASE_DEC, NULL, 0x00,
527                                 "Gives an approximate value of the aspect ratio of the pixels.",
528                                 HFILL
529                         }
530                 },
531                 { &hf_global_color_map,
532                         {       "Global color map",
533                                 IMG_GIF ".global.color_map",
534                                 FT_BYTES, BASE_NONE, NULL, 0x00,
535                                 "Global color map.",
536                                 HFILL
537                         }
538                 },
539
540                 /*
541                  * Local color map (part of image)
542                  */
543                 { &hf_local_color_map_present,
544                         {       "Local color map is present",
545                                 IMG_GIF ".local.color_map.present",
546                                 FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x80,
547                                 "Indicates if the local color map is present",
548                                 HFILL
549                         }
550                 },
551                 { &hf_local_color_resolution,
552                         {       "Bits per color minus 1",
553                                 IMG_GIF ".local.color_bpp",
554                                 FT_UINT8, BASE_DEC, NULL, 0x70,
555                                 "The number of bits per color is one plus the field value.",
556                                 HFILL
557                         }
558                 },
559                 { &hf_local_color_map_ordered,
560                         {       "Local color map is ordered",
561                                 IMG_GIF ".local.color_map.ordered",
562                                 FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x08,
563                                 "Indicates whether the local color map is ordered.",
564                                 HFILL
565                         }
566                 },
567                 { &hf_local_image_bpp,
568                         {       "Image bits per pixel minus 1",
569                                 IMG_GIF ".local.bpp",
570                                 FT_UINT8, BASE_DEC, NULL, 0x07,
571                                 "The number of bits per pixel is one plus the field value.",
572                                 HFILL
573                         }
574                 },
575                 { &hf_local_color_map,
576                         {       "Local color map",
577                                 IMG_GIF ".local.color_map",
578                                 FT_BYTES, BASE_NONE, NULL, 0x00,
579                                 "Local color map.",
580                                 HFILL
581                         }
582                 },
583
584                 /*
585                  * Extension
586                  */
587                 { &hf_extension,
588                         {       "Extension",
589                                 IMG_GIF ".extension",
590                                 FT_NONE, BASE_NONE, NULL, 0x00,
591                                 "Extension.",
592                                 HFILL
593                         }
594                 },
595                 { &hf_extension_label,
596                         {       "Extension label",
597                                 IMG_GIF ".extension.label",
598                                 FT_UINT8, BASE_HEX, VALS(vals_extensions), 0x00,
599                                 "Extension label.",
600                                 HFILL
601                         }
602                 },
603
604                 /*
605                  * Image
606                  */
607                 { &hf_image,
608                         {       "Image",
609                                 IMG_GIF ".image",
610                                 FT_NONE, BASE_NONE, NULL, 0x00,
611                                 "Image.",
612                                 HFILL
613                         }
614                 },
615                 { &hf_image_left,
616                         {       "Image left position",
617                                 IMG_GIF ".image.left",
618                                 FT_UINT16, BASE_DEC, NULL, 0x00,
619                                 "Offset between left of Screen and left of Image.",
620                                 HFILL
621                         }
622                 },
623                 { &hf_image_top,
624                         {       "Image top position",
625                                 IMG_GIF ".image.top",
626                                 FT_UINT16, BASE_DEC, NULL, 0x00,
627                                 "Offset between top of Screen and top of Image.",
628                                 HFILL
629                         }
630                 },
631                 { &hf_image_width,
632                         {       "Image width",
633                                 IMG_GIF ".image.width",
634                                 FT_UINT16, BASE_DEC, NULL, 0x00,
635                                 "Image width.",
636                                 HFILL
637                         }
638                 },
639                 { &hf_image_height,
640                         {       "Image height",
641                                 IMG_GIF ".image.height",
642                                 FT_UINT16, BASE_DEC, NULL, 0x00,
643                                 "Image height.",
644                                 HFILL
645                         }
646                 },
647                 { &hf_image_code_size,
648                         {       "LZW minimum code size",
649                                 IMG_GIF ".image.code_size",
650                                 FT_UINT8, BASE_DEC, NULL, 0x00,
651                                 "Minimum code size for the LZW compression.",
652                                 HFILL
653                         }
654                 },
655                 /*
656                  * Trailer
657                  */
658                 { &hf_trailer,
659                         {       "Trailer (End of the GIF stream)",
660                                 IMG_GIF ".end",
661                                 FT_NONE, BASE_NONE, NULL, 0x00,
662                                 "This byte tells the decoder that the data stream is finished.",
663                                 HFILL
664                         }
665                 },
666         };
667
668         /* Setup protocol subtree array */
669         static gint *ett[] = {
670                 &ett_gif,
671                 &ett_global_flags,
672                 &ett_local_flags,
673                 &ett_extension,
674                 &ett_image,
675         };
676
677         /* Register the protocol name and description */
678         proto_gif = proto_register_protocol(
679                         "Compuserve GIF",
680                         "GIF image",
681                         IMG_GIF
682         );
683
684         /* Required function calls to register the header fields
685          * and subtrees used */
686         proto_register_field_array(proto_gif, hf, array_length(hf));
687         proto_register_subtree_array(ett, array_length(ett));
688
689         register_dissector("image-gif", dissect_gif, proto_gif);
690 }
691
692
693 void
694 proto_reg_handoff_gif(void)
695 {
696         dissector_handle_t gif_handle;
697
698         gif_handle = create_dissector_handle(dissect_gif, proto_gif);
699
700         /* Register the GIF media type */
701         dissector_add_string("media_type", "image/gif", gif_handle);
702 }