/src/wireshark/epan/dissectors/file-gif.c
Line | Count | Source |
1 | | /* file-gif.c |
2 | | * |
3 | | * Routines for image/gif media dissection |
4 | | * Copyright 2003, 2004, Olivier Biot. |
5 | | * |
6 | | * Refer to the AUTHORS file or the AUTHORS section in the man page |
7 | | * for contacting the author(s) of this file. |
8 | | * |
9 | | * Wireshark - Network traffic analyzer |
10 | | * By Gerald Combs <gerald@wireshark.org> |
11 | | * Copyright 1998 Gerald Combs |
12 | | * |
13 | | * Compuserve GIF media decoding functionality provided by Olivier Biot. |
14 | | * |
15 | | * The two GIF specifications are found at several locations, such as W3C: |
16 | | * http://www.w3.org/Graphics/GIF/spec-gif87.txt |
17 | | * http://www.w3.org/Graphics/GIF/spec-gif89a.txt |
18 | | * |
19 | | * SPDX-License-Identifier: GPL-2.0-or-later |
20 | | */ |
21 | | #include "config.h" |
22 | | |
23 | | #include <epan/packet.h> |
24 | | #include <epan/expert.h> |
25 | | #include <wsutil/array.h> |
26 | | |
27 | | #include <wsutil/str_util.h> |
28 | | |
29 | | void proto_register_gif(void); |
30 | | void proto_reg_handoff_gif(void); |
31 | | |
32 | | |
33 | | /************************** Variable declarations **************************/ |
34 | | |
35 | | static const value_string vals_true_false[] = { |
36 | | { 0, "False" }, |
37 | | { 1, "True" }, |
38 | | { 0, NULL }, |
39 | | }; |
40 | | |
41 | | static const value_string vals_extensions[] = { |
42 | | { 0xF9, "Graphics Control" }, |
43 | | { 0xFE, "Comment" }, |
44 | | { 0xFF, "Application" }, |
45 | | { 0x01, "Plain Text" }, |
46 | | { 0x00, NULL }, |
47 | | }; |
48 | | |
49 | | enum { |
50 | | GIF_87a = 0x87, |
51 | | GIF_89a = 0x89 |
52 | | }; |
53 | | |
54 | | static dissector_handle_t gif_handle; |
55 | | |
56 | | static int proto_gif; |
57 | | |
58 | | static int hf_background_color; |
59 | | static int hf_data_block; |
60 | | static int hf_extension; |
61 | | static int hf_extension_label; |
62 | | static int hf_global_color_map; |
63 | | static int hf_global_color_map_ordered; |
64 | | static int hf_global_color_map_present; |
65 | | static int hf_global_color_resolution; |
66 | | static int hf_global_image_bpp; |
67 | | static int hf_image; |
68 | | static int hf_image_code_size; |
69 | | static int hf_image_height; |
70 | | static int hf_image_left; |
71 | | static int hf_image_top; |
72 | | static int hf_image_width; |
73 | | static int hf_local_color_map; |
74 | | static int hf_local_color_map_ordered; |
75 | | static int hf_local_color_map_present; |
76 | | static int hf_local_color_resolution; |
77 | | static int hf_local_image_bpp; |
78 | | static int hf_pixel_aspect_ratio; |
79 | | static int hf_screen_height; |
80 | | static int hf_screen_width; |
81 | | static int hf_trailer; |
82 | | static int hf_version; |
83 | | |
84 | | /* Initialize the subtree pointers */ |
85 | | static int ett_gif; |
86 | | static int ett_global_flags; |
87 | | static int ett_local_flags; |
88 | | static int ett_extension; |
89 | | static int ett_image; |
90 | | |
91 | | static expert_field ei_gif_unknown_data_block_type; |
92 | | |
93 | | /****************** GIF protocol dissection functions ******************/ |
94 | | |
95 | | static int |
96 | | dissect_gif_data_block_seq(tvbuff_t *tvb, int offset, proto_tree *tree) |
97 | 84 | { |
98 | 84 | int offset_start = offset; |
99 | 84 | uint8_t len; |
100 | 84 | proto_item *db_ti; |
101 | | |
102 | 198 | do { |
103 | | /* Read length of data block */ |
104 | 198 | len = tvb_get_uint8(tvb, offset); |
105 | 198 | db_ti = proto_tree_add_item(tree, hf_data_block, |
106 | 198 | tvb, offset, 1, ENC_NA); |
107 | 198 | proto_item_append_text(db_ti, " (length = %u)", len); |
108 | 198 | offset += (1 + len); |
109 | 198 | } while (len > 0); |
110 | | |
111 | 84 | return offset - offset_start; |
112 | 84 | } |
113 | | |
114 | | /* There are two Compuserve GIF standards: GIF87a and GIF89a. GIF image files |
115 | | * always convey their version in the first 6 bytes, written as an US-ASCII |
116 | | * string representation of the version: "GIF87a" or "GIF89a". |
117 | | */ |
118 | | |
119 | | static int |
120 | | dissect_gif(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
121 | 287 | { |
122 | 287 | proto_item *ti; |
123 | 287 | proto_tree *gif_tree; /* Main GIF tree */ |
124 | 287 | proto_tree *subtree; |
125 | 287 | unsigned offset = 0; |
126 | 287 | uint8_t peek; |
127 | 287 | bool color_map_present; |
128 | 287 | uint8_t color_resolution; |
129 | 287 | uint8_t image_bpp; |
130 | 287 | const uint8_t *ver_str; |
131 | 287 | uint8_t version; |
132 | | |
133 | 287 | if (tvb_reported_length(tvb) < 20) |
134 | 38 | return 0; |
135 | | |
136 | | /* Check whether we're processing a GIF object */ |
137 | | /* see http://www.w3.org/Graphics/GIF/spec-gif89a.txt section 17 */ |
138 | 249 | if (tvb_strneql(tvb, 0, "GIF87a", 6) == 0) { |
139 | 11 | version = GIF_87a; |
140 | 238 | } else if (tvb_strneql(tvb, 0, "GIF89a", 6) == 0) { |
141 | 33 | version = GIF_89a; |
142 | 205 | } else { |
143 | | /* Not a GIF image! */ |
144 | 205 | return 0; |
145 | 205 | } |
146 | | |
147 | 44 | ti = proto_tree_add_item(tree, proto_gif, tvb, offset, -1, ENC_NA); |
148 | 44 | gif_tree = proto_item_add_subtree(ti, ett_gif); |
149 | | |
150 | | /* GIF signature */ |
151 | 44 | proto_tree_add_item_ret_string(gif_tree, hf_version, |
152 | 44 | tvb, offset, 6, ENC_ASCII|ENC_NA, pinfo->pool, &ver_str); |
153 | 44 | proto_item_append_text(ti, ", Version: %s", ver_str); |
154 | 44 | col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)", ver_str); |
155 | 44 | offset += 6; |
156 | | |
157 | | /* Screen descriptor */ |
158 | 44 | proto_tree_add_item(gif_tree, hf_screen_width, tvb, offset, 2, ENC_LITTLE_ENDIAN); |
159 | 44 | offset += 2; |
160 | 44 | proto_tree_add_item(gif_tree, hf_screen_height, tvb, offset, 2, ENC_LITTLE_ENDIAN); |
161 | 44 | offset += 2; |
162 | | |
163 | 44 | peek = tvb_get_uint8(tvb, offset); |
164 | | /* Bitfield gccc 0ppp |
165 | | * g... .... : global color map present |
166 | | * .ccc .... : color resolution in bits (add one) |
167 | | * .... 0... : GIF87a - reserved (no use) |
168 | | * GIF89a - ordered (most important color 1st) |
169 | | * .... .ppp : bits per pixel in image (add one) |
170 | | */ |
171 | 44 | color_map_present = peek & 0x80; |
172 | 44 | color_resolution = 1 + ((peek & 0x60) >> 4); |
173 | 44 | image_bpp = 1 + (peek & 0x07); |
174 | | |
175 | 44 | subtree = proto_tree_add_subtree(gif_tree, tvb, offset, 1, ett_global_flags, &ti, |
176 | 44 | "Global settings:"); |
177 | 44 | if (color_map_present) |
178 | 4 | proto_item_append_text(ti, " (Global color table present)"); |
179 | 44 | proto_item_append_text(ti, |
180 | 44 | " (%u bit%s per color) (%u bit%s per pixel)", |
181 | 44 | color_resolution, plurality(color_resolution, "", "s"), |
182 | 44 | image_bpp, plurality(image_bpp, "", "s")); |
183 | 44 | proto_tree_add_item(subtree, hf_global_color_map_present, |
184 | 44 | tvb, offset, 1, ENC_LITTLE_ENDIAN); |
185 | 44 | proto_tree_add_item(subtree, hf_global_color_resolution, |
186 | 44 | tvb, offset, 1, ENC_LITTLE_ENDIAN); |
187 | 44 | if (version == GIF_89a) { |
188 | 33 | proto_tree_add_item(subtree, hf_global_color_map_ordered, |
189 | 33 | tvb, offset, 1, ENC_LITTLE_ENDIAN); |
190 | 33 | } |
191 | 44 | proto_tree_add_item(subtree, hf_global_image_bpp, |
192 | 44 | tvb, offset, 1, ENC_LITTLE_ENDIAN); |
193 | 44 | offset++; |
194 | | |
195 | | /* Background color */ |
196 | 44 | proto_tree_add_item(gif_tree, hf_background_color, |
197 | 44 | tvb, offset, 1, ENC_LITTLE_ENDIAN); |
198 | 44 | offset++; |
199 | | |
200 | | /* byte at offset 12 is 0x00 - reserved in GIF87a but encodes the |
201 | | * pixel aspect ratio in GIF89a as: |
202 | | * aspect-ratio = (15 + pixel-aspect-ratio) / 64 |
203 | | * where the aspect-ratio is not computed if pixel-aspect-ratio == 0 |
204 | | */ |
205 | 44 | if (version == GIF_89a) { |
206 | 33 | peek = tvb_get_uint8(tvb, offset); |
207 | 33 | if (peek) { |
208 | | /* Only display if different from 0 */ |
209 | 28 | proto_tree_add_uint_format(gif_tree, hf_pixel_aspect_ratio, |
210 | 28 | tvb, offset, 1, peek, |
211 | 28 | "%u, yields an aspect ratio of (15 + %u) / 64 = %.2f", |
212 | 28 | peek, peek, (float)(15 + peek) / 64.0); |
213 | 28 | } |
214 | 33 | } |
215 | 44 | offset++; |
216 | | |
217 | | /* Global color map |
218 | | * If present, it takes 2 ^ (image_bpp) byte tuples (R, G, B) |
219 | | * that contain the Red, Green and Blue intensity of the colors |
220 | | * in the Global Color Map */ |
221 | 44 | if (color_map_present) { |
222 | 4 | unsigned len; |
223 | | |
224 | 4 | len = 3 * (1 << image_bpp); |
225 | 4 | proto_tree_add_item(gif_tree, hf_global_color_map, |
226 | 4 | tvb, offset, len, ENC_NA); |
227 | 4 | offset += len; |
228 | 4 | } |
229 | | |
230 | | |
231 | | /* From now on, a set of images prefixed with the image separator |
232 | | * character 0x2C (',') will appear in the byte stream. Each image |
233 | | * hence consists of: |
234 | | * - The image separator character 0x2C |
235 | | * - Image left (16 bits LSB first): pixels from left border |
236 | | * - Image top (16 bits LSB first): pixels from to border |
237 | | * - Image width (16 bits LSB first) |
238 | | * - Image height (16 bits LSB first) |
239 | | * - A bitfield MI00 0ppp |
240 | | * M... .... : Use global color map if unset (ignore ppp); |
241 | | * if set a local color map will be defined. |
242 | | * .I.. .... : Image formatted in interlaced order if set; |
243 | | * otherwise it is plain sequential order |
244 | | * ..0. .... : GIF87a - Reserved |
245 | | * ..s. .... GIF89a - Set if local color map is ordered |
246 | | * ...0 0... : Reserved |
247 | | * .... .ppp : bits per pixel in image (add one) |
248 | | * - If the local color map bit is set, then a local color table follows |
249 | | * with length = 3 x 2 ^ (1 + bits per pixel) |
250 | | * - The raster data |
251 | | * |
252 | | * NOTE that the GIF specification only requires that: |
253 | | * image left + image width <= screen width |
254 | | * image top + image height <= screen height |
255 | | * |
256 | | * The Raster Data is encoded as follows: |
257 | | * - Code size (1 byte) |
258 | | * - Blocks consisting of |
259 | | * o Byte count (1 byte): number of bytes in the block |
260 | | * o Data bytes: as many as specified in the byte count |
261 | | * End of data is given with an empty block (byte count == 0). |
262 | | * |
263 | | * |
264 | | * GIF terminator |
265 | | * This is a synchronization method, based on the final character 0x3B |
266 | | * (';') at the end of an image |
267 | | * |
268 | | * |
269 | | * GIF extension |
270 | | * This is a block of data encoded as: |
271 | | * - The GIF extension block introducer 0x21 ('!') |
272 | | * - The extension function code (1 byte) |
273 | | * - Blocks consisting of |
274 | | * o Byte count (1 byte): number of bytes in the block |
275 | | * o Data bytes: as many as specified in the byte count |
276 | | * End of data is given with an empty block (byte count == 0). |
277 | | * |
278 | | * NOTE that the GIF extension block can only appear at the following |
279 | | * locations: |
280 | | * - Immediately before an Image Descriptor |
281 | | * - Before the GIF termination character |
282 | | */ |
283 | 7.38k | while (tvb_reported_length_remaining(tvb, offset)) { |
284 | 7.34k | int ret; |
285 | 7.34k | int offset_start = offset; |
286 | | |
287 | 7.34k | peek = tvb_get_uint8(tvb, offset); |
288 | 7.34k | if (peek == 0x21) { /* GIF extension block */ |
289 | 48 | ti = proto_tree_add_item(gif_tree, hf_extension, |
290 | 48 | tvb, offset, 1, ENC_NA); |
291 | 48 | subtree = proto_item_add_subtree(ti, ett_extension); |
292 | 48 | offset++; |
293 | 48 | proto_tree_add_item(subtree, hf_extension_label, |
294 | 48 | tvb, offset, 1, ENC_LITTLE_ENDIAN); |
295 | 48 | peek = tvb_get_uint8(tvb, offset); |
296 | 48 | proto_item_append_text(ti, ": %s", |
297 | 48 | val_to_str(pinfo->pool, peek, vals_extensions, |
298 | 48 | "<Warning: Unknown extension 0x%02X>")); |
299 | 48 | offset++; |
300 | 48 | ret = dissect_gif_data_block_seq(tvb, offset, subtree); |
301 | 48 | if (ret <= 0) |
302 | 0 | break; |
303 | 48 | offset += ret; |
304 | 7.29k | } else if (peek == 0x2C) { /* Image separator */ |
305 | 39 | proto_tree *subtree2; |
306 | 39 | proto_item *ti2; |
307 | | |
308 | 39 | ti = proto_tree_add_item(gif_tree, hf_image, |
309 | 39 | tvb, offset, 1, ENC_NA); |
310 | 39 | subtree = proto_item_add_subtree(ti, ett_image); |
311 | 39 | offset++; |
312 | | /* Screen descriptor */ |
313 | 39 | proto_tree_add_item(subtree, hf_image_left, |
314 | 39 | tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; |
315 | 39 | proto_tree_add_item(subtree, hf_image_top, |
316 | 39 | tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; |
317 | 39 | proto_tree_add_item(subtree, hf_image_width, |
318 | 39 | tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; |
319 | 39 | proto_tree_add_item(subtree, hf_image_height, |
320 | 39 | tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; |
321 | | /* bit field */ |
322 | 39 | peek = tvb_get_uint8(tvb, offset); |
323 | 39 | color_map_present = peek & 0x80; |
324 | 39 | color_resolution = 1 + ((peek & 0x60) >> 4); |
325 | 39 | image_bpp = 1 + (peek & 0x07); |
326 | | |
327 | 39 | subtree2 = proto_tree_add_subtree(subtree, tvb, offset, 1, ett_local_flags, &ti2, |
328 | 39 | "Local settings:"); |
329 | 39 | if (color_map_present) |
330 | 13 | proto_item_append_text(ti2, " (Local color table present)"); |
331 | 39 | proto_item_append_text(ti2, |
332 | 39 | " (%u bit%s per color) (%u bit%s per pixel)", |
333 | 39 | color_resolution, plurality(color_resolution, "", "s"), |
334 | 39 | image_bpp, plurality(image_bpp, "", "s")); |
335 | 39 | proto_tree_add_item(subtree2, hf_local_color_map_present, |
336 | 39 | tvb, offset, 1, ENC_LITTLE_ENDIAN); |
337 | 39 | proto_tree_add_item(subtree2, hf_local_color_resolution, |
338 | 39 | tvb, offset, 1, ENC_LITTLE_ENDIAN); |
339 | 39 | if (version == GIF_89a) { |
340 | 28 | proto_tree_add_item(subtree2, hf_local_color_map_ordered, |
341 | 28 | tvb, offset, 1, ENC_LITTLE_ENDIAN); |
342 | 28 | } |
343 | 39 | proto_tree_add_item(subtree2, hf_global_image_bpp, |
344 | 39 | tvb, offset, 1, ENC_LITTLE_ENDIAN); |
345 | 39 | offset++; |
346 | | |
347 | | /* Local color map |
348 | | * If present, it takes 2 ^ (image_bpp) byte tuples (R, G, B) |
349 | | * that contain the Red, Green and Blue intensity of the colors |
350 | | * in the Local Color Map */ |
351 | 39 | if (color_map_present) { |
352 | 13 | unsigned len; |
353 | | |
354 | 13 | len = 3 * (1 << image_bpp); |
355 | 13 | proto_tree_add_item(subtree, hf_local_color_map, |
356 | 13 | tvb, offset, len, ENC_NA); |
357 | 13 | offset += len; |
358 | 13 | } |
359 | | |
360 | 39 | proto_tree_add_item(subtree, hf_image_code_size, |
361 | 39 | tvb, offset, 1, ENC_LITTLE_ENDIAN); |
362 | 39 | offset++; |
363 | 39 | ret = dissect_gif_data_block_seq(tvb, offset, subtree); |
364 | 39 | if (ret <= 0) |
365 | 0 | break; |
366 | 39 | offset += ret; |
367 | 7.25k | } else if (peek == 0x3B) { /* Trailer byte */ |
368 | | /* GIF processing stops at this very byte */ |
369 | 5 | proto_tree_add_item(gif_tree, hf_trailer, |
370 | 5 | tvb, offset, 1, ENC_NA); |
371 | 5 | offset++; |
372 | 5 | break; |
373 | 7.25k | } else { |
374 | 7.25k | proto_tree_add_expert(gif_tree, pinfo, |
375 | 7.25k | &ei_gif_unknown_data_block_type, |
376 | 7.25k | tvb, offset, 1); |
377 | 7.25k | offset++; |
378 | 7.25k | } |
379 | 7.34k | proto_item_set_len(ti, offset-offset_start); |
380 | 7.34k | } /* while */ |
381 | | |
382 | 44 | return offset; |
383 | 249 | } |
384 | | |
385 | | static bool |
386 | | dissect_gif_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
387 | 287 | { |
388 | 287 | return dissect_gif(tvb, pinfo, tree, data) > 0; |
389 | 287 | } |
390 | | |
391 | | |
392 | | /****************** Register the protocol with Wireshark ******************/ |
393 | | |
394 | | |
395 | | /* This format is required because a script is used to build the C function |
396 | | * that calls the protocol registration. */ |
397 | | |
398 | | void |
399 | | proto_register_gif(void) |
400 | 15 | { |
401 | 15 | static hf_register_info hf[] = { |
402 | 15 | { &hf_version, |
403 | 15 | { "Version", "image-gif.version", |
404 | 15 | FT_STRING, BASE_NONE, NULL, 0x00, |
405 | 15 | "GIF Version", HFILL } |
406 | 15 | }, |
407 | 15 | { &hf_screen_width, |
408 | 15 | { "Screen width", "image-gif.screen.width", |
409 | 15 | FT_UINT16, BASE_DEC, NULL, 0x00, |
410 | 15 | NULL, HFILL } |
411 | 15 | }, |
412 | 15 | { &hf_screen_height, |
413 | 15 | { "Screen height", "image-gif.screen.height", |
414 | 15 | FT_UINT16, BASE_DEC, NULL, 0x00, |
415 | 15 | NULL, HFILL } |
416 | 15 | }, |
417 | 15 | { &hf_global_color_map_present, |
418 | 15 | { "Global color map is present", "image-gif.global.color_map.present", |
419 | 15 | FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x80, |
420 | 15 | "Indicates if the global color map is present", HFILL } |
421 | 15 | }, |
422 | 15 | { &hf_global_color_resolution, |
423 | 15 | { "Bits per color minus 1", "image-gif.global.color_bpp", |
424 | 15 | FT_UINT8, BASE_DEC, NULL, 0x70, |
425 | 15 | "The number of bits per color is one plus the field value.", HFILL } |
426 | 15 | }, |
427 | 15 | { &hf_global_color_map_ordered, |
428 | 15 | { "Global color map is ordered", "image-gif.global.color_map.ordered", |
429 | 15 | FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x08, |
430 | 15 | "Indicates whether the global color map is ordered.", HFILL } |
431 | 15 | }, |
432 | 15 | { &hf_global_image_bpp, |
433 | 15 | { "Image bits per pixel minus 1", "image-gif.global.bpp", |
434 | 15 | FT_UINT8, BASE_DEC, NULL, 0x07, |
435 | 15 | "The number of bits per pixel is one plus the field value.", HFILL } |
436 | 15 | }, |
437 | 15 | { &hf_background_color, |
438 | 15 | { "Background color index", "image-gif.image_background_index", |
439 | 15 | FT_UINT8, BASE_DEC, NULL, 0x00, |
440 | 15 | "Index of the background color in the color map.", HFILL } |
441 | 15 | }, |
442 | 15 | { &hf_pixel_aspect_ratio, |
443 | 15 | { "Global pixel aspect ratio", "image-gif.global.pixel_aspect_ratio", |
444 | 15 | FT_UINT8, BASE_DEC, NULL, 0x00, |
445 | 15 | "Gives an approximate value of the aspect ratio of the pixels.", HFILL } |
446 | 15 | }, |
447 | 15 | { &hf_global_color_map, |
448 | 15 | { "Global color map", "image-gif.global.color_map", |
449 | 15 | FT_BYTES, BASE_NONE, NULL, 0x00, |
450 | 15 | "Global color map.", HFILL } |
451 | 15 | }, |
452 | 15 | { &hf_image_left, |
453 | 15 | { "Image left position", "image-gif.image.left", |
454 | 15 | FT_UINT16, BASE_DEC, NULL, 0x00, |
455 | 15 | "Offset between left of Screen and left of Image.", HFILL } |
456 | 15 | }, |
457 | 15 | { &hf_image_top, |
458 | 15 | { "Image top position", "image-gif.image.top", |
459 | 15 | FT_UINT16, BASE_DEC, NULL, 0x00, |
460 | 15 | "Offset between top of Screen and top of Image.", HFILL } |
461 | 15 | }, |
462 | 15 | { &hf_image_width, |
463 | 15 | { "Image width", "image-gif.image.width", |
464 | 15 | FT_UINT16, BASE_DEC, NULL, 0x00, |
465 | 15 | "Image width.", HFILL } |
466 | 15 | }, |
467 | 15 | { &hf_image_height, |
468 | 15 | { "Image height", "image-gif.image.height", |
469 | 15 | FT_UINT16, BASE_DEC, NULL, 0x00, |
470 | 15 | "Image height.", HFILL } |
471 | 15 | }, |
472 | 15 | { &hf_local_color_map_present, |
473 | 15 | { "Local color map is present", "image-gif.local.color_map.present", |
474 | 15 | FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x80, |
475 | 15 | "Indicates if the local color map is present", HFILL } |
476 | 15 | }, |
477 | 15 | { &hf_local_color_resolution, |
478 | 15 | { "Bits per color minus 1", "image-gif.local.color_bpp", |
479 | 15 | FT_UINT8, BASE_DEC, NULL, 0x70, |
480 | 15 | "The number of bits per color is one plus the field value.", HFILL } |
481 | 15 | }, |
482 | 15 | { &hf_local_color_map_ordered, |
483 | 15 | { "Local color map is ordered", "image-gif.local.color_map.ordered", |
484 | 15 | FT_UINT8, BASE_DEC, VALS(vals_true_false), 0x08, |
485 | 15 | "Indicates whether the local color map is ordered.", HFILL } |
486 | 15 | }, |
487 | 15 | { &hf_local_image_bpp, |
488 | 15 | { "Image bits per pixel minus 1", "image-gif.local.bpp", |
489 | 15 | FT_UINT8, BASE_DEC, NULL, 0x07, |
490 | 15 | "The number of bits per pixel is one plus the field value.", HFILL } |
491 | 15 | }, |
492 | 15 | { &hf_local_color_map, |
493 | 15 | { "Local color map", "image-gif.local.color_map", |
494 | 15 | FT_BYTES, BASE_NONE, NULL, 0x00, |
495 | 15 | "Local color map.", HFILL } |
496 | 15 | }, |
497 | 15 | { &hf_extension, |
498 | 15 | { "Extension", "image-gif.extension", |
499 | 15 | FT_NONE, BASE_NONE, NULL, 0x00, |
500 | 15 | "Extension.", HFILL } |
501 | 15 | }, |
502 | 15 | { &hf_extension_label, |
503 | 15 | { "Extension label", "image-gif.extension.label", |
504 | 15 | FT_UINT8, BASE_HEX, VALS(vals_extensions), 0x00, |
505 | 15 | "Extension label.", HFILL } |
506 | 15 | }, |
507 | 15 | { &hf_image, |
508 | 15 | { "Image", "image-gif.image", |
509 | 15 | FT_NONE, BASE_NONE, NULL, 0x00, |
510 | 15 | "Image.", HFILL } |
511 | 15 | }, |
512 | 15 | { &hf_image_code_size, |
513 | 15 | { "LZW minimum code size", "image-gif.image.code_size", |
514 | 15 | FT_UINT8, BASE_DEC, NULL, 0x00, |
515 | 15 | "Minimum code size for the LZW compression.", HFILL } |
516 | 15 | }, |
517 | 15 | { &hf_trailer, |
518 | 15 | { "Trailer (End of the GIF stream)", "image-gif.end", |
519 | 15 | FT_NONE, BASE_NONE, NULL, 0x00, |
520 | 15 | "This byte tells the decoder that the data stream is finished.", HFILL } |
521 | 15 | }, |
522 | 15 | { &hf_data_block, |
523 | 15 | { "Data block", "image-gif.data_block", |
524 | 15 | FT_UINT_BYTES, BASE_NONE|BASE_ALLOW_ZERO, NULL, 0x00, |
525 | 15 | NULL, HFILL } |
526 | 15 | }, |
527 | 15 | }; |
528 | | |
529 | | /* Setup protocol subtree array */ |
530 | 15 | static int *ett[] = { |
531 | 15 | &ett_gif, |
532 | 15 | &ett_global_flags, |
533 | 15 | &ett_local_flags, |
534 | 15 | &ett_extension, |
535 | 15 | &ett_image, |
536 | 15 | }; |
537 | | |
538 | 15 | static ei_register_info ei[] = { |
539 | 15 | { &ei_gif_unknown_data_block_type, |
540 | 15 | { "gif.data_block_type.unknown", PI_PROTOCOL, PI_WARN, |
541 | 15 | "Unknown GIF data block type", EXPFILL } |
542 | 15 | } |
543 | 15 | }; |
544 | | |
545 | 15 | expert_module_t* expert_gif; |
546 | | |
547 | | /* Register the protocol name and description */ |
548 | 15 | proto_gif = proto_register_protocol("Compuserve GIF", "GIF image", "image-gif"); |
549 | | |
550 | | /* Required function calls to register the header fields |
551 | | * and subtrees used */ |
552 | 15 | proto_register_field_array(proto_gif, hf, array_length(hf)); |
553 | 15 | proto_register_subtree_array(ett, array_length(ett)); |
554 | 15 | expert_gif = expert_register_protocol(proto_gif); |
555 | 15 | expert_register_field_array(expert_gif, ei, array_length(ei)); |
556 | | |
557 | 15 | gif_handle = register_dissector("image-gif", dissect_gif, proto_gif); |
558 | 15 | } |
559 | | |
560 | | |
561 | | void |
562 | | proto_reg_handoff_gif(void) |
563 | 15 | { |
564 | | /* Register the GIF media type */ |
565 | 15 | dissector_add_string("media_type", "image/gif", gif_handle); |
566 | 15 | heur_dissector_add("http", dissect_gif_heur, "GIF file in HTTP", "gif_http", proto_gif, HEURISTIC_ENABLE); |
567 | 15 | heur_dissector_add("wtap_file", dissect_gif_heur, "GIF file", "gif_wtap", proto_gif, HEURISTIC_ENABLE); |
568 | 15 | } |
569 | | |
570 | | /* |
571 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
572 | | * |
573 | | * Local variables: |
574 | | * c-basic-offset: 4 |
575 | | * tab-width: 8 |
576 | | * indent-tabs-mode: nil |
577 | | * End: |
578 | | * |
579 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
580 | | * :indentSize=4:tabSize=8:noTabs=true: |
581 | | */ |