3 * Functions for struct-dna, the genetic file dot c!
7 * ***** BEGIN GPL LICENSE BLOCK *****
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24 * All rights reserved.
26 * The Original Code is: all of this file.
28 * Contributor(s): none yet.
30 * ***** END GPL LICENSE BLOCK *****
39 #include <unistd.h> // for read close
41 #include <io.h> // for open close read
44 #include <string.h> // strncmp
45 #include <stdio.h> // for printf
46 #include <stdlib.h> // for atoi
47 #include <fcntl.h> // for open O_RDONLY
49 #include "MEM_guardedalloc.h" // for MEM_freeN MEM_mallocN MEM_callocN
50 #include "BLI_blenlib.h" // for BLI_filesize
52 #include "BKE_utildefines.h" // for O_BINARY TRUE MIN2
54 #include "DNA_sdna_types.h" // for SDNA ;-)
56 #include "BLO_writefile.h"
57 #include "BLO_genfile.h"
61 #include "BLO_sys_types.h" // for intptr_t support
63 /* gcc 4.1 on mingw was complaining that __int64 was alredy defined
64 actually is saw the line below as typedef long long long long...
65 Anyhow, since its alredy defined, its safe to do an ifndef here- Cambpell*/
68 typedef long long __int64;
73 * - please note: no builtin security to detect input of double structs
74 * - if you want a struct not to be in DNA file: add two hash marks above it (#<enter>#<enter>)
76 Structure DNA data is added to each blender file and to each executable, this to detect
77 in .blend files new veriables in structs, changed array sizes, etc. It's also used for
78 converting endian and pointer size (32-64 bits)
79 As an extra, Python uses a call to detect run-time the contents of a blender struct.
81 Create a structDNA: only needed when one of the input include (.h) files change.
83 SDNA (4 bytes) (magic number)
85 <nr> (4 bytes) amount of names (int)
91 <nr> amount of types (int)
97 <len> (short) the lengths of types
102 <nr> amount of structs (int)
103 <typenr><nr_of_elems> <typenr><namenr> <typenr><namenr> ...
105 !!Remember to read/write integer and short aligned!!
107 While writing a file, the names of a struct is indicated with a type number,
108 to be found with: type= findstruct_nr(SDNA *, char *)
109 The value of 'type' corresponds with the the index within the structs array
111 For the moment: the complete DNA file is included in a .blend file. For
112 the future we can think of smarter methods, like only included the used
113 structs. Only needed to keep a file short though...
115 ALLOWED AND TESTED CHANGES IN STRUCTS:
116 - type change (a char to float will be divided by 255)
117 - location within a struct (everthing can be randomly mixed up)
118 - struct within struct (within struct etc), this is recursive
119 - adding new elements, will be default initialized zero
121 - change of array sizes
122 - change of a pointer type: when the name doesn't change the contents is copied
125 - array (vec[3]) to float struct (vec3f)
128 - endian compatibility
129 - pointer conversion (32-64 bits)
132 - do not use #defines in structs for array lenghts, this cannot be read by the dna functions
133 - do not use uint, but unsigned int instead, ushort and ulong are allowed
134 - only use a long in Blender if you want this to be the size of a pointer. so it is
135 32 bits or 64 bits, dependant at the cpu architecture
136 - chars are always unsigned
137 - aligment of variables has to be done in such a way, that any system does
138 not create 'padding' (gaps) in structures. So make sure that:
145 - the sdna functions have several error prints builtin, always check blender running from a console.
150 static int le_int(int temp);
151 static short le_short(short temp);
153 /* ************************* ENDIAN STUFF ********************** */
155 static short le_short(short temp)
158 char *rt=(char *)&temp, *rtn=(char *)&new;
167 static int le_int(int temp)
170 char *rt=(char *)&temp, *rtn=(char *)&new;
181 /* ************************* MAKE DNA ********************** */
183 /* allowed duplicate code from makesdna.c */
184 static int arraysize(char *astr, int len)
187 char str[100], *cp=0;
189 memcpy(str, astr, len+1);
191 for(a=0; a<len; a++) {
195 else if( str[a]==']' && cp) {
204 /* ************************* END MAKE DNA ********************** */
206 /* ************************* DIV ********************** */
208 void dna_freestructDNA(struct SDNA *sdna)
210 MEM_freeN(sdna->data);
211 MEM_freeN(sdna->names);
212 MEM_freeN(sdna->types);
213 MEM_freeN(sdna->structs);
218 static int ispointer(char *name)
220 /* check if pointer or function pointer */
221 return (name[0]=='*' || (name[0]=='(' && name[1]=='*'));
224 static int elementsize(struct SDNA *sdna, short type, short name)
225 /* call with numbers from struct-array */
227 int mul, namelen, len;
230 cp= sdna->names[name];
234 /* is it a pointer or function pointer? */
236 /* has the naam an extra length? (array) */
238 if( cp[namelen-1]==']') mul= arraysize(cp, namelen);
240 len= sdna->pointerlen*mul;
242 else if( sdna->typelens[type] ) {
243 /* has the naam an extra length? (array) */
245 if( cp[namelen-1]==']') mul= arraysize(cp, namelen);
247 len= mul*sdna->typelens[type];
255 static void printstruct(struct SDNA *sdna, short strnr)
261 sp= sdna->structs[strnr];
263 printf("struct %s\n", sdna->types[ sp[0] ]);
267 for(b=0; b< nr; b++, sp+= 2) {
268 printf(" %s %s\n", sdna->types[sp[0]], sdna->names[sp[1]]);
273 static short *findstruct_name(struct SDNA *sdna, char *str)
279 for(a=0; a<sdna->nr_structs; a++) {
281 sp= sdna->structs[a];
283 if(strcmp( sdna->types[ sp[0] ], str )==0) return sp;
289 int dna_findstruct_nr(struct SDNA *sdna, char *str)
294 if(sdna->lastfind<sdna->nr_structs) {
295 sp= sdna->structs[sdna->lastfind];
296 if(strcmp( sdna->types[ sp[0] ], str )==0) return sdna->lastfind;
299 for(a=0; a<sdna->nr_structs; a++) {
301 sp= sdna->structs[a];
303 if(strcmp( sdna->types[ sp[0] ], str )==0) {
312 /* ************************* END DIV ********************** */
314 /* ************************* READ DNA ********************** */
316 static void init_structDNA(struct SDNA *sdna, int do_endian_swap)
317 /* in sdna->data the data, now we convert that to something understandable */
325 data= (int *)sdna->data;
328 if( *data == *verg ) {
332 /* load names array */
334 if( *data == *verg ) {
337 if(do_endian_swap) sdna->nr_names= le_int(*data);
338 else sdna->nr_names= *data;
341 sdna->names= MEM_callocN( sizeof(void *)*sdna->nr_names, "sdnanames");
344 printf("NAME error in SDNA file\n");
350 while(nr<sdna->nr_names) {
356 nr= (intptr_t)cp; /* prevent BUS error */
360 /* load type names array */
363 if( *data == *verg ) {
366 if(do_endian_swap) sdna->nr_types= le_int(*data);
367 else sdna->nr_types= *data;
370 sdna->types= MEM_callocN( sizeof(void *)*sdna->nr_types, "sdnatypes");
373 printf("TYPE error in SDNA file\n");
379 while(nr<sdna->nr_types) {
382 /* this is a patch, to change struct names without a confict with SDNA */
383 /* be careful to use it, in this case for a system-struct (opengl/X) */
386 /* struct Screen was already used by X, 'bScreen' replaces the old IrisGL 'Screen' struct */
387 if( strcmp("bScreen", cp)==0 ) sdna->types[nr]= cp+1;
394 nr= (intptr_t)cp; /* prevent BUS error */
398 /* load typelen array */
401 if( *data == *verg ) {
411 spo[0]= le_short(spo[0]);
419 printf("TLEN error in SDNA file\n");
422 if(sdna->nr_types & 1) sp++; /* prevent BUS error */
424 /* load struct array */
427 if( *data == *verg ) {
430 if(do_endian_swap) sdna->nr_structs= le_int(*data);
431 else sdna->nr_structs= *data;
434 sdna->structs= MEM_callocN( sizeof(void *)*sdna->nr_structs, "sdnastrcs");
437 printf("STRC error in SDNA file\n");
443 while(nr<sdna->nr_structs) {
444 sdna->structs[nr]= sp;
449 sp[0]= le_short(sp[0]);
450 sp[1]= le_short(sp[1]);
455 sp[0]= le_short(sp[0]);
456 sp[1]= le_short(sp[1]);
467 /* finally pointerlen: use struct ListBase to test it, never change the size of it! */
468 sp= findstruct_name(sdna, "ListBase");
469 /* weird; i have no memory of that... I think I used sizeof(void *) before... (ton) */
471 sdna->pointerlen= sdna->typelens[ sp[0] ]/2;
473 if(sp[1]!=2 || (sdna->pointerlen!=4 && sdna->pointerlen!=8)) {
474 printf("ListBase struct error! Needs it to calculate pointerize.\n");
476 /* well, at least sizeof(ListBase) is error proof! (ton) */
482 struct SDNA *dna_sdna_from_data(void *data, int datalen, int do_endian_swap)
484 struct SDNA *sdna= MEM_mallocN(sizeof(*sdna), "sdna");
488 sdna->datalen= datalen;
489 sdna->data= MEM_mallocN(datalen, "sdna_data");
490 memcpy(sdna->data, data, datalen);
492 init_structDNA(sdna, do_endian_swap);
497 /* ******************** END READ DNA ********************** */
499 /* ******************* HANDLE DNA ***************** */
501 static void recurs_test_compflags(struct SDNA *sdna, char *compflags, int structnr)
503 int a, b, typenr, elems;
507 /* check all structs, test if it's inside another struct */
508 sp= sdna->structs[structnr];
511 for(a=0; a<sdna->nr_structs; a++) {
512 if(a!=structnr && compflags[a]==1) {
513 sp= sdna->structs[a];
516 for(b=0; b<elems; b++, sp+=2) {
518 cp= sdna->names[ sp[1] ];
521 recurs_test_compflags(sdna, compflags, a);
530 /* Unsure of exact function - compares the sdna argument to
531 * newsdna and sets up the information necessary to convert
532 * data written with a dna of oldsdna to inmemory data with a
533 * structure defined by the newsdna sdna (I think). -zr
536 /* well, the function below is just a lookup table to speed
537 * up reading files. doh! -ton
541 char *dna_get_structDNA_compareflags(struct SDNA *sdna, struct SDNA *newsdna)
543 /* flag: 0: doesn't exist anymore (or not yet)
548 short *spold, *spcur;
552 if(sdna->nr_structs==0) {
553 printf("error: file without SDNA\n");
557 compflags= MEM_callocN(sdna->nr_structs, "compflags");
559 /* we check all structs in 'sdna' and compare them with
560 * the structs in 'newsdna'
563 for(a=0; a<sdna->nr_structs; a++) {
564 spold= sdna->structs[a];
566 /* search for type in cur */
567 spcur= findstruct_name(newsdna, sdna->types[spold[0]]);
572 /* compare length and amount of elems */
573 if( spcur[1] == spold[1]) {
574 if( newsdna->typelens[spcur[0]] == sdna->typelens[spold[0]] ) {
576 /* same length, same amount of elems, now per type and name */
581 str1= newsdna->types[spcur[0]];
582 str2= sdna->types[spold[0]];
583 if(strcmp(str1, str2)!=0) break;
585 str1= newsdna->names[spcur[1]];
586 str2= sdna->names[spold[1]];
587 if(strcmp(str1, str2)!=0) break;
589 /* same type and same name, now pointersize */
590 if(ispointer(str1)) {
591 if(sdna->pointerlen!=newsdna->pointerlen) break;
598 if(b==0) compflags[a]= 1;
606 /* first struct in util.h is struct Link, this is skipped in compflags (als # 0).
607 * was a bug, and this way dirty patched! Solve this later....
611 /* Because structs can be inside structs, we recursively
612 * set flags when a struct is altered
614 for(a=0; a<sdna->nr_structs; a++) {
615 if(compflags[a]==2) recurs_test_compflags(sdna, compflags, a);
619 for(a=0; a<sdna->nr_structs; a++) {
620 if(compflags[a]==2) {
621 spold= sdna->structs[a];
622 printf("changed: %s\n", sdna->types[ spold[0] ]);
630 static void cast_elem(char *ctype, char *otype, char *name, char *curdata, char *olddata)
633 int arrlen, curlen=1, oldlen=1, ctypenr, otypenr;
635 arrlen= arraysize(name, strlen(name));
638 if(strcmp(otype, "char")==0) otypenr= 0;
639 else if((strcmp(otype, "uchar")==0)||(strcmp(otype, "unsigned char")==0)) otypenr= 1;
640 else if(strcmp(otype, "short")==0) otypenr= 2;
641 else if((strcmp(otype, "ushort")==0)||(strcmp(otype, "unsigned short")==0)) otypenr= 3;
642 else if(strcmp(otype, "int")==0) otypenr= 4;
643 else if(strcmp(otype, "long")==0) otypenr= 5;
644 else if((strcmp(otype, "ulong")==0)||(strcmp(otype, "unsigned long")==0)) otypenr= 6;
645 else if(strcmp(otype, "float")==0) otypenr= 7;
646 else if(strcmp(otype, "double")==0) otypenr= 8;
650 if(strcmp(ctype, "char")==0) ctypenr= 0;
651 else if((strcmp(ctype, "uchar")==0)||(strcmp(ctype, "unsigned char")==0)) ctypenr= 1;
652 else if(strcmp(ctype, "short")==0) ctypenr= 2;
653 else if((strcmp(ctype, "ushort")==0)||(strcmp(ctype, "unsigned short")==0)) ctypenr= 3;
654 else if(strcmp(ctype, "int")==0) ctypenr= 4;
655 else if(strcmp(ctype, "long")==0) ctypenr= 5;
656 else if((strcmp(ctype, "ulong")==0)||(strcmp(ctype, "unsigned long")==0)) ctypenr= 6;
657 else if(strcmp(ctype, "float")==0) ctypenr= 7;
658 else if(strcmp(ctype, "double")==0) ctypenr= 8;
662 if(otypenr < 2) oldlen= 1;
663 else if(otypenr < 4) oldlen= 2;
664 else if(otypenr < 8) oldlen= 4;
667 if(ctypenr < 2) curlen= 1;
668 else if(ctypenr < 4) curlen= 2;
669 else if(ctypenr < 8) curlen= 4;
675 val= *olddata; break;
677 val= *( (unsigned char *)olddata); break;
679 val= *( (short *)olddata); break;
681 val= *( (unsigned short *)olddata); break;
683 val= *( (int *)olddata); break;
685 val= *( (int *)olddata); break;
687 val= *( (unsigned int *)olddata); break;
689 val= *( (float *)olddata); break;
691 val= *( (double *)olddata); break;
696 *curdata= val; break;
698 *( (unsigned char *)curdata)= val; break;
700 *( (short *)curdata)= val; break;
702 *( (unsigned short *)curdata)= val; break;
704 *( (int *)curdata)= val; break;
706 *( (int *)curdata)= val; break;
708 *( (unsigned int *)curdata)= val; break;
710 if(otypenr<2) val/= 255;
711 *( (float *)curdata)= val; break;
713 if(otypenr<2) val/= 255;
714 *( (double *)curdata)= val; break;
723 static void cast_pointer(int curlen, int oldlen, char *name, char *curdata, char *olddata)
732 arrlen= arraysize(name, strlen(name));
737 memcpy(curdata, olddata, curlen);
739 else if(curlen==4 && oldlen==8) {
741 lval= *( (__int64 *)olddata );
743 lval= *( (long long *)olddata );
745 *((int *)curdata) = lval>>3; /* is of course gambling! */
747 else if(curlen==8 && oldlen==4) {
749 *( (__int64 *)curdata ) = *((int *)olddata);
751 *( (long long *)curdata ) = *((int *)olddata);
756 printf("errpr: illegal pointersize! \n");
766 static int elem_strcmp(char *name, char *oname)
770 /* strcmp without array part */
773 if(name[a] != oname[a]) return 1;
774 if(name[a]=='[') break;
775 if(name[a]==0) break;
778 if(name[a] != oname[a]) return 1;
782 static char *find_elem(struct SDNA *sdna, char *type, char *name, short *old, char *olddata, short **sppo)
784 int a, elemcount, len;
787 /* without arraypart, so names can differ: return old namenr and type */
789 /* in old is the old struct */
792 for(a=0; a<elemcount; a++, old+=2) {
794 otype= sdna->types[old[0]];
795 oname= sdna->names[old[1]];
797 len= elementsize(sdna, old[0], old[1]);
799 if( elem_strcmp(name, oname)==0 ) { /* naam equal */
800 if( strcmp(type, otype)==0 ) { /* type equal */
813 static void reconstruct_elem(struct SDNA *newsdna, struct SDNA *oldsdna, char *type, char *name, char *curdata, short *old, char *olddata)
815 /* rules: test for NAME:
818 - name partially equal (array differs)
821 (nzc 2-4-2001 I want the 'unsigned' bit to be parsed as well. Where
824 int a, elemcount, len, array, oldsize, cursize, mul;
825 char *otype, *oname, *cp;
827 /* is 'name' an array? */
830 while( *cp && *cp!='[') {
833 if( *cp!= '[' ) array= 0;
835 /* in old is the old struct */
838 for(a=0; a<elemcount; a++, old+=2) {
839 otype= oldsdna->types[old[0]];
840 oname= oldsdna->names[old[1]];
841 len= elementsize(oldsdna, old[0], old[1]);
843 if( strcmp(name, oname)==0 ) { /* name equal */
845 if(ispointer(name)) { /* pointer of functionpointer afhandelen */
846 cast_pointer(newsdna->pointerlen, oldsdna->pointerlen, name, curdata, olddata);
848 else if( strcmp(type, otype)==0 ) { /* type equal */
849 memcpy(curdata, olddata, len);
851 else cast_elem(type, otype, name, curdata, olddata);
855 else if(array) { /* name is an array */
857 if(oname[array]=='[' && strncmp(name, oname, array)==0 ) { /* basis equal */
859 cursize= arraysize(name, strlen(name));
860 oldsize= arraysize(oname, strlen(oname));
862 if(ispointer(name)) { /* handle pointer or functionpointer */
863 if(cursize>oldsize) cast_pointer(newsdna->pointerlen, oldsdna->pointerlen, oname, curdata, olddata);
864 else cast_pointer(newsdna->pointerlen, oldsdna->pointerlen, name, curdata, olddata);
866 else if(strcmp(type, otype)==0 ) { /* type equal */
868 mul*= MIN2(cursize, oldsize);
869 memcpy(curdata, olddata, mul);
872 if(cursize>oldsize) cast_elem(type, otype, oname, curdata, olddata);
873 else cast_elem(type, otype, name, curdata, olddata);
882 static void reconstruct_struct(struct SDNA *newsdna, struct SDNA *oldsdna, char *compflags, int oldSDNAnr, char *data, int curSDNAnr, char *cur)
885 * Per element from cur_struct, read data from old_struct.
886 * If element is a struct, call recursive.
888 int a, elemcount, elen, eleno, mul, mulo, firststructtypenr;
889 short *spo, *spc, *sppo;
890 char *name, *nameo, *type, *cpo, *cpc;
892 if(oldSDNAnr== -1) return;
893 if(curSDNAnr== -1) return;
895 if( compflags[oldSDNAnr]==1 ) { /* if recursive: test for equal */
897 spo= oldsdna->structs[oldSDNAnr];
898 elen= oldsdna->typelens[ spo[0] ];
899 memcpy( cur, data, elen);
904 firststructtypenr= *(newsdna->structs[0]);
906 spo= oldsdna->structs[oldSDNAnr];
907 spc= newsdna->structs[curSDNAnr];
913 for(a=0; a<elemcount; a++, spc+=2) {
914 type= newsdna->types[spc[0]];
915 name= newsdna->names[spc[1]];
917 elen= elementsize(newsdna, spc[0], spc[1]);
919 /* test: is type a struct? */
920 if(spc[0]>=firststructtypenr && !ispointer(name)) {
922 /* where does the old struct data start (and is there an old one?) */
923 cpo= find_elem(oldsdna, type, name, spo, data, &sppo);
926 oldSDNAnr= dna_findstruct_nr(oldsdna, type);
927 curSDNAnr= dna_findstruct_nr(newsdna, type);
930 mul= arraysize(name, strlen(name));
931 nameo= oldsdna->names[sppo[1]];
932 mulo= arraysize(nameo, strlen(nameo));
934 eleno= elementsize(oldsdna, sppo[0], sppo[1]);
940 reconstruct_struct(newsdna, oldsdna, compflags, oldSDNAnr, cpo, curSDNAnr, cpc);
944 /* new struct array larger than old */
953 reconstruct_elem(newsdna, oldsdna, type, name, cpc, spo, data);
960 void dna_switch_endian_struct(struct SDNA *oldsdna, int oldSDNAnr, char *data)
963 * If element is a struct, call recursive.
965 int a, mul, elemcount, elen, elena, firststructtypenr;
966 short *spo, *spc, skip;
967 char *name, *type, *cpo, *cur, cval;
969 if(oldSDNAnr== -1) return;
970 firststructtypenr= *(oldsdna->structs[0]);
972 spo= spc= oldsdna->structs[oldSDNAnr];
979 for(a=0; a<elemcount; a++, spc+=2) {
980 type= oldsdna->types[spc[0]];
981 name= oldsdna->names[spc[1]];
983 /* elementsize = including arraysize */
984 elen= elementsize(oldsdna, spc[0], spc[1]);
986 /* test: is type a struct? */
987 if(spc[0]>=firststructtypenr && !ispointer(name)) {
988 /* where does the old data start (is there one?) */
989 cpo= find_elem(oldsdna, type, name, spo, data, 0);
991 oldSDNAnr= dna_findstruct_nr(oldsdna, type);
993 mul= arraysize(name, strlen(name));
997 dna_switch_endian_struct(oldsdna, oldSDNAnr, cpo);
1004 if(ispointer(name)) {
1005 if(oldsdna->pointerlen==8) {
1007 mul= arraysize(name, strlen(name));
1010 cval= cpo[0]; cpo[0]= cpo[7]; cpo[7]= cval;
1011 cval= cpo[1]; cpo[1]= cpo[6]; cpo[6]= cval;
1012 cval= cpo[2]; cpo[2]= cpo[5]; cpo[5]= cval;
1013 cval= cpo[3]; cpo[3]= cpo[4]; cpo[4]= cval;
1022 if( spc[0]==2 || spc[0]==3 ) { /* short-ushort */
1024 /* exception: variable called blocktype/ipowin: derived from ID_ */
1026 if(name[0]=='b' && name[1]=='l') {
1027 if(strcmp(name, "blocktype")==0) skip= 1;
1029 else if(name[0]=='i' && name[1]=='p') {
1030 if(strcmp(name, "ipowin")==0) skip= 1;
1034 mul= arraysize(name, strlen(name));
1044 else if(spc[0]>3 && spc[0]<8) { /* int-long-ulong-float */
1046 mul= arraysize(name, strlen(name));
1064 void *dna_reconstruct(struct SDNA *newsdna, struct SDNA *oldsdna, char *compflags, int oldSDNAnr, int blocks, void *data)
1066 int a, curSDNAnr, curlen=0, oldlen;
1068 char *cur, *type, *cpc, *cpo;
1070 /* oldSDNAnr == structnr, we're looking for the corresponding 'cur' number */
1071 spo= oldsdna->structs[oldSDNAnr];
1072 type= oldsdna->types[ spo[0] ];
1073 oldlen= oldsdna->typelens[ spo[0] ];
1074 curSDNAnr= dna_findstruct_nr(newsdna, type);
1076 /* init data and alloc */
1077 if(curSDNAnr >= 0) {
1078 spc= newsdna->structs[curSDNAnr];
1079 curlen= newsdna->typelens[ spc[0] ];
1085 cur= MEM_callocN( blocks*curlen, "reconstruct");
1088 for(a=0; a<blocks; a++) {
1089 reconstruct_struct(newsdna, oldsdna, compflags, oldSDNAnr, cpo, curSDNAnr, cpc);
1097 int dna_elem_offset(struct SDNA *sdna, char *stype, char *vartype, char *name)
1100 int SDNAnr= dna_findstruct_nr(sdna, stype);
1101 short *spo= sdna->structs[SDNAnr];
1102 char *cp= find_elem(sdna, vartype, name, spo, NULL, NULL);
1103 return (int)((intptr_t)cp);