3 Math and matrix functions, misc tools
5 Extension module for the rfxswf library.
6 Part of the swftools package.
8 Copyright (c) 2000, 2001 Rainer Böhme <rfxswf@reflex-studio.de>
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
24 // Matrix & Math tools for SWF files
27 SFIXED RFXSWF_SP(SFIXED a1,SFIXED a2,SFIXED b1,SFIXED b2)
29 a = (S64)a1*(S64)b1+(S64)a2*(S64)b2;
30 return (SFIXED)(a>>16);
32 SFIXED RFXSWF_QFIX(int zaehler,int nenner) // bildet Quotient von zwei INTs in SFIXED
33 { S64 z = zaehler<<16;
34 S64 a = z/(S64)nenner;
39 MATRIX * swf_MatrixJoin(MATRIX * d,MATRIX * s1,MATRIX * s2)
42 if (!s1) return (s2)?(MATRIX *)memcpy(d,s2,sizeof(MATRIX)):NULL;
43 if (!s2) return (MATRIX *)memcpy(d,s1,sizeof(MATRIX));
45 d->tx = s1->tx + s2->tx;
46 d->ty = s1->ty + s2->ty;
48 d->sx = RFXSWF_SP(s1->sx,s1->r1,s2->sx,s2->r0);
49 d->sy = RFXSWF_SP(s1->r0,s1->sy,s2->r1,s2->sy);
50 d->r0 = RFXSWF_SP(s1->r0,s1->sy,s2->sx,s2->r0);
51 d->r1 = RFXSWF_SP(s1->sx,s1->r1,s2->r1,s2->sy);
58 MATRIX * swf_MatrixMapTriangle(MATRIX * m,int dx,int dy,int x0,int y0,
59 int x1,int y1,int x2,int y2)
66 if ((!dx)||(!dy)) return NULL; // check DIV by zero
70 m->sx = RFXSWF_QFIX(dx1,dx);
71 m->sy = RFXSWF_QFIX(dy2,dy);
72 m->r0 = RFXSWF_QFIX(dy1,dx);
73 m->r1 = RFXSWF_QFIX(dx2,dy);
78 void swf_SetDefineID(TAG * tag, U16 newid)
80 int oldlen = tag->len;
82 swf_SetU16(tag, newid); /* set defining ID */
86 U16 swf_GetDefineID(TAG * t)
91 oldTagPos = swf_GetTagPos(t);
94 switch (swf_GetTagID(t))
95 { case ST_DEFINESHAPE:
98 case ST_DEFINEMORPHSHAPE:
99 case ST_DEFINEEDITTEXT:
101 case ST_DEFINEBITSJPEG2:
102 case ST_DEFINEBITSJPEG3:
103 case ST_DEFINEBITSLOSSLESS:
104 case ST_DEFINEBITSLOSSLESS2:
105 case ST_DEFINEBUTTON:
106 case ST_DEFINEBUTTON2:
107 case ST_DEFINEBUTTONCXFORM: //pseudodefine
108 case ST_DEFINEBUTTONSOUND: //pseudodefine
111 case ST_DEFINEFONTINFO: //pseudodefine
112 case ST_DEFINEFONTINFO2: //pseudodefine
116 case ST_DEFINESPRITE:
118 case ST_DEFINEVIDEOSTREAM:
119 case ST_GLYPHNAMES: //pseudodefine
120 case ST_VIDEOFRAME: //pseudodefine
121 case ST_NAMECHARACTER: //pseudodefine
122 case ST_DOINITACTION: //pseudodefine
126 fprintf(stderr, "rfxswf: Error: tag %d (%s) has no id\n", t->id, swf_TagGetName(t));
129 swf_SetTagPos(t,oldTagPos);
134 SRECT swf_GetDefineBBox(TAG * t)
140 oldTagPos = swf_GetTagPos(t);
145 switch (swf_GetTagID(t))
146 { case ST_DEFINESHAPE:
147 case ST_DEFINESHAPE2:
148 case ST_DEFINESHAPE3:
149 case ST_DEFINEEDITTEXT:
152 case ST_DEFINEVIDEOSTREAM:
156 case ST_DEFINEMORPHSHAPE:
160 swf_ExpandRect2(&b1, &b2);
162 case ST_DEFINEBITSLOSSLESS:
163 case ST_DEFINEBITSLOSSLESS2:
165 case ST_DEFINEBITSJPEG2:
166 case ST_DEFINEBITSJPEG3:
171 swf_SetTagPos(t,oldTagPos);
176 U16 swf_GetPlaceID(TAG * t)
181 oldTagPos = swf_GetTagPos(t);
184 switch (swf_GetTagID(t))
185 { case ST_PLACEOBJECT:
186 case ST_REMOVEOBJECT:
187 case ST_FREECHARACTER:
192 case ST_PLACEOBJECT2:
193 { U8 flags = swf_GetU8(t);
194 U16 d = swf_GetU16(t);
195 id = (flags&PF_CHAR)?swf_GetU16(t):id;
200 swf_SetTagPos(t,oldTagPos);
205 static int swf_definingtagids[] =
218 ST_DEFINEBITSLOSSLESS,
219 ST_DEFINEBITSLOSSLESS2,
225 ST_DEFINEVIDEOSTREAM,
229 // tags which may be used inside a sprite definition
230 static int swf_spritetagids[] =
235 ST_REMOVEOBJECT2, //?
246 static int swf_pseudodefiningtagids[] =
250 ST_DEFINEBUTTONCXFORM,
251 ST_DEFINEBUTTONSOUND,
259 U8 swf_isAllowedSpriteTag(TAG * tag)
263 while(swf_spritetagids[t]>=0)
265 if(swf_spritetagids[t] == id)
272 U8 swf_isDefiningTag(TAG * tag)
276 while(swf_definingtagids[t]>=0)
278 if(swf_definingtagids[t] == id)
285 U8 swf_isPseudoDefiningTag(TAG * tag)
289 while(swf_pseudodefiningtagids[t]>=0)
291 if(swf_pseudodefiningtagids[t] == id)
298 int swf_GetDepth(TAG * t)
302 oldTagPos = swf_GetTagPos(t);
305 switch (swf_GetTagID(t))
306 { case ST_PLACEOBJECT:
307 case ST_REMOVEOBJECT:
309 depth = swf_GetU16(t);
311 case ST_REMOVEOBJECT2:
312 depth = swf_GetU16(t);
314 case ST_PLACEOBJECT2:
315 { U8 flags = swf_GetU8(t);
316 depth = swf_GetU16(t);
320 depth = swf_GetU16(t);
323 swf_SetTagPos(t,oldTagPos);
327 void swf_SetDepth(TAG * t, U16 depth)
329 switch (swf_GetTagID(t))
330 { case ST_PLACEOBJECT:
331 case ST_REMOVEOBJECT:
332 PUT16(t->data, depth);
334 case ST_REMOVEOBJECT2:
335 PUT16(t->data, depth);
337 case ST_PLACEOBJECT2:
338 PUT16(&t->data[1], depth);
341 PUT16(t->data, depth);
344 fprintf(stderr, "rfxswf: Error: tag %d has no depth\n", t->id);
348 char* swf_GetName(TAG * t)
354 oldTagPos = swf_GetTagPos(t);
356 switch(swf_GetTagID(t))
359 name = &t->data[swf_GetTagPos(t)];
361 case ST_PLACEOBJECT2: {
362 U8 flags = swf_GetU8(t);
363 swf_GetU16(t); //depth;
367 swf_GetMatrix(t, &m);
369 swf_GetCXForm(t, &c, 1);
372 if(flags&PF_CLIPACTION)
375 swf_ResetReadBits(t);
376 name = &t->data[swf_GetTagPos(t)];
381 swf_SetTagPos(t,oldTagPos);
385 /* used in enumerateUsedIDs */
386 void swf_GetMorphGradient(TAG * tag, GRADIENT * gradient1, GRADIENT * gradient2)
396 gradient2->num = swf_GetU8(tag);
397 for(t=0;t<gradient1->num;t++)
402 gradient1->ratios[t] = swf_GetU8(tag);
403 swf_GetRGBA(tag, &gradient1->rgba[t]);
404 gradient2->ratios[t] = swf_GetU8(tag);
405 swf_GetRGBA(tag, &gradient2->rgba[t]);
409 #define DEBUG_ENUMERATE if(0)
411 static void enumerateUsedIDs_styles(TAG * tag, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
415 count = swf_GetU8(tag);
416 if(count == 0xff && num>1) // defineshape2,3 only
417 count = swf_GetU16(tag);
423 swf_ResetReadBits(tag);
424 type = swf_GetU8(tag); //type
427 {swf_GetRGBA(tag, NULL);if(morph) swf_GetRGBA(tag, NULL);}
429 {swf_GetRGB(tag, NULL);if(morph) swf_GetRGB(tag, NULL);}
431 else if(type == 0x10 || type == 0x12)
433 swf_ResetReadBits(tag);
434 swf_GetMatrix(tag, NULL);
436 swf_GetMatrix(tag, NULL);
437 swf_ResetReadBits(tag);
439 swf_GetMorphGradient(tag, NULL, NULL);
441 swf_GetGradient(tag, NULL, /*alpha*/ num>=3?1:0);
443 else if(type == 0x40 || type == 0x41)
445 swf_ResetReadBits(tag);
447 if(tag->data[tag->pos] != 0xff ||
448 tag->data[tag->pos+1] != 0xff)
449 (callback)(tag, tag->pos, callback_data);
452 swf_ResetReadBits(tag);
453 swf_GetMatrix(tag, NULL);
455 swf_GetMatrix(tag, NULL);
458 fprintf(stderr, "rfxswf:swftools.c Unknown fillstyle:0x%02x\n",type);
461 swf_ResetReadBits(tag);
462 count = swf_GetU8(tag); // line style array
464 count = swf_GetU16(tag);
471 {swf_GetRGBA(tag, NULL);if(morph) swf_GetRGBA(tag, NULL);}
473 {swf_GetRGB(tag, NULL);if(morph) swf_GetRGB(tag, NULL);}
477 void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), void*callback_data)
480 swf_ResetReadBits(tag);
484 case ST_DEFINEBUTTONCXFORM: {
486 callback(tag, tag->pos + base, callback_data);
489 callback(tag, tag->pos + base, callback_data);
490 swf_GetU16(tag); //sound id
491 flags = swf_GetU8(tag);
493 swf_GetU32(tag); // in point
495 swf_GetU32(tag); // out points
497 swf_GetU16(tag); // loop count
500 int npoints = swf_GetU8(tag);
502 for(s=0;s<npoints;s++)
511 case ST_DEFINEBUTTONSOUND:
512 callback(tag, tag->pos + base, callback_data); //button id
515 case ST_EXPORTASSETS: {
516 int num = swf_GetU16(tag);
519 callback(tag, tag->pos + base, callback_data); //button id
520 swf_GetU16(tag); //id
521 while(swf_GetU8(tag)); //name
525 case ST_FREECHARACTER: /* unusual tags, which all start with an ID */
526 case ST_NAMECHARACTER:
527 case ST_GENERATORTEXT:
528 callback(tag, tag->pos + base, callback_data);
531 callback(tag, tag->pos + base, callback_data);
533 case ST_PLACEOBJECT2:
534 // only if placeflaghascharacter
535 if(!(tag->data[0]&2))
537 callback(tag, 3 + base, callback_data);
539 case ST_REMOVEOBJECT:
540 callback(tag, tag->pos + base, callback_data);
543 callback(tag, tag->pos + base, callback_data);
545 case ST_DEFINESPRITE: {
547 break; // sprite is expanded
549 swf_GetU16(tag); // id
550 swf_GetU16(tag); // framenum
553 U16 flags = swf_GetU16(tag);
556 TAG *tag2 = swf_InsertTag(NULL, id);
559 len = swf_GetU32(tag);
562 tag2->len = tag2->memsize = len;
563 tag2->data = malloc(len);
564 memcpy(tag2->data, &tag->data[tag->pos], len);
565 /* I never saw recursive sprites, but they are (theoretically)
566 possible, so better add base here again */
567 enumerateUsedIDs(tag2, tag->pos + base, callback, callback_data);
569 swf_GetBlock(tag, NULL, len);
573 case ST_DEFINEBUTTON2: // has some font ids in the button records
576 case ST_DEFINEBUTTON: {
577 swf_GetU16(tag); //button id
581 swf_GetU8(tag); //flag
582 offset = swf_GetU16(tag); //offset
587 if(!swf_GetU8(tag)) //flags
589 callback(tag, tag->pos + base, callback_data);
590 swf_GetU16(tag); //char
591 swf_GetU16(tag); //layer
592 swf_ResetReadBits(tag);
593 swf_GetMatrix(tag, NULL);
595 swf_ResetReadBits(tag);
596 swf_GetCXForm(tag, NULL, 1);
602 case ST_DEFINEEDITTEXT: {
604 swf_GetU16(tag); //id
605 swf_GetRect(tag, NULL); //bounding box
606 swf_ResetReadBits(tag);
607 flags1 = swf_GetU8(tag);
608 flags2 = swf_GetU8(tag);
610 callback(tag, tag->pos + base, callback_data);
615 case ST_DEFINETEXT: {
616 int glyphbits, advancebits;
618 id = swf_GetU16(tag); //id
619 swf_GetRect(tag, NULL); //bounding box
620 swf_ResetReadBits(tag);
621 swf_GetMatrix(tag, NULL); //matrix
622 swf_ResetReadBits(tag);
623 glyphbits = swf_GetU8(tag); //glyphbits
624 advancebits = swf_GetU8(tag); //advancebits
627 swf_ResetReadBits(tag);
628 flags = swf_GetBits(tag, 8);
630 if(flags & 128) // text style record
632 swf_ResetReadBits(tag);
633 if(flags & 8) { // hasfont
634 callback(tag, tag->pos + base, callback_data);
635 id = swf_GetU16(tag);
637 if(flags & 4) { // hascolor
638 if(num==1) swf_GetRGB(tag, NULL);
639 else swf_GetRGBA(tag, NULL);
641 if(flags & 2) { //has x offset
642 swf_ResetReadBits(tag);
645 if(flags & 1) { //has y offset
646 swf_ResetReadBits(tag);
649 if(flags & 8) { //has height
650 swf_ResetReadBits(tag);
653 } else { // glyph record
655 swf_ResetReadBits(tag);
656 for(t=0;t<flags;t++) {
657 swf_GetBits(tag, glyphbits);
658 swf_GetBits(tag, advancebits);
665 case ST_DEFINEFONTINFO:
666 case ST_DEFINEFONTINFO2:
668 callback(tag, tag->pos + base, callback_data);
670 case ST_DEFINEVIDEOSTREAM:
673 case ST_DOINITACTION:
674 callback(tag, tag->pos + base, callback_data);
677 case ST_DEFINEMORPHSHAPE:
678 case ST_DEFINESHAPE3:
680 case ST_DEFINESHAPE2:
682 case ST_DEFINESHAPE: {
688 if(tag->id == ST_DEFINEMORPHSHAPE) {
693 id = swf_GetU16(tag); // id;
694 swf_GetRect(tag, NULL); // bounds
696 swf_ResetReadBits(tag);
697 swf_GetRect(tag, NULL); // bounds2
698 swf_GetU32(tag); //offset to endedges
701 DEBUG_ENUMERATE printf("Tag:%d Name:%s ID:%d\n", tag->id, swf_TagGetName(tag), id);
703 enumerateUsedIDs_styles(tag, callback, callback_data, num, morph);
704 DEBUG_ENUMERATE printf("-------\n");
705 while(--numshapes>=0) /* morph shapes define two shapes */
707 DEBUG_ENUMERATE printf("shape:%d\n", numshapes);
708 fillbits = swf_GetBits(tag, 4);
709 linebits = swf_GetBits(tag, 4);
710 DEBUG_ENUMERATE printf("%d %d\n", fillbits, linebits);
711 swf_ResetReadBits(tag);
714 flags = swf_GetBits(tag, 1);
715 if(!flags) { //style change
716 flags = swf_GetBits(tag, 5);
720 int n = swf_GetBits(tag, 5);
722 x = swf_GetBits(tag, n); //x
723 y = swf_GetBits(tag, n); //y
724 DEBUG_ENUMERATE printf("move %f %f\n",x/20.0,y/20.0);
726 if(flags&2) { //fill0
728 fill0 = swf_GetBits(tag, fillbits);
729 DEBUG_ENUMERATE printf("fill0 %d\n", fill0);
731 if(flags&4) { //fill1
733 fill1 = swf_GetBits(tag, fillbits);
734 DEBUG_ENUMERATE printf("fill1 %d\n", fill1);
736 if(flags&8) { //linestyle
738 line = swf_GetBits(tag, linebits);
739 DEBUG_ENUMERATE printf("linestyle %d\n",line);
742 DEBUG_ENUMERATE printf("more fillstyles\n");
743 enumerateUsedIDs_styles(tag, callback, callback_data, num, 0);
744 fillbits = swf_GetBits(tag, 4);
745 linebits = swf_GetBits(tag, 4);
748 flags = swf_GetBits(tag, 1);
749 if(flags) { //straight edge
750 int n = swf_GetBits(tag, 4) + 2;
751 if(swf_GetBits(tag, 1)) { //line flag
753 x = swf_GetSBits(tag, n); //delta x
754 y = swf_GetSBits(tag, n); //delta y
755 DEBUG_ENUMERATE printf("line %f %f\n",x/20.0,y/20.0);
757 int v=swf_GetBits(tag, 1);
759 d = swf_GetSBits(tag, n); //vert/horz
760 DEBUG_ENUMERATE printf("%s %f\n",v?"vertical":"horizontal", d/20.0);
762 } else { //curved edge
763 int n = swf_GetBits(tag, 4) + 2;
765 x1 = swf_GetSBits(tag, n);
766 y1 = swf_GetSBits(tag, n);
767 x2 = swf_GetSBits(tag, n);
768 y2 = swf_GetSBits(tag, n);
769 DEBUG_ENUMERATE printf("curve %f %f %f %f\n", x1/20.0, y1/20.0, x2/20.0, y2/20.0);
781 void callbackCount(TAG * t,int pos, void*ptr)
784 DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
787 void callbackFillin(TAG * t,int pos, void*ptr)
791 DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
794 int swf_GetNumUsedIDs(TAG * t)
797 enumerateUsedIDs(t, 0, callbackCount, &num);
801 void swf_GetUsedIDs(TAG * t, int * positions)
803 int * ptr = positions;
804 enumerateUsedIDs(t, 0, callbackFillin, &ptr);
807 void swf_Relocate (SWF*swf, char*bitmap)
811 memset(slaveids, -1, sizeof(slaveids));
819 if(swf_isDefiningTag(tag))
824 id = swf_GetDefineID(tag); //own id
826 if(!bitmap[id]) { //free
831 for (t=1;t<65536;t++)
841 slaveids[id] = newid;
843 swf_SetDefineID(tag, newid);
846 num = swf_GetNumUsedIDs(tag);
847 ptr = malloc(sizeof(int)*num);
848 swf_GetUsedIDs(tag, ptr);
851 int id = GET16(&tag->data[ptr[t]]);
853 fprintf(stderr, "swf_Relocate: Mapping id never encountered before: %d\n", id);
857 PUT16(&tag->data[ptr[t]], id);
863 void swf_RelocateDepth(SWF*swf, char*bitmap)
868 for(nr=65535;nr>=0;nr--) {
872 // now nr is the highest used depth. So we start
873 // assigning depths at nr+1
878 /* TODO * clip depths
881 int depth = swf_GetDepth(tag);
883 int newdepth = depth+nr;
885 fprintf(stderr, "Couldn't relocate depths: too large values\n");
888 swf_SetDepth(tag, newdepth);
894 U8 swf_isShapeTag(TAG*tag)
896 if(tag->id == ST_DEFINESHAPE ||
897 tag->id == ST_DEFINESHAPE2 ||
898 tag->id == ST_DEFINESHAPE3)
903 U8 swf_isImageTag(TAG*tag)
905 if(tag->id == ST_DEFINEBITSJPEG ||
906 tag->id == ST_DEFINEBITSJPEG2 ||
907 tag->id == ST_DEFINEBITSJPEG3 ||
908 tag->id == ST_DEFINEBITSLOSSLESS ||
909 tag->id == ST_DEFINEBITSLOSSLESS2)
914 TAG* swf_Concatenate (TAG*list1,TAG*list2)
916 TAG*tag=0,*lasttag=0;
918 char depthmap[65536];
920 memset(bitmap, 0, sizeof(bitmap));
921 memset(depthmap, 0, sizeof(depthmap));
922 memset(&swf1, 0, sizeof(swf1));
923 memset(&swf2, 0, sizeof(swf2));
925 swf1.firstTag = list1;
927 swf2.firstTag = list2;
932 if(!swf_isDefiningTag(tag)) {
933 int id = swf_GetDefineID(tag);
936 if(tag->id == ST_PLACEOBJECT ||
937 tag->id == ST_PLACEOBJECT2) {
938 int depth = swf_GetDepth(tag);
941 if(tag->id == ST_REMOVEOBJECT ||
942 tag->id == ST_REMOVEOBJECT2) {
943 int depth = swf_GetDepth(tag);
949 swf_Relocate(&swf2, bitmap);
950 swf_RelocateDepth(&swf2, depthmap);
951 lasttag->next = swf2.firstTag;
952 swf2.firstTag->prev = lasttag;
954 return swf1.firstTag;
957 static int tagHash(TAG*tag)
960 unsigned int a = 0x6b973e5a;
961 /* start at pos 2, as 0 and 1 are the id */
962 for(t=2;t<tag->len;t++) {
965 a += tag->data[t]*0xefbc35a5*b*(t+1);
967 return a&0x7fffffff; //always return unsigned
970 void swf_Optimize(SWF*swf)
972 const int hash_size = 131072;
973 char* dontremap = malloc(sizeof(char)*65536);
974 U16* remap = malloc(sizeof(U16)*65536);
975 TAG* id2tag = malloc(sizeof(TAG*)*65536);
976 TAG** hashmap = malloc(sizeof(TAG*)*hash_size);
978 memset(dontremap, 0, sizeof(char)*65536);
979 memset(hashmap, 0, sizeof(TAG*)*hash_size);
980 memset(id2tag, 0, sizeof(TAG*)*65536);
982 for(t=0;t<65536;t++) {
990 /* make sure we don't remap to this tag,
991 as it might have different "helper tags"
992 FIXME: a better way would be to compare
993 the helper tags, too.
995 if(swf_isPseudoDefiningTag(tag)) {
996 //dontremap[swf_GetDefineID(tag)] = 1; //FIXME
1000 tag = swf->firstTag;
1004 TAG*next = tag->next;
1006 if(swf_isDefiningTag(tag)) {
1008 int id = swf_GetDefineID(tag);
1009 int hash = tagHash(tag);
1012 while((tag2 = hashmap[hash%hash_size])) {
1013 if(tag2 != (TAG*)(-1) && tag->len == tag2->len) {
1015 /* start at pos 2, as 0 and 1 are the id */
1016 for(t=2;t<tag->len;t++) {
1017 if(tag->data[t] != tag2->data[t])
1025 /* we found two identical tags- remap one
1027 remap[id] = swf_GetDefineID(tag2);
1033 while(hashmap[hash%hash_size]) hash++;
1034 hashmap[hash%hash_size] = tag;
1037 if(tag == swf->firstTag)
1038 swf->firstTag = next;
1041 } else if(swf_isPseudoDefiningTag(tag)) {
1042 int id = swf_GetDefineID(tag);
1044 /* if this tag was remapped, we don't
1045 need the helper tag anymore. Discard
1048 if(tag == swf->firstTag)
1049 swf->firstTag = next;
1056 int num = swf_GetNumUsedIDs(tag);
1057 int*positions = malloc(sizeof(int)*num);
1059 swf_GetUsedIDs(tag, positions);
1060 for(t=0;t<num;t++) {
1061 int id = GET16(&tag->data[positions[t]]);
1063 PUT16(&tag->data[positions[t]], id);