make numbers garbage-collected objects
[swfdec.git] / swfdec / swfdec_tag.c
blob3cad0a0f9fdc7106899f333925a93b833bf3a1c5
1 /* Swfdec
2 * Copyright (C) 2003-2006 David Schleef <ds@schleef.org>
3 * 2005-2006 Eric Anholt <eric@anholt.net>
4 * 2006-2007 Benjamin Otte <otte@gnome.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
26 #include <zlib.h>
27 #include <math.h>
28 #include <string.h>
29 #include <stdlib.h>
31 #include "swfdec_tag.h"
32 #include "swfdec_bits.h"
33 #include "swfdec_button.h"
34 #include "swfdec_debug.h"
35 #include "swfdec_text_field.h"
36 #include "swfdec_filter.h"
37 #include "swfdec_font.h"
38 #include "swfdec_image.h"
39 #include "swfdec_morphshape.h"
40 #include "swfdec_pattern.h"
41 #include "swfdec_player_internal.h"
42 #include "swfdec_script_internal.h"
43 #include "swfdec_shape.h"
44 #include "swfdec_sound.h"
45 #include "swfdec_sprite.h"
46 #include "swfdec_text.h"
47 #include "swfdec_video.h"
49 static int
50 tag_func_end (SwfdecSwfDecoder * s, guint tag)
52 return SWFDEC_STATUS_OK;
55 static int
56 tag_func_protect (SwfdecSwfDecoder * s, guint tag)
58 if (s->protection) {
59 SWFDEC_INFO ("This file is really protected.");
60 g_free (s->password);
61 s->password = NULL;
63 s->protection = TRUE;
64 if (swfdec_bits_left (&s->b)) {
65 /* FIXME: What's this for? */
66 swfdec_bits_get_u16 (&s->b);
67 s->password = swfdec_bits_get_string (&s->b, s->version);
69 return SWFDEC_STATUS_OK;
72 static int
73 tag_func_frame_label (SwfdecSwfDecoder * s, guint tag)
75 SwfdecSpriteFrame *frame = &s->parse_sprite->frames[s->parse_sprite->parse_frame];
77 if (frame->labels) {
78 SWFDEC_WARNING ("adding another label for frame %d (%s)",
79 s->parse_sprite->parse_frame, (char *)frame->labels->data);
81 frame->labels = g_slist_prepend (frame->labels,
82 swfdec_bits_get_string (&s->b, s->version));
83 SWFDEC_LOG ("frame %d named %s", s->parse_sprite->parse_frame,
84 (char *)frame->labels->data);
86 return SWFDEC_STATUS_OK;
90 /* text */
92 int
93 tag_func_define_text (SwfdecSwfDecoder * s, guint tag)
95 SwfdecBits *bits = &s->b;
96 int id;
97 int n_glyph_bits;
98 int n_advance_bits;
99 SwfdecText *text = NULL;
100 SwfdecTextGlyph glyph = { 0 };
102 id = swfdec_bits_get_u16 (bits);
103 text = swfdec_swf_decoder_create_character (s, id, SWFDEC_TYPE_TEXT);
104 if (!text)
105 return SWFDEC_STATUS_OK;
107 glyph.color = 0xffffffff;
109 swfdec_bits_get_rect (bits, &SWFDEC_GRAPHIC (text)->extents);
110 swfdec_bits_get_matrix (bits, &text->transform, &text->transform_inverse);
111 swfdec_bits_syncbits (bits);
112 n_glyph_bits = swfdec_bits_get_u8 (bits);
113 n_advance_bits = swfdec_bits_get_u8 (bits);
115 //printf(" n_glyph_bits = %d\n", n_glyph_bits);
116 //printf(" n_advance_bits = %d\n", n_advance_bits);
118 while (swfdec_bits_peekbits (bits, 8) != 0) {
119 int type;
121 type = swfdec_bits_getbit (bits);
122 if (type == 0) {
123 /* glyph record */
124 int n_glyphs;
125 int i;
127 n_glyphs = swfdec_bits_getbits (bits, 7);
128 if (glyph.font == NULL)
129 SWFDEC_ERROR ("no font for %d glyphs", n_glyphs);
130 for (i = 0; i < n_glyphs; i++) {
131 glyph.glyph = swfdec_bits_getbits (bits, n_glyph_bits);
133 if (glyph.font != NULL)
134 g_array_append_val (text->glyphs, glyph);
135 glyph.x += swfdec_bits_getsbits (bits, n_advance_bits);
137 } else {
138 /* state change */
139 int reserved;
140 int has_font;
141 int has_color;
142 int has_y_offset;
143 int has_x_offset;
145 reserved = swfdec_bits_getbits (bits, 3);
146 has_font = swfdec_bits_getbit (bits);
147 has_color = swfdec_bits_getbit (bits);
148 has_y_offset = swfdec_bits_getbit (bits);
149 has_x_offset = swfdec_bits_getbit (bits);
150 if (has_font) {
151 glyph.font = swfdec_swf_decoder_get_character (s, swfdec_bits_get_u16 (bits));
152 //printf(" font = %d\n",font);
154 if (has_color) {
155 if (tag == SWFDEC_TAG_DEFINETEXT) {
156 glyph.color = swfdec_bits_get_color (bits);
157 } else {
158 glyph.color = swfdec_bits_get_rgba (bits);
160 //printf(" color = %08x\n",glyph.color);
162 if (has_x_offset) {
163 glyph.x = swfdec_bits_get_s16 (bits);
165 if (has_y_offset) {
166 glyph.y = swfdec_bits_get_s16 (bits);
168 if (has_font) {
169 glyph.height = swfdec_bits_get_u16 (bits);
172 swfdec_bits_syncbits (bits);
174 swfdec_bits_get_u8 (bits);
176 return SWFDEC_STATUS_OK;
180 tag_func_define_sprite (SwfdecSwfDecoder * s, guint define_sprite_tag)
182 SwfdecBits parse;
183 int id;
184 SwfdecSprite *sprite;
185 int ret;
186 guint tag = 1;
188 parse = s->b;
190 id = swfdec_bits_get_u16 (&parse);
191 sprite = swfdec_swf_decoder_create_character (s, id, SWFDEC_TYPE_SPRITE);
192 if (!sprite)
193 return SWFDEC_STATUS_OK;
195 SWFDEC_LOG (" ID: %d", id);
197 swfdec_sprite_set_n_frames (sprite, swfdec_bits_get_u16 (&parse), SWFDEC_DECODER (s)->rate);
199 s->parse_sprite = sprite;
200 while (swfdec_bits_left (&parse)) {
201 int x;
202 guint tag_len;
203 SwfdecTagFunc func;
205 x = swfdec_bits_get_u16 (&parse);
206 tag = x >> 6;
207 tag_len = x & 0x3f;
208 if (tag_len == 0x3f) {
209 tag_len = swfdec_bits_get_u32 (&parse);
211 SWFDEC_INFO ("sprite parsing at %td, tag %d %s, length %d",
212 parse.buffer ? parse.ptr - parse.buffer->data : 0, tag,
213 swfdec_swf_decoder_get_tag_name (tag), tag_len);
215 if (tag_len == 0) {
216 swfdec_bits_init_data (&s->b, NULL, 0);
217 } else {
218 swfdec_bits_init_bits (&s->b, &parse, tag_len);
221 func = swfdec_swf_decoder_get_tag_func (tag);
222 if (tag == 0) {
223 break;
224 } else if (func == NULL) {
225 SWFDEC_FIXME ("tag function not implemented for %d %s",
226 tag, swfdec_swf_decoder_get_tag_name (tag));
227 } else if ((swfdec_swf_decoder_get_tag_flag (tag) & 1) == 0) {
228 SWFDEC_ERROR ("invalid tag %d %s during DefineSprite",
229 tag, swfdec_swf_decoder_get_tag_name (tag));
230 } else if (s->parse_sprite->parse_frame < s->parse_sprite->n_frames) {
231 ret = func (s, tag);
233 if (swfdec_bits_left (&s->b)) {
234 SWFDEC_WARNING ("early parse finish (%d bytes)",
235 swfdec_bits_left (&s->b) / 8);
237 } else {
238 SWFDEC_ERROR ("data after last frame");
242 /* sanity check the sprite */
243 if (s->parse_sprite->n_frames != s->parse_sprite->parse_frame) {
244 SWFDEC_INFO ("not enough frames in sprite %u (have %u, want %u), filling up with empty frames",
245 id, s->parse_sprite->parse_frame, s->parse_sprite->n_frames);
246 s->parse_sprite->parse_frame = s->parse_sprite->n_frames;
249 s->b = parse;
250 /* this assumes that no recursive DefineSprite happens and we check it doesn't */
251 s->parse_sprite = s->main_sprite;
252 SWFDEC_LOG ("done parsing this sprite");
254 return SWFDEC_STATUS_OK;
257 static int
258 tag_func_file_attributes (SwfdecSwfDecoder *s, guint tag)
260 if (swfdec_bits_getbits (&s->b, 3))
261 SWFDEC_INFO ("reserved bits (1) aren't 0");
262 s->has_metadata = swfdec_bits_getbit (&s->b);
263 SWFDEC_LOG (" has metadata: %d", s->has_metadata);
264 if (swfdec_bits_getbits (&s->b, 3))
265 SWFDEC_INFO ("reserved bits (2) aren't 0");
266 s->use_network = swfdec_bits_getbit (&s->b);
267 SWFDEC_LOG (" use network: %d", s->use_network);
268 if (swfdec_bits_getbits (&s->b, 24))
269 SWFDEC_INFO ("reserved bits (3) aren't 0");
270 /* initialize default security if it wasn't initialized yet */
272 return SWFDEC_STATUS_OK;
275 static int
276 tag_func_enqueue (SwfdecSwfDecoder *s, guint tag)
278 SwfdecBuffer *buffer;
280 buffer = swfdec_bits_get_buffer (&s->b, -1);
281 SWFDEC_LOG ("queueing %s tag for sprite %u", swfdec_swf_decoder_get_tag_name (tag),
282 SWFDEC_CHARACTER (s->parse_sprite)->id);
283 swfdec_sprite_add_action (s->parse_sprite, tag, buffer);
285 return SWFDEC_STATUS_OK;
288 static int
289 tag_func_show_frame (SwfdecSwfDecoder * s, guint tag)
291 SWFDEC_DEBUG("show_frame %d of id %d", s->parse_sprite->parse_frame,
292 SWFDEC_CHARACTER (s->parse_sprite)->id);
294 s->parse_sprite->parse_frame++;
295 tag_func_enqueue (s, tag);
297 return SWFDEC_STATUS_IMAGE;
300 static int
301 tag_func_do_action (SwfdecSwfDecoder * s, guint tag)
303 SwfdecScript *script;
304 SwfdecBits bits;
305 char *name;
307 if (swfdec_bits_left (&s->b) == 0) {
308 SWFDEC_WARNING ("empty script, ignoring");
309 return SWFDEC_STATUS_OK;
311 name = g_strdup_printf ("Sprite%u_Frame%u", SWFDEC_CHARACTER (s->parse_sprite)->id,
312 s->parse_sprite->parse_frame);
313 bits = s->b;
314 script = swfdec_script_new_from_bits (&bits, name, s->version);
315 g_free (name);
316 if (script) {
317 swfdec_swf_decoder_add_script (s, script);
318 tag_func_enqueue (s, tag);
321 return SWFDEC_STATUS_OK;
324 static int
325 tag_func_do_init_action (SwfdecSwfDecoder * s, guint tag)
327 SwfdecScript *script;
328 SwfdecBits bits;
329 char *name;
330 guint id;
332 bits = s->b;
333 id = swfdec_bits_get_u16 (&bits);
334 if (swfdec_bits_left (&bits) == 0) {
335 SWFDEC_WARNING ("empty script, ignoring");
336 return SWFDEC_STATUS_OK;
338 name = g_strdup_printf ("Init %u", id);
339 script = swfdec_script_new_from_bits (&bits, name, s->version);
340 g_free (name);
341 if (script) {
342 swfdec_swf_decoder_add_script (s, script);
343 tag_func_enqueue (s, tag);
346 return SWFDEC_STATUS_OK;
349 /* only needed for the codec finding stuff */
350 static int
351 tag_func_sound_stream_head (SwfdecSwfDecoder *s, guint tag)
353 SwfdecBits bits;
354 SwfdecAudioFormat playback_format, format;
355 guint playback_codec, codec;
356 int n_samples;
358 bits = s->b;
360 /* we don't care about playback suggestions */
361 playback_codec = swfdec_bits_getbits (&bits, 4);
362 playback_format = swfdec_audio_format_parse (&bits);
363 SWFDEC_LOG (" suggested playback format: %s", swfdec_audio_format_to_string (playback_format));
365 codec = swfdec_bits_getbits (&bits, 4);
366 format = swfdec_audio_format_parse (&bits);
367 n_samples = swfdec_bits_get_u16 (&bits);
368 SWFDEC_LOG (" codec: %u", codec);
369 SWFDEC_LOG (" format: %s", swfdec_audio_format_to_string (format));
370 SWFDEC_LOG (" samples: %u", n_samples);
372 swfdec_decoder_use_audio_codec (SWFDEC_DECODER (s), codec, format);
374 return tag_func_enqueue (s, tag);
377 static int
378 tag_func_metadata (SwfdecSwfDecoder *s, guint tag)
380 char *meta;
382 meta = swfdec_bits_get_string (&s->b, s->version);
383 if (meta == NULL) {
384 SWFDEC_ERROR ("invalid metadata contents");
385 return SWFDEC_STATUS_OK;
387 SWFDEC_LOG ("metadata: %s", meta);
389 if (s->metadata) {
390 SWFDEC_ERROR ("Duplicated metadata tag, ignoring");
391 /* FIXME: or use this one? */
392 g_free (meta);
393 } else {
394 s->metadata = meta;
396 return SWFDEC_STATUS_OK;
399 struct tag_func_struct
401 const char *name;
402 SwfdecTagFunc func;
403 int flag;
405 static struct tag_func_struct tag_funcs[] = {
406 [SWFDEC_TAG_END] = {"End", tag_func_end, SWFDEC_TAG_DEFINE_SPRITE },
407 [SWFDEC_TAG_SHOWFRAME] = {"ShowFrame", tag_func_show_frame, SWFDEC_TAG_DEFINE_SPRITE },
408 [SWFDEC_TAG_DEFINESHAPE] = {"DefineShape", tag_define_shape, 0},
409 [SWFDEC_TAG_FREECHARACTER] = {"FreeCharacter", NULL, 0},
410 [SWFDEC_TAG_PLACEOBJECT] = {"PlaceObject", tag_func_enqueue, SWFDEC_TAG_DEFINE_SPRITE },
411 [SWFDEC_TAG_REMOVEOBJECT] = {"RemoveObject", tag_func_enqueue, SWFDEC_TAG_DEFINE_SPRITE },
412 [SWFDEC_TAG_DEFINEBITSJPEG] = {"DefineBitsJPEG", tag_func_define_bits_jpeg, 0},
413 [SWFDEC_TAG_DEFINEBUTTON] = {"DefineButton", tag_func_define_button, 0},
414 [SWFDEC_TAG_JPEGTABLES] = {"JPEGTables", swfdec_image_jpegtables, 0},
415 [SWFDEC_TAG_SETBACKGROUNDCOLOR] =
416 {"SetBackgroundColor", tag_func_enqueue, SWFDEC_TAG_DEFINE_SPRITE },
417 [SWFDEC_TAG_DEFINEFONT] = {"DefineFont", tag_func_define_font, 0},
418 [SWFDEC_TAG_DEFINETEXT] = {"DefineText", tag_func_define_text, 0},
419 [SWFDEC_TAG_DOACTION] = {"DoAction", tag_func_do_action, SWFDEC_TAG_DEFINE_SPRITE },
420 [SWFDEC_TAG_DEFINEFONTINFO] = {"DefineFontInfo", tag_func_define_font_info, 0},
421 [SWFDEC_TAG_DEFINESOUND] = {"DefineSound", tag_func_define_sound, 0},
422 [SWFDEC_TAG_STARTSOUND] = {"StartSound", tag_func_enqueue, SWFDEC_TAG_DEFINE_SPRITE },
423 [SWFDEC_TAG_DEFINEBUTTONSOUND] =
424 {"DefineButtonSound", tag_func_define_button_sound, 0},
425 [SWFDEC_TAG_SOUNDSTREAMHEAD] = {"SoundStreamHead", tag_func_sound_stream_head, SWFDEC_TAG_DEFINE_SPRITE },
426 [SWFDEC_TAG_SOUNDSTREAMBLOCK] = {"SoundStreamBlock", tag_func_enqueue, SWFDEC_TAG_DEFINE_SPRITE },
427 [SWFDEC_TAG_DEFINEBITSLOSSLESS] =
428 {"DefineBitsLossless", tag_func_define_bits_lossless, 0},
429 [SWFDEC_TAG_DEFINEBITSJPEG2] = {"DefineBitsJPEG2", tag_func_define_bits_jpeg_2, 0},
430 [SWFDEC_TAG_DEFINESHAPE2] = {"DefineShape2", tag_define_shape, 0},
431 [SWFDEC_TAG_DEFINEBUTTONCXFORM] = {"DefineButtonCXForm", NULL, 0},
432 [SWFDEC_TAG_PROTECT] = {"Protect", tag_func_protect, 0},
433 [SWFDEC_TAG_PLACEOBJECT2] = {"PlaceObject2", tag_func_enqueue, SWFDEC_TAG_DEFINE_SPRITE },
434 [SWFDEC_TAG_REMOVEOBJECT2] = {"RemoveObject2", tag_func_enqueue, SWFDEC_TAG_DEFINE_SPRITE },
435 [SWFDEC_TAG_DEFINESHAPE3] = {"DefineShape3", tag_define_shape_3, 0},
436 [SWFDEC_TAG_DEFINETEXT2] = {"DefineText2", tag_func_define_text, 0},
437 [SWFDEC_TAG_DEFINEBUTTON2] = {"DefineButton2", tag_func_define_button_2, 0},
438 [SWFDEC_TAG_DEFINEBITSJPEG3] = {"DefineBitsJPEG3", tag_func_define_bits_jpeg_3, 0},
439 [SWFDEC_TAG_DEFINEBITSLOSSLESS2] =
440 {"DefineBitsLossless2", tag_func_define_bits_lossless_2, 0},
441 [SWFDEC_TAG_DEFINEEDITTEXT] = {"DefineEditText", tag_func_define_edit_text, 0},
442 [SWFDEC_TAG_DEFINEMOVIE] = {"DefineMovie", NULL, 0},
443 [SWFDEC_TAG_DEFINESPRITE] = {"DefineSprite", tag_func_define_sprite, 0},
444 [SWFDEC_TAG_NAMECHARACTER] = {"NameCharacter", NULL, 0},
445 [SWFDEC_TAG_PRODUCTINFO] = {"ProductInfo", NULL, 0},
446 [SWFDEC_TAG_GENERATORTEXT] = {"GeneratorText", NULL, 0},
447 [SWFDEC_TAG_FRAMELABEL] = {"FrameLabel", tag_func_frame_label, SWFDEC_TAG_DEFINE_SPRITE },
448 [SWFDEC_TAG_SOUNDSTREAMHEAD2] = {"SoundStreamHead2", tag_func_sound_stream_head, SWFDEC_TAG_DEFINE_SPRITE },
449 [SWFDEC_TAG_DEFINEMORPHSHAPE] =
450 {"DefineMorphShape", tag_define_morph_shape, 0},
451 [SWFDEC_TAG_DEFINEFONT2] = {"DefineFont2", tag_func_define_font_2, 0},
452 [SWFDEC_TAG_TEMPLATECOMMAND] = {"TemplateCommand", NULL, 0},
453 [SWFDEC_TAG_GENERATOR3] = {"Generator3", NULL, 0},
454 [SWFDEC_TAG_EXTERNALFONT] = {"ExternalFont", NULL, 0},
455 [SWFDEC_TAG_EXPORTASSETS] = {"ExportAssets", tag_func_enqueue, 0},
456 [SWFDEC_TAG_IMPORTASSETS] = {"ImportAssets", NULL, 0},
457 [SWFDEC_TAG_ENABLEDEBUGGER] = {"EnableDebugger", NULL, 0},
458 [SWFDEC_TAG_DOINITACTION] = {"DoInitAction", tag_func_do_init_action, SWFDEC_TAG_DEFINE_SPRITE },
459 [SWFDEC_TAG_DEFINEVIDEOSTREAM] = {"DefineVideoStream", tag_func_define_video, SWFDEC_TAG_DEFINE_SPRITE },
460 [SWFDEC_TAG_VIDEOFRAME] = {"VideoFrame", tag_func_video_frame, SWFDEC_TAG_DEFINE_SPRITE },
461 [SWFDEC_TAG_DEFINEFONTINFO2] = {"DefineFontInfo2", tag_func_define_font_info, 0},
462 [SWFDEC_TAG_DEBUGID] = {"DebugID", NULL, 0},
463 [SWFDEC_TAG_ENABLEDEBUGGER2] = {"EnableDebugger2", NULL, 0},
464 [SWFDEC_TAG_SCRIPTLIMITS] = {"ScriptLimits", NULL, 0},
465 [SWFDEC_TAG_SETTABINDEX] = {"SetTabIndex", NULL, SWFDEC_TAG_DEFINE_SPRITE },
466 [SWFDEC_TAG_FILEATTRIBUTES] = {"FileAttributes", tag_func_file_attributes, SWFDEC_TAG_FIRST_ONLY },
467 [SWFDEC_TAG_PLACEOBJECT3] = {"PlaceObject3", tag_func_enqueue, SWFDEC_TAG_DEFINE_SPRITE },
468 [SWFDEC_TAG_IMPORTASSETS2] = {"ImportAssets2", NULL, 0},
469 [SWFDEC_TAG_DEFINEFONTALIGNZONES] = {"DefineFontAlignZones", NULL, 0},
470 [SWFDEC_TAG_CSMTEXTSETTINGS] = {"CSMTextSettings", NULL, 0},
471 [SWFDEC_TAG_DEFINEFONT3] = {"DefineFont3", tag_func_define_font_2, 0},
472 [SWFDEC_TAG_SYMBOLCLASS] = {"SymbolClass", NULL, SWFDEC_TAG_DEFINE_SPRITE },
473 [SWFDEC_TAG_METADATA] = {"Metadata", tag_func_metadata, 0},
474 [SWFDEC_TAG_DEFINESCALINGGRID] = {"DefineScalingGrid", NULL, 0},
475 [SWFDEC_TAG_DOABC] = {"DoAbc", NULL, SWFDEC_TAG_DEFINE_SPRITE },
476 [SWFDEC_TAG_DEFINESHAPE4] = {"DefineShape4", tag_define_shape_4, 0},
477 [SWFDEC_TAG_DEFINEMORPHSHAPE2] = {"DefineMorphShape2", NULL, 0},
478 [SWFDEC_TAG_PRIVATE_IMAGE] = { "PrivateImage", NULL, 0},
479 [SWFDEC_TAG_DEFINESCENEDATA] = { "DefineSceneData", NULL, 0},
480 [SWFDEC_TAG_DEFINEBINARYDATA] = { "DefineBinaryData", NULL, 0},
481 [SWFDEC_TAG_DEFINEFONTNAME] = { "DefineFontName", tag_func_define_font_name, 0},
482 [SWFDEC_TAG_STARTSOUND2] = {"StartSound2", NULL, SWFDEC_TAG_DEFINE_SPRITE }
485 static const int n_tag_funcs = sizeof (tag_funcs) / sizeof (tag_funcs[0]);
487 const char *
488 swfdec_swf_decoder_get_tag_name (int tag)
490 if (tag >= 0 && tag < n_tag_funcs) {
491 if (tag_funcs[tag].name) {
492 return tag_funcs[tag].name;
496 return "unknown";
499 SwfdecTagFunc
500 swfdec_swf_decoder_get_tag_func (int tag)
502 if (tag >= 0 && tag < n_tag_funcs) {
503 if (tag_funcs[tag].func) {
504 return tag_funcs[tag].func;
508 return NULL;
512 swfdec_swf_decoder_get_tag_flag (int tag)
514 if (tag >= 0 && tag < n_tag_funcs) {
515 return tag_funcs[tag].flag;
518 return 0;