| /* | |
| * Typelib (SLTG) generation | |
| * | |
| * Copyright 2015,2016 Dmitry Timoshkov | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include "config.h" | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <stdarg.h> | |
| #include <stdio.h> | |
| #include <ctype.h> | |
| #include <time.h> | |
| #include "widl.h" | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "typelib.h" | |
| #include "typelib_struct.h" | |
| #include "utils.h" | |
| #include "header.h" | |
| #include "typetree.h" | |
| static const GUID sltg_library_guid = { 0x204ff,0,0,{ 0xc0,0,0,0,0,0,0,0x46 } }; | |
| struct sltg_data | |
| { | |
| int size, allocated; | |
| char *data; | |
| }; | |
| struct sltg_library | |
| { | |
| short name; | |
| char *helpstring; | |
| char *helpfile; | |
| int helpcontext; | |
| int syskind; | |
| LCID lcid; | |
| int libflags; | |
| int version; | |
| GUID uuid; | |
| }; | |
| struct sltg_block | |
| { | |
| int length; | |
| int index_string; | |
| void *data; | |
| }; | |
| struct sltg_typelib | |
| { | |
| typelib_t *typelib; | |
| struct sltg_data index; | |
| struct sltg_data name_table; | |
| struct sltg_library library; | |
| struct sltg_block *blocks; | |
| int block_count; | |
| int first_block; | |
| short typeinfo_count; | |
| int typeinfo_size; | |
| struct sltg_block *typeinfo; | |
| }; | |
| struct sltg_hrefmap | |
| { | |
| int href_count; | |
| int *href; | |
| }; | |
| #pragma pack(push,1) | |
| struct sltg_typeinfo_header | |
| { | |
| short magic; | |
| int href_offset; | |
| int res06; | |
| int member_offset; | |
| int res0e; | |
| int version; | |
| int res16; | |
| struct | |
| { | |
| unsigned unknown1 : 3; | |
| unsigned flags : 13; | |
| unsigned unknown2 : 8; | |
| unsigned typekind : 8; | |
| } misc; | |
| int res1e; | |
| }; | |
| struct sltg_member_header | |
| { | |
| short res00; | |
| short res02; | |
| char res04; | |
| int extra; | |
| }; | |
| struct sltg_variable | |
| { | |
| char magic; /* 0x0a */ | |
| char flags; | |
| short next; | |
| short name; | |
| short byte_offs; /* pos in struct, or offset to const type or const data (if flags & 0x08) */ | |
| short type; /* if flags & 0x02 this is the type, else offset to type */ | |
| int memid; | |
| short helpcontext; | |
| short helpstring; | |
| short varflags; /* only present if magic & 0x20 */ | |
| }; | |
| struct sltg_tail | |
| { | |
| short cFuncs; | |
| short cVars; | |
| short cImplTypes; | |
| short res06; /* always 0000 */ | |
| short funcs_off; /* offset to functions (starting from the member header) */ | |
| short vars_off; /* offset to vars (starting from the member header) */ | |
| short impls_off; /* offset to implemented types (starting from the member header) */ | |
| short funcs_bytes; /* bytes used by function data */ | |
| short vars_bytes; /* bytes used by var data */ | |
| short impls_bytes; /* bytes used by implemented type data */ | |
| short tdescalias_vt; /* for TKIND_ALIAS */ | |
| short res16; /* always ffff */ | |
| short res18; /* always 0000 */ | |
| short res1a; /* always 0000 */ | |
| short simple_alias; /* tdescalias_vt is a vt rather than an offset? */ | |
| short res1e; /* always 0000 */ | |
| short cbSizeInstance; | |
| short cbAlignment; | |
| short res24; | |
| short res26; | |
| short cbSizeVft; | |
| short res2a; /* always ffff */ | |
| short res2c; /* always ffff */ | |
| short res2e; /* always ffff */ | |
| short res30; /* always ffff */ | |
| short res32; /* unknown */ | |
| short type_bytes; /* bytes used by type descriptions */ | |
| }; | |
| struct sltg_hrefinfo | |
| { | |
| char magic; /* 0xdf */ | |
| char res01; /* 0x00 */ | |
| char res02[0x42]; /* 0xff... */ | |
| int number; /* this is 8 times the number of refs */ | |
| /* Now we have number bytes (8 for each ref) of SLTG_UnknownRefInfo */ | |
| short res50;/* 0xffff */ | |
| char res52; /* 0x01 */ | |
| int res53; /* 0x00000000 */ | |
| /* Now we have number/8 SLTG_Names (first WORD is no of bytes in the ascii | |
| * string). Strings look like "*\Rxxxx*#n". If xxxx == ffff then the | |
| * ref refers to the nth type listed in this library (0 based). Else | |
| * the xxxx (which maybe fewer than 4 digits) is the offset into the name | |
| * table to a string "*\G{<guid>}#1.0#0#C:\WINNT\System32\stdole32.tlb#" | |
| * The guid is the typelib guid; the ref again refers to the nth type of | |
| * the imported typelib. | |
| */ | |
| char resxx; /* 0xdf */ | |
| }; | |
| struct sltg_function | |
| { | |
| char magic; /* 0x4c, 0xcb or 0x8b with optional SLTG_FUNCTION_FLAGS_PRESENT flag */ | |
| char flags; /* high nibble is INVOKE_KIND, low nibble = 2 */ | |
| short next; /* byte offset from beginning of group to next fn */ | |
| short name; /* Offset within name table to name */ | |
| int dispid; /* dispid */ | |
| short helpcontext; /* helpcontext (again 1 is special) */ | |
| short helpstring; /* helpstring offset to offset */ | |
| short arg_off; /* offset to args from start of block */ | |
| char nacc; /* lowest 3bits are CALLCONV, rest are no of args */ | |
| char retnextopt; /* if 0x80 bit set ret type follows else next WORD | |
| is offset to ret type. No of optional args is | |
| middle 6 bits */ | |
| short rettype; /* return type VT_?? or offset to ret type */ | |
| short vtblpos; /* position in vtbl? */ | |
| short funcflags; /* present if magic & 0x20 */ | |
| /* Param list starts, repeat next two as required */ | |
| #if 0 | |
| WORD name; /* offset to 2nd letter of name */ | |
| WORD+ type; /* VT_ of param */ | |
| #endif | |
| }; | |
| struct sltg_impl_info | |
| { | |
| short res00; | |
| short next; | |
| short res04; | |
| char impltypeflags; | |
| char res07; | |
| short res08; | |
| short ref; | |
| short res0c; | |
| short res0e; | |
| short res10; | |
| short res12; | |
| short pos; | |
| }; | |
| #pragma pack(pop) | |
| static void add_structure_typeinfo(struct sltg_typelib *typelib, type_t *type); | |
| static void add_interface_typeinfo(struct sltg_typelib *typelib, type_t *type); | |
| static void add_enum_typeinfo(struct sltg_typelib *typelib, type_t *type); | |
| static void add_union_typeinfo(struct sltg_typelib *typelib, type_t *type); | |
| static void add_coclass_typeinfo(struct sltg_typelib *typelib, type_t *type); | |
| static void init_sltg_data(struct sltg_data *data) | |
| { | |
| data->size = 0; | |
| data->allocated = 0; | |
| data->data = NULL; | |
| } | |
| static int add_index(struct sltg_data *index, const char *name) | |
| { | |
| int name_offset = index->size; | |
| int new_size = index->size + strlen(name) + 1; | |
| chat("add_index: name_offset %d, \"%s\"\n", name_offset, name); | |
| if (new_size > index->allocated) | |
| { | |
| index->allocated = index->allocated ? max(index->allocated * 2, new_size) : new_size; | |
| index->data = xrealloc(index->data, index->allocated); | |
| } | |
| strcpy(index->data + index->size, name); | |
| index->size = new_size; | |
| return name_offset; | |
| } | |
| static void init_index(struct sltg_data *index) | |
| { | |
| static const char compobj[] = { 1,'C','o','m','p','O','b','j',0 }; | |
| init_sltg_data(index); | |
| add_index(index, compobj); | |
| } | |
| static int add_name(struct sltg_data *name_table, const char *name) | |
| { | |
| int name_offset = name_table->size; | |
| int new_size = name_table->size + strlen(name) + 1 + 8; | |
| int aligned_size; | |
| chat("add_name: %s\n", name); | |
| aligned_size = (new_size + 0x1f) & ~0x1f; | |
| if (aligned_size - new_size < 4) | |
| new_size = aligned_size; | |
| else | |
| new_size = (new_size + 1) & ~1; | |
| if (new_size > name_table->allocated) | |
| { | |
| name_table->allocated = name_table->allocated ? max(name_table->allocated * 2, new_size) : new_size; | |
| name_table->data = xrealloc(name_table->data, name_table->allocated); | |
| } | |
| memset(name_table->data + name_table->size, 0xff, 8); | |
| strcpy(name_table->data + name_table->size + 8, name); | |
| name_table->size = new_size; | |
| name_table->data[name_table->size - 1] = 0; /* clear alignment */ | |
| return name_offset; | |
| } | |
| static void init_name_table(struct sltg_data *name_table) | |
| { | |
| init_sltg_data(name_table); | |
| } | |
| static void init_library(struct sltg_typelib *sltg) | |
| { | |
| const attr_t *attr; | |
| sltg->library.name = add_name(&sltg->name_table, sltg->typelib->name); | |
| sltg->library.helpstring = NULL; | |
| sltg->library.helpcontext = 0; | |
| sltg->library.syskind = SYS_WIN32; | |
| sltg->library.lcid = 0x0409; | |
| sltg->library.libflags = 0; | |
| sltg->library.version = 0; | |
| sltg->library.helpfile = NULL; | |
| memset(&sltg->library.uuid, 0, sizeof(sltg->library.uuid)); | |
| if (!sltg->typelib->attrs) return; | |
| LIST_FOR_EACH_ENTRY(attr, sltg->typelib->attrs, const attr_t, entry) | |
| { | |
| const expr_t *expr; | |
| switch (attr->type) | |
| { | |
| case ATTR_VERSION: | |
| { | |
| const version_t *version = attr->u.pval; | |
| unsigned short major = version ? version->major : 0, minor = version ? version->minor : 0; | |
| sltg->library.version = (minor << 16) | major; | |
| break; | |
| } | |
| case ATTR_HELPSTRING: | |
| sltg->library.helpstring = attr->u.pval; | |
| break; | |
| case ATTR_HELPFILE: | |
| sltg->library.helpfile = attr->u.pval; | |
| break; | |
| case ATTR_UUID: | |
| sltg->library.uuid = *(GUID *)attr->u.pval; | |
| break; | |
| case ATTR_HELPCONTEXT: | |
| expr = attr->u.pval; | |
| sltg->library.helpcontext = expr->cval; | |
| break; | |
| case ATTR_LIBLCID: | |
| expr = attr->u.pval; | |
| sltg->library.lcid = expr->cval; | |
| break; | |
| case ATTR_CONTROL: | |
| sltg->library.libflags |= 0x02; /* LIBFLAG_FCONTROL */ | |
| break; | |
| case ATTR_HIDDEN: | |
| sltg->library.libflags |= 0x04; /* LIBFLAG_FHIDDEN */ | |
| break; | |
| case ATTR_RESTRICTED: | |
| sltg->library.libflags |= 0x01; /* LIBFLAG_FRESTRICTED */ | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| } | |
| static void add_block_index(struct sltg_typelib *sltg, void *data, int length, int index) | |
| { | |
| sltg->blocks = xrealloc(sltg->blocks, sizeof(sltg->blocks[0]) * (sltg->block_count + 1)); | |
| sltg->blocks[sltg->block_count].length = length; | |
| sltg->blocks[sltg->block_count].data = data; | |
| sltg->blocks[sltg->block_count].index_string = index; | |
| sltg->block_count++; | |
| } | |
| static void add_block(struct sltg_typelib *sltg, void *data, int size, const char *name) | |
| { | |
| struct sltg_block *block = xmalloc(sizeof(*block)); | |
| int index; | |
| chat("add_block: %p,%d,\"%s\"\n", data, size, name); | |
| index = add_index(&sltg->index, name); | |
| add_block_index(sltg, data, size, index); | |
| } | |
| static void *create_library_block(struct sltg_typelib *typelib, int *size, int *index) | |
| { | |
| void *block; | |
| short *p; | |
| *size = sizeof(short) * 9 + sizeof(int) * 3 + sizeof(GUID); | |
| if (typelib->library.helpstring) *size += strlen(typelib->library.helpstring); | |
| if (typelib->library.helpfile) *size += strlen(typelib->library.helpfile); | |
| block = xmalloc(*size); | |
| p = block; | |
| *p++ = 0x51cc; /* magic */ | |
| *p++ = 3; /* res02 */ | |
| *p++ = typelib->library.name; | |
| *p++ = 0xffff; /* res06 */ | |
| if (typelib->library.helpstring) | |
| { | |
| *p++ = strlen(typelib->library.helpstring); | |
| strcpy((char *)p, typelib->library.helpstring); | |
| p = (short *)((char *)p + strlen(typelib->library.helpstring)); | |
| } | |
| else | |
| *p++ = 0xffff; | |
| if (typelib->library.helpfile) | |
| { | |
| *p++ = strlen(typelib->library.helpfile); | |
| strcpy((char *)p, typelib->library.helpfile); | |
| p = (short *)((char *)p + strlen(typelib->library.helpfile)); | |
| } | |
| else | |
| *p++ = 0xffff; | |
| *(int *)p = typelib->library.helpcontext; | |
| p += 2; | |
| *p++ = typelib->library.syskind; | |
| *p++ = typelib->library.lcid; | |
| *(int *)p = 0; /* res12 */ | |
| p += 2; | |
| *p++ = typelib->library.libflags; | |
| *(int *)p = typelib->library.version; | |
| p += 2; | |
| *(GUID *)p = typelib->library.uuid; | |
| *index = add_index(&typelib->index, "dir"); | |
| return block; | |
| } | |
| static const char *new_index_name(void) | |
| { | |
| static char name[11] = "AAAAAAAAAA"; | |
| static int pos = 0; | |
| char *new_name; | |
| if (name[pos] == 'Z') | |
| { | |
| pos++; | |
| if (pos > 9) | |
| error("too many index names\n"); | |
| } | |
| name[pos]++; | |
| new_name = xmalloc(sizeof(name)); | |
| strcpy(new_name, name); | |
| return new_name; | |
| } | |
| static void sltg_add_typeinfo(struct sltg_typelib *sltg, void *data, int length, const char *name) | |
| { | |
| chat("sltg_add_typeinfo: %p,%d,%s\n", data, length, name); | |
| sltg->typeinfo = xrealloc(sltg->typeinfo, sizeof(sltg->typeinfo[0]) * (sltg->typeinfo_count + 1)); | |
| sltg->typeinfo[sltg->typeinfo_count].length = length; | |
| sltg->typeinfo[sltg->typeinfo_count].data = data; | |
| sltg->typeinfo[sltg->typeinfo_count].index_string = 0; | |
| sltg->typeinfo_count++; | |
| sltg->typeinfo_size += length; | |
| } | |
| static void append_data(struct sltg_data *block, const void *data, int size) | |
| { | |
| int new_size = block->size + size; | |
| if (new_size > block->allocated) | |
| { | |
| block->allocated = max(block->allocated * 2, new_size); | |
| block->data = xrealloc(block->data, block->allocated); | |
| } | |
| memcpy(block->data + block->size, data, size); | |
| block->size = new_size; | |
| } | |
| static void add_module_typeinfo(struct sltg_typelib *typelib, type_t *type) | |
| { | |
| error("add_module_typeinfo: %s not implemented\n", type->name); | |
| } | |
| static const char *add_typeinfo_block(struct sltg_typelib *typelib, const type_t *type, short kind) | |
| { | |
| struct sltg_data block; | |
| const char *index_name, *other_name; | |
| short val; | |
| void *p; | |
| int helpcontext = 0; | |
| GUID guid = { 0 }; | |
| const expr_t *expr; | |
| index_name = new_index_name(); | |
| other_name = new_index_name(); | |
| expr = get_attrp(type->attrs, ATTR_HELPCONTEXT); | |
| if (expr) helpcontext = expr->cval; | |
| p = get_attrp(type->attrs, ATTR_UUID); | |
| if (p) guid = *(GUID *)p; | |
| init_sltg_data(&block); | |
| val = strlen(index_name); | |
| append_data(&block, &val, sizeof(val)); | |
| append_data(&block, index_name, val); | |
| val = strlen(other_name); | |
| append_data(&block, &val, sizeof(val)); | |
| append_data(&block, other_name, val); | |
| val = -1; /* res1a */ | |
| append_data(&block, &val, sizeof(val)); | |
| val = add_name(&typelib->name_table, type->name); /* name offset */ | |
| append_data(&block, &val, sizeof(val)); | |
| val = 0; /* FIXME: helpstring */ | |
| append_data(&block, &val, sizeof(val)); | |
| val = -1; /* res20 */ | |
| append_data(&block, &val, sizeof(val)); | |
| append_data(&block, &helpcontext, sizeof(helpcontext)); | |
| val = -1; /* res26 */ | |
| append_data(&block, &val, sizeof(val)); | |
| append_data(&block, &guid, sizeof(guid)); | |
| append_data(&block, &kind, sizeof(kind)); | |
| sltg_add_typeinfo(typelib, block.data, block.size, index_name); | |
| return index_name; | |
| } | |
| static void init_typeinfo(struct sltg_typeinfo_header *ti, const type_t *type, short kind, | |
| const struct sltg_hrefmap *hrefmap) | |
| { | |
| unsigned short major, minor; | |
| get_version( type->attrs, &major, &minor ); | |
| ti->magic = 0x0501; | |
| ti->href_offset = -1; | |
| ti->res06 = -1; | |
| ti->member_offset = sizeof(*ti); | |
| ti->res0e = -1; | |
| ti->version = minor << 16 | major; | |
| ti->res16 = 0xfffe0000; | |
| ti->misc.unknown1 = 0x02; | |
| ti->misc.flags = 0; /* FIXME */ | |
| ti->misc.unknown2 = 0x02; | |
| ti->misc.typekind = kind; | |
| ti->res1e = 0; | |
| if (hrefmap->href_count) | |
| { | |
| char name[64]; | |
| int i, hrefinfo_size; | |
| hrefinfo_size = sizeof(struct sltg_hrefinfo); | |
| for (i = 0; i < hrefmap->href_count; i++) | |
| { | |
| snprintf(name, sizeof(name), "*\\Rffff*#%x", hrefmap->href[i]); | |
| hrefinfo_size += 8 + 2 + strlen(name); | |
| } | |
| ti->href_offset = ti->member_offset; | |
| ti->member_offset += hrefinfo_size; | |
| } | |
| } | |
| static void write_hrefmap(struct sltg_data *data, const struct sltg_hrefmap *hrefmap) | |
| { | |
| struct sltg_hrefinfo hrefinfo; | |
| char name[64]; | |
| int i; | |
| if (!hrefmap->href_count) return; | |
| hrefinfo.magic = 0xdf; | |
| hrefinfo.res01 = 0; | |
| memset(hrefinfo.res02, 0xff, sizeof(hrefinfo.res02)); | |
| hrefinfo.number = hrefmap->href_count * 8; | |
| hrefinfo.res50 = -1; | |
| hrefinfo.res52 = 1; | |
| hrefinfo.res53 = 0; | |
| hrefinfo.resxx = 0xdf; | |
| append_data(data, &hrefinfo, offsetof(struct sltg_hrefinfo, res50)); | |
| for (i = 0; i < hrefmap->href_count; i++) | |
| append_data(data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8); | |
| append_data(data, &hrefinfo.res50, 7); | |
| for (i = 0; i < hrefmap->href_count; i++) | |
| { | |
| short len; | |
| snprintf(name, sizeof(name), "*\\Rffff*#%x", hrefmap->href[i]); | |
| len = strlen(name); | |
| append_data(data, &len, sizeof(len)); | |
| append_data(data, name, len); | |
| } | |
| append_data(data, &hrefinfo.resxx, sizeof(hrefinfo.resxx)); | |
| } | |
| static void dump_var_desc(const char *data, int size) | |
| { | |
| const unsigned char *p = (const unsigned char *)data; | |
| int i; | |
| if (!(debuglevel & (DEBUGLEVEL_TRACE | DEBUGLEVEL_CHAT))) return; | |
| chat("dump_var_desc: size %d bytes\n", size); | |
| for (i = 0; i < size; i++) | |
| fprintf(stderr, " %02x", *p++); | |
| fprintf(stderr, "\n"); | |
| } | |
| static int get_element_size(type_t *type) | |
| { | |
| int vt = get_type_vt(type); | |
| switch (vt) | |
| { | |
| case VT_I1: | |
| case VT_UI1: | |
| return 1; | |
| case VT_UI2: | |
| case VT_I2: | |
| case VT_BOOL: | |
| return 2; | |
| case VT_INT: | |
| case VT_UINT: | |
| case VT_I4: | |
| case VT_UI4: | |
| case VT_R4: | |
| case VT_ERROR: | |
| case VT_HRESULT: | |
| return 4; | |
| case VT_R8: | |
| case VT_I8: | |
| case VT_UI8: | |
| case VT_CY: | |
| case VT_DATE: | |
| return 8; | |
| case VT_DECIMAL: | |
| return 16; | |
| case VT_PTR: | |
| case VT_UNKNOWN: | |
| case VT_DISPATCH: | |
| case VT_BSTR: | |
| case VT_LPSTR: | |
| case VT_LPWSTR: | |
| return 4; | |
| case VT_VOID: | |
| return 0; | |
| case VT_VARIANT: | |
| return 16; | |
| case VT_USERDEFINED: | |
| return 0; | |
| default: | |
| error("get_element_size: unrecognized vt %d\n", vt); | |
| break; | |
| } | |
| return 0; | |
| } | |
| static int local_href(struct sltg_hrefmap *hrefmap, int typelib_href) | |
| { | |
| int i, href = -1; | |
| for (i = 0; i < hrefmap->href_count; i++) | |
| { | |
| if (hrefmap->href[i] == typelib_href) | |
| { | |
| href = i; | |
| break; | |
| } | |
| } | |
| if (href == -1) | |
| { | |
| href = hrefmap->href_count; | |
| if (hrefmap->href) | |
| hrefmap->href = xrealloc(hrefmap->href, sizeof(*hrefmap->href) * (hrefmap->href_count + 1)); | |
| else | |
| hrefmap->href = xmalloc(sizeof(*hrefmap->href)); | |
| hrefmap->href[hrefmap->href_count] = typelib_href; | |
| hrefmap->href_count++; | |
| } | |
| chat("typelib href %d mapped to local href %d\n", typelib_href, href); | |
| return href << 2; | |
| } | |
| static short write_var_desc(struct sltg_typelib *typelib, struct sltg_data *data, type_t *type, short param_flags, | |
| short flags, short base_offset, int *size_instance, struct sltg_hrefmap *hrefmap) | |
| { | |
| short vt, vt_flags, desc_offset; | |
| chat("write_var_desc: type %p, type->name %s\n", | |
| type, type->name ? type->name : "NULL"); | |
| if (is_array(type) && !type_array_is_decl_as_ptr(type)) | |
| { | |
| int num_dims, elements, array_start, size, array_size; | |
| type_t *atype; | |
| struct | |
| { | |
| short cDims; | |
| short fFeatures; | |
| int cbElements; | |
| int cLocks; | |
| int pvData; | |
| int bound[2]; | |
| } *array; | |
| int *bound; | |
| short vt_off[2]; | |
| elements = 1; | |
| num_dims = 0; | |
| atype = type; | |
| while (is_array(atype) && !type_array_is_decl_as_ptr(atype)) | |
| { | |
| num_dims++; | |
| elements *= type_array_get_dim(atype); | |
| atype = type_array_get_element_type(atype); | |
| } | |
| chat("write_var_desc: VT_CARRAY: %d dimensions, %d elements\n", num_dims, elements); | |
| array_start = data->size; | |
| size = sizeof(*array) + (num_dims - 1) * 8 /* sizeof(SAFEARRAYBOUND) */; | |
| array = xmalloc(size); | |
| array->cDims = num_dims; | |
| array->fFeatures = 0x0004; /* FADF_EMBEDDED */ | |
| array->cbElements = get_element_size(atype); | |
| array->cLocks = 0; | |
| array->pvData = 0; | |
| bound = array->bound; | |
| array_size = array->cbElements; | |
| atype = type; | |
| while (is_array(atype) && !type_array_is_decl_as_ptr(atype)) | |
| { | |
| bound[0] = type_array_get_dim(atype); | |
| array_size *= bound[0]; | |
| bound[1] = 0; | |
| bound += 2; | |
| atype = type_array_get_element_type(atype); | |
| } | |
| if (size_instance) | |
| { | |
| *size_instance += array_size; | |
| size_instance = NULL; /* don't account for element size */ | |
| } | |
| append_data(data, array, size); | |
| desc_offset = data->size; | |
| vt_off[0] = VT_CARRAY; | |
| vt_off[1] = array_start + base_offset; | |
| append_data(data, vt_off, sizeof(vt_off)); | |
| /* fall through to write array element description */ | |
| type = atype; | |
| } | |
| else | |
| desc_offset = data->size; | |
| vt = get_type_vt(type); | |
| if (vt == VT_PTR) | |
| { | |
| type_t *ref = is_ptr(type) ? type_pointer_get_ref_type(type) : type_array_get_element_type(type); | |
| if (is_ptr(ref)) | |
| { | |
| chat("write_var_desc: vt VT_PTR | 0x0400 | %04x\n", param_flags); | |
| vt = VT_PTR | 0x0400 | param_flags; | |
| append_data(data, &vt, sizeof(vt)); | |
| write_var_desc(typelib, data, ref, 0, 0, base_offset, size_instance, hrefmap); | |
| } | |
| else | |
| write_var_desc(typelib, data, ref, param_flags, 0x0e00, base_offset, size_instance, hrefmap); | |
| return desc_offset; | |
| } | |
| chat("write_var_desc: vt %d, flags %04x\n", vt, flags); | |
| vt_flags = vt | flags | param_flags; | |
| append_data(data, &vt_flags, sizeof(vt_flags)); | |
| if (vt == VT_USERDEFINED) | |
| { | |
| short href; | |
| while (type->typelib_idx < 0 && type_is_alias(type)) | |
| type = type_alias_get_aliasee_type(type); | |
| chat("write_var_desc: VT_USERDEFINED, type %p, name %s, real type %d, href %d\n", | |
| type, type->name, type_get_type(type), type->typelib_idx); | |
| if (type->typelib_idx == -1) | |
| { | |
| chat("write_var_desc: trying to ref not added type\n"); | |
| switch (type_get_type(type)) | |
| { | |
| case TYPE_STRUCT: | |
| add_structure_typeinfo(typelib, type); | |
| break; | |
| case TYPE_INTERFACE: | |
| add_interface_typeinfo(typelib, type); | |
| break; | |
| case TYPE_ENUM: | |
| add_enum_typeinfo(typelib, type); | |
| break; | |
| case TYPE_UNION: | |
| add_union_typeinfo(typelib, type); | |
| break; | |
| case TYPE_COCLASS: | |
| add_coclass_typeinfo(typelib, type); | |
| break; | |
| default: | |
| error("write_var_desc: VT_USERDEFINED - unhandled type %d\n", | |
| type_get_type(type)); | |
| } | |
| } | |
| if (type->typelib_idx == -1) | |
| error("write_var_desc: trying to ref not added type\n"); | |
| href = local_href(hrefmap, type->typelib_idx); | |
| chat("write_var_desc: VT_USERDEFINED, local href %d\n", href); | |
| append_data(data, &href, sizeof(href)); | |
| } | |
| if (size_instance) | |
| *size_instance += get_element_size(type); | |
| return desc_offset; | |
| } | |
| static void init_sltg_tail(struct sltg_tail *tail) | |
| { | |
| tail->cFuncs = 0; | |
| tail->cVars = 0; | |
| tail->cImplTypes = 0; | |
| tail->res06 = 0; | |
| tail->funcs_off = -1; | |
| tail->vars_off = -1; | |
| tail->impls_off = -1; | |
| tail->funcs_bytes = -1; | |
| tail->vars_bytes = -1; | |
| tail->impls_bytes = -1; | |
| tail->tdescalias_vt = -1; | |
| tail->res16 = -1; | |
| tail->res18 = 0; | |
| tail->res1a = 0; | |
| tail->simple_alias = 0; | |
| tail->res1e = 0; | |
| tail->cbSizeInstance = 0; | |
| tail->cbAlignment = 4; | |
| tail->res24 = -1; | |
| tail->res26 = -1; | |
| tail->cbSizeVft = 0; | |
| tail->res2a = -1; | |
| tail->res2c = -1; | |
| tail->res2e = -1; | |
| tail->res30 = -1; | |
| tail->res32 = 0; | |
| tail->type_bytes = 0; | |
| } | |
| static void add_structure_typeinfo(struct sltg_typelib *typelib, type_t *type) | |
| { | |
| struct sltg_data data, *var_data = NULL; | |
| struct sltg_hrefmap hrefmap; | |
| const char *index_name; | |
| struct sltg_typeinfo_header ti; | |
| struct sltg_member_header member; | |
| struct sltg_tail tail; | |
| int member_offset, var_count = 0, var_data_size = 0, size_instance = 0; | |
| short *type_desc_offset = NULL; | |
| if (type->typelib_idx != -1) return; | |
| chat("add_structure_typeinfo: type %p, type->name %s\n", type, type->name); | |
| type->typelib_idx = typelib->block_count; | |
| hrefmap.href_count = 0; | |
| hrefmap.href = NULL; | |
| if (type_struct_get_fields(type)) | |
| { | |
| int i = 0; | |
| var_t *var; | |
| var_count = list_count(type_struct_get_fields(type)); | |
| var_data = xmalloc(var_count * sizeof(*var_data)); | |
| type_desc_offset = xmalloc(var_count * sizeof(*type_desc_offset)); | |
| LIST_FOR_EACH_ENTRY(var, type_struct_get_fields(type), var_t, entry) | |
| { | |
| short base_offset; | |
| chat("add_structure_typeinfo: var %p (%s), type %p (%s)\n", | |
| var, var->name, var->declspec.type, var->declspec.type->name); | |
| init_sltg_data(&var_data[i]); | |
| base_offset = var_data_size + (i + 1) * sizeof(struct sltg_variable); | |
| type_desc_offset[i] = write_var_desc(typelib, &var_data[i], var->declspec.type, 0, 0, | |
| base_offset, &size_instance, &hrefmap); | |
| dump_var_desc(var_data[i].data, var_data[i].size); | |
| if (var_data[i].size > sizeof(short)) | |
| var_data_size += var_data[i].size; | |
| i++; | |
| } | |
| } | |
| init_sltg_data(&data); | |
| index_name = add_typeinfo_block(typelib, type, TKIND_RECORD); | |
| init_typeinfo(&ti, type, TKIND_RECORD, &hrefmap); | |
| append_data(&data, &ti, sizeof(ti)); | |
| write_hrefmap(&data, &hrefmap); | |
| member_offset = data.size; | |
| member.res00 = 0x0001; | |
| member.res02 = 0xffff; | |
| member.res04 = 0x01; | |
| member.extra = var_data_size + var_count * sizeof(struct sltg_variable); | |
| append_data(&data, &member, sizeof(member)); | |
| var_data_size = 0; | |
| if (type_struct_get_fields(type)) | |
| { | |
| int i = 0; | |
| short next = member_offset; | |
| var_t *var; | |
| LIST_FOR_EACH_ENTRY(var, type_struct_get_fields(type), var_t, entry) | |
| { | |
| struct sltg_variable variable; | |
| next += sizeof(variable); | |
| variable.magic = 0x2a; /* always write flags to simplify calculations */ | |
| variable.name = add_name(&typelib->name_table, var->name); | |
| variable.byte_offs = 0; | |
| if (var_data[i].size > sizeof(short)) | |
| { | |
| variable.flags = 0; | |
| var_data_size = next - member_offset + type_desc_offset[i]; | |
| variable.type = var_data_size; | |
| next += var_data[i].size; | |
| } | |
| else | |
| { | |
| variable.flags = 0x02; | |
| variable.type = *(short *)var_data[i].data; | |
| } | |
| variable.next = i < var_count - 1 ? next - member_offset : -1; | |
| variable.memid = 0x40000000 + i; | |
| variable.helpcontext = -2; /* 0xfffe */ | |
| variable.helpstring = -1; | |
| variable.varflags = 0; | |
| append_data(&data, &variable, sizeof(variable)); | |
| if (var_data[i].size > sizeof(short)) | |
| append_data(&data, var_data[i].data, var_data[i].size); | |
| i++; | |
| } | |
| } | |
| init_sltg_tail(&tail); | |
| tail.cVars = var_count; | |
| tail.vars_off = 0; | |
| tail.vars_bytes = var_data_size; | |
| tail.cbSizeInstance = size_instance; | |
| tail.type_bytes = data.size - member_offset - sizeof(member); | |
| append_data(&data, &tail, sizeof(tail)); | |
| add_block(typelib, data.data, data.size, index_name); | |
| } | |
| static importinfo_t *find_importinfo(typelib_t *typelib, const char *name) | |
| { | |
| importlib_t *importlib; | |
| LIST_FOR_EACH_ENTRY(importlib, &typelib->importlibs, importlib_t, entry) | |
| { | |
| int i; | |
| for (i = 0; i < importlib->ntypeinfos; i++) | |
| { | |
| if (!strcmp(name, importlib->importinfos[i].name)) | |
| { | |
| chat("Found %s in importlib list\n", name); | |
| return &importlib->importinfos[i]; | |
| } | |
| } | |
| } | |
| return NULL; | |
| } | |
| static int get_func_flags(const var_t *func, int *dispid, int *invokekind, int *helpcontext, const char **helpstring) | |
| { | |
| const attr_t *attr; | |
| int flags; | |
| *invokekind = 1 /* INVOKE_FUNC */; | |
| *helpcontext = -2; | |
| *helpstring = NULL; | |
| if (!func->attrs) return 0; | |
| flags = 0; | |
| LIST_FOR_EACH_ENTRY(attr, func->attrs, const attr_t, entry) | |
| { | |
| expr_t *expr = attr->u.pval; | |
| switch(attr->type) | |
| { | |
| case ATTR_BINDABLE: | |
| flags |= 0x4; /* FUNCFLAG_FBINDABLE */ | |
| break; | |
| case ATTR_DEFAULTBIND: | |
| flags |= 0x20; /* FUNCFLAG_FDEFAULTBIND */ | |
| break; | |
| case ATTR_DEFAULTCOLLELEM: | |
| flags |= 0x100; /* FUNCFLAG_FDEFAULTCOLLELEM */ | |
| break; | |
| case ATTR_DISPLAYBIND: | |
| flags |= 0x10; /* FUNCFLAG_FDISPLAYBIND */ | |
| break; | |
| case ATTR_HELPCONTEXT: | |
| *helpcontext = expr->u.integer.value; | |
| break; | |
| case ATTR_HELPSTRING: | |
| *helpstring = attr->u.pval; | |
| break; | |
| case ATTR_HIDDEN: | |
| flags |= 0x40; /* FUNCFLAG_FHIDDEN */ | |
| break; | |
| case ATTR_ID: | |
| *dispid = expr->cval; | |
| break; | |
| case ATTR_IMMEDIATEBIND: | |
| flags |= 0x1000; /* FUNCFLAG_FIMMEDIATEBIND */ | |
| break; | |
| case ATTR_NONBROWSABLE: | |
| flags |= 0x400; /* FUNCFLAG_FNONBROWSABLE */ | |
| break; | |
| case ATTR_PROPGET: | |
| *invokekind = 0x2; /* INVOKE_PROPERTYGET */ | |
| break; | |
| case ATTR_PROPPUT: | |
| *invokekind = 0x4; /* INVOKE_PROPERTYPUT */ | |
| break; | |
| case ATTR_PROPPUTREF: | |
| *invokekind = 0x8; /* INVOKE_PROPERTYPUTREF */ | |
| break; | |
| /* FIXME: FUNCFLAG_FREPLACEABLE */ | |
| case ATTR_REQUESTEDIT: | |
| flags |= 0x8; /* FUNCFLAG_FREQUESTEDIT */ | |
| break; | |
| case ATTR_RESTRICTED: | |
| flags |= 0x1; /* FUNCFLAG_FRESTRICTED */ | |
| break; | |
| case ATTR_SOURCE: | |
| flags |= 0x2; /* FUNCFLAG_FSOURCE */ | |
| break; | |
| case ATTR_UIDEFAULT: | |
| flags |= 0x200; /* FUNCFLAG_FUIDEFAULT */ | |
| break; | |
| case ATTR_USESGETLASTERROR: | |
| flags |= 0x80; /* FUNCFLAG_FUSESGETLASTERROR */ | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| return flags; | |
| } | |
| static int get_param_flags(const var_t *param) | |
| { | |
| const attr_t *attr; | |
| int flags, in, out; | |
| if (!param->attrs) return 0; | |
| flags = 0; | |
| in = out = 0; | |
| LIST_FOR_EACH_ENTRY(attr, param->attrs, const attr_t, entry) | |
| { | |
| switch(attr->type) | |
| { | |
| case ATTR_IN: | |
| in++; | |
| break; | |
| case ATTR_OUT: | |
| out++; | |
| break; | |
| case ATTR_PARAMLCID: | |
| flags |= 0x2000; | |
| break; | |
| case ATTR_RETVAL: | |
| flags |= 0x80; | |
| break; | |
| default: | |
| chat("unhandled param attr %d\n", attr->type); | |
| break; | |
| } | |
| } | |
| if (out) | |
| flags |= in ? 0x8000 : 0x4000; | |
| else if (!in) | |
| flags |= 0xc000; | |
| return flags; | |
| } | |
| static int add_func_desc(struct sltg_typelib *typelib, struct sltg_data *data, var_t *func, | |
| int idx, int dispid, short base_offset, struct sltg_hrefmap *hrefmap) | |
| { | |
| struct sltg_data ret_data, *arg_data; | |
| int arg_count = 0, arg_data_size, optional = 0, old_size; | |
| int funcflags = 0, invokekind = 1 /* INVOKE_FUNC */, helpcontext; | |
| const char *helpstring; | |
| const var_t *arg; | |
| short ret_desc_offset, *arg_desc_offset, arg_offset; | |
| struct sltg_function func_desc; | |
| chat("add_func_desc: %s, idx %#x, dispid %#x\n", func->name, idx, dispid); | |
| old_size = data->size; | |
| init_sltg_data(&ret_data); | |
| ret_desc_offset = write_var_desc(typelib, &ret_data, type_function_get_rettype(func->declspec.type), | |
| 0, 0, base_offset, NULL, hrefmap); | |
| dump_var_desc(ret_data.data, ret_data.size); | |
| arg_data_size = 0; | |
| arg_offset = base_offset + sizeof(struct sltg_function); | |
| if (ret_data.size > sizeof(short)) | |
| { | |
| arg_data_size += ret_data.size; | |
| arg_offset += ret_data.size; | |
| } | |
| if (type_function_get_args(func->declspec.type)) | |
| { | |
| int i = 0; | |
| arg_count = list_count(type_function_get_args(func->declspec.type)); | |
| arg_data = xmalloc(arg_count * sizeof(*arg_data)); | |
| arg_desc_offset = xmalloc(arg_count * sizeof(*arg_desc_offset)); | |
| arg_offset += arg_count * 2 * sizeof(short); | |
| LIST_FOR_EACH_ENTRY(arg, type_function_get_args(func->declspec.type), const var_t, entry) | |
| { | |
| const attr_t *attr; | |
| short param_flags = get_param_flags(arg); | |
| chat("add_func_desc: arg[%d] %p (%s), type %p (%s)\n", | |
| i, arg, arg->name, arg->declspec.type, arg->declspec.type->name); | |
| init_sltg_data(&arg_data[i]); | |
| arg_desc_offset[i] = write_var_desc(typelib, &arg_data[i], arg->declspec.type, param_flags, 0, | |
| arg_offset, NULL, hrefmap); | |
| dump_var_desc(arg_data[i].data, arg_data[i].size); | |
| if (arg_data[i].size > sizeof(short)) | |
| { | |
| arg_data_size += arg_data[i].size; | |
| arg_offset += arg_data[i].size; | |
| } | |
| i++; | |
| if (!arg->attrs) continue; | |
| LIST_FOR_EACH_ENTRY(attr, arg->attrs, const attr_t, entry) | |
| { | |
| if (attr->type == ATTR_OPTIONAL) | |
| optional++; | |
| } | |
| } | |
| } | |
| funcflags = get_func_flags(func, &dispid, &invokekind, &helpcontext, &helpstring); | |
| if (base_offset != -1) | |
| chat("add_func_desc: flags %#x, dispid %#x, invokekind %d, helpcontext %#x, helpstring %s\n", | |
| funcflags, dispid, invokekind, helpcontext, helpstring); | |
| func_desc.magic = 0x6c; /* always write flags to simplify calculations */ | |
| func_desc.flags = (invokekind << 4) | 0x02; | |
| if (idx & 0x80000000) | |
| { | |
| func_desc.next = -1; | |
| idx &= ~0x80000000; | |
| } | |
| else | |
| func_desc.next = base_offset + sizeof(func_desc) + arg_data_size + arg_count * 2 * sizeof(short); | |
| func_desc.name = base_offset != -1 ? add_name(&typelib->name_table, func->name) : -1; | |
| func_desc.dispid = dispid; | |
| func_desc.helpcontext = helpcontext; | |
| func_desc.helpstring = (helpstring && base_offset != -1) ? add_name(&typelib->name_table, helpstring) : -1; | |
| func_desc.arg_off = arg_count ? base_offset + sizeof(func_desc) : -1; | |
| func_desc.nacc = (arg_count << 3) | 4 /* CC_STDCALL */; | |
| func_desc.retnextopt = (optional << 1); | |
| if (ret_data.size > sizeof(short)) | |
| { | |
| func_desc.rettype = base_offset + sizeof(func_desc) + ret_desc_offset; | |
| if (arg_count) | |
| func_desc.arg_off += ret_data.size; | |
| } | |
| else | |
| { | |
| func_desc.retnextopt |= 0x80; | |
| func_desc.rettype = *(short *)ret_data.data; | |
| } | |
| func_desc.vtblpos = idx * pointer_size; | |
| func_desc.funcflags = funcflags; | |
| append_data(data, &func_desc, sizeof(func_desc)); | |
| arg_offset = base_offset + sizeof(struct sltg_function); | |
| if (ret_data.size > sizeof(short)) | |
| { | |
| append_data(data, ret_data.data, ret_data.size); | |
| func_desc.arg_off += ret_data.size; | |
| arg_offset += ret_data.size; | |
| } | |
| if (arg_count) | |
| { | |
| int i = 0; | |
| arg_offset += arg_count * 2 * sizeof(short); | |
| LIST_FOR_EACH_ENTRY(arg, type_function_get_args(func->declspec.type), const var_t, entry) | |
| { | |
| short name, type_offset; | |
| name = base_offset != -1 ? add_name(&typelib->name_table, arg->name) : -1; | |
| if (arg_data[i].size > sizeof(short)) | |
| { | |
| type_offset = (arg_offset + arg_desc_offset[i]); | |
| arg_offset += arg_data[i].size; | |
| } | |
| else | |
| { | |
| name |= 1; | |
| type_offset = *(short *)arg_data[i].data; | |
| } | |
| append_data(data, &name, sizeof(name)); | |
| append_data(data, &type_offset, sizeof(type_offset)); | |
| if (base_offset != -1) | |
| chat("add_func_desc: arg[%d] - name %s (%#x), type_offset %#x\n", | |
| i, arg->name, name, type_offset); | |
| i++; | |
| } | |
| for (i = 0; i < arg_count; i++) | |
| { | |
| if (arg_data[i].size > sizeof(short)) | |
| append_data(data, arg_data[i].data, arg_data[i].size); | |
| } | |
| } | |
| return data->size - old_size; | |
| } | |
| static void write_impl_href(struct sltg_data *data, short href) | |
| { | |
| struct sltg_impl_info impl_info; | |
| impl_info.res00 = 0x004a; | |
| impl_info.next = -1; | |
| impl_info.res04 = -1; | |
| impl_info.impltypeflags = 0; | |
| impl_info.res07 = 0x80; | |
| impl_info.res08 = 0x0012; | |
| impl_info.ref = href; | |
| impl_info.res0c = 0x4001; | |
| impl_info.res0e = -2; /* 0xfffe */ | |
| impl_info.res10 = -1; | |
| impl_info.res12 = 0x001d; | |
| impl_info.pos = 0; | |
| append_data(data, &impl_info, sizeof(impl_info)); | |
| } | |
| static void add_interface_typeinfo(struct sltg_typelib *typelib, type_t *iface) | |
| { | |
| const statement_t *stmt_func; | |
| importinfo_t *ref_importinfo = NULL; | |
| short inherit_href = -1; | |
| struct sltg_data data; | |
| struct sltg_hrefmap hrefmap; | |
| const char *index_name; | |
| struct sltg_typeinfo_header ti; | |
| struct sltg_member_header member; | |
| struct sltg_tail tail; | |
| int member_offset, base_offset, func_data_size, i; | |
| int func_count, inherited_func_count = 0; | |
| int dispid, inherit_level = 0; | |
| if (iface->typelib_idx != -1) return; | |
| chat("add_interface_typeinfo: type %p, type->name %s\n", iface, iface->name); | |
| if (!iface->details.iface) | |
| { | |
| error("interface %s is referenced but not defined\n", iface->name); | |
| return; | |
| } | |
| if (is_attr(iface->attrs, ATTR_DISPINTERFACE)) | |
| { | |
| error("support for dispinterface %s is not implemented\n", iface->name); | |
| return; | |
| } | |
| hrefmap.href_count = 0; | |
| hrefmap.href = NULL; | |
| if (type_iface_get_inherit(iface)) | |
| { | |
| type_t *inherit; | |
| inherit = type_iface_get_inherit(iface); | |
| chat("add_interface_typeinfo: inheriting from base interface %s\n", inherit->name); | |
| ref_importinfo = find_importinfo(typelib->typelib, inherit->name); | |
| if (!ref_importinfo && type_iface_get_inherit(inherit)) | |
| add_interface_typeinfo(typelib, inherit); | |
| if (ref_importinfo) | |
| error("support for imported interfaces is not implemented\n"); | |
| inherit_href = local_href(&hrefmap, inherit->typelib_idx); | |
| while (inherit) | |
| { | |
| inherit_level++; | |
| inherited_func_count += list_count(type_iface_get_stmts(inherit)); | |
| inherit = type_iface_get_inherit(inherit); | |
| } | |
| } | |
| /* check typelib_idx again, it could have been added while resolving the parent interface */ | |
| if (iface->typelib_idx != -1) return; | |
| iface->typelib_idx = typelib->block_count; | |
| /* pass 1: calculate function descriptions data size */ | |
| init_sltg_data(&data); | |
| STATEMENTS_FOR_EACH_FUNC(stmt_func, type_iface_get_stmts(iface)) | |
| { | |
| add_func_desc(typelib, &data, stmt_func->u.var, -1, -1, -1, &hrefmap); | |
| } | |
| func_data_size = data.size; | |
| /* pass 2: write function descriptions */ | |
| init_sltg_data(&data); | |
| func_count = list_count(type_iface_get_stmts(iface)); | |
| index_name = add_typeinfo_block(typelib, iface, TKIND_INTERFACE); | |
| init_typeinfo(&ti, iface, TKIND_INTERFACE, &hrefmap); | |
| append_data(&data, &ti, sizeof(ti)); | |
| write_hrefmap(&data, &hrefmap); | |
| member_offset = data.size; | |
| base_offset = 0; | |
| member.res00 = 0x0001; | |
| member.res02 = 0xffff; | |
| member.res04 = 0x01; | |
| member.extra = func_data_size; | |
| if (inherit_href != -1) | |
| { | |
| member.extra += sizeof(struct sltg_impl_info); | |
| base_offset += sizeof(struct sltg_impl_info); | |
| } | |
| append_data(&data, &member, sizeof(member)); | |
| if (inherit_href != -1) | |
| write_impl_href(&data, inherit_href); | |
| i = 0; | |
| dispid = 0x60000000 | (inherit_level << 16); | |
| STATEMENTS_FOR_EACH_FUNC(stmt_func, type_iface_get_stmts(iface)) | |
| { | |
| int idx = inherited_func_count + i; | |
| if (i == func_count - 1) idx |= 0x80000000; | |
| base_offset += add_func_desc(typelib, &data, stmt_func->u.var, | |
| idx, dispid + i, base_offset, &hrefmap); | |
| i++; | |
| } | |
| init_sltg_tail(&tail); | |
| tail.cFuncs = func_count; | |
| tail.funcs_off = 0; | |
| tail.funcs_bytes = func_data_size; | |
| tail.cbSizeInstance = pointer_size; | |
| tail.cbAlignment = pointer_size; | |
| tail.cbSizeVft = (inherited_func_count + func_count) * pointer_size; | |
| tail.type_bytes = data.size - member_offset - sizeof(member); | |
| tail.res24 = 0; | |
| tail.res26 = 0; | |
| if (inherit_href != -1) | |
| { | |
| tail.cImplTypes++; | |
| tail.impls_off = 0; | |
| tail.impls_bytes = 0; | |
| tail.funcs_off += sizeof(struct sltg_impl_info); | |
| } | |
| append_data(&data, &tail, sizeof(tail)); | |
| add_block(typelib, data.data, data.size, index_name); | |
| } | |
| static void add_enum_typeinfo(struct sltg_typelib *typelib, type_t *type) | |
| { | |
| struct sltg_data data, *var_data = NULL; | |
| struct sltg_hrefmap hrefmap; | |
| const char *index_name; | |
| struct sltg_typeinfo_header ti; | |
| struct sltg_member_header member; | |
| struct sltg_tail tail; | |
| int member_offset, var_count = 0; | |
| short *type_desc_offset = NULL; | |
| if (type->typelib_idx != -1) return; | |
| chat("add_enum_typeinfo: type %p, type->name %s\n", type, type->name); | |
| type->typelib_idx = typelib->block_count; | |
| hrefmap.href_count = 0; | |
| hrefmap.href = NULL; | |
| if (type_enum_get_values(type)) | |
| { | |
| int i = 0; | |
| var_t *var; | |
| var_count = list_count(type_enum_get_values(type)); | |
| var_data = xmalloc(var_count * sizeof(*var_data)); | |
| type_desc_offset = xmalloc(var_count * sizeof(*type_desc_offset)); | |
| LIST_FOR_EACH_ENTRY(var, type_enum_get_values(type), var_t, entry) | |
| { | |
| short base_offset; | |
| chat("add_enum_typeinfo: var %p (%s), type %p (%s)\n", | |
| var, var->name, var->declspec.type, var->declspec.type->name); | |
| init_sltg_data(&var_data[i]); | |
| base_offset = (i + 1) * (sizeof(struct sltg_variable) + sizeof(int)); | |
| type_desc_offset[i] = write_var_desc(typelib, &var_data[i], var->declspec.type, 0, 0, | |
| base_offset, NULL, &hrefmap); | |
| dump_var_desc(var_data[i].data, var_data[i].size); | |
| i++; | |
| } | |
| } | |
| init_sltg_data(&data); | |
| index_name = add_typeinfo_block(typelib, type, TKIND_ENUM); | |
| init_typeinfo(&ti, type, TKIND_ENUM, &hrefmap); | |
| append_data(&data, &ti, sizeof(ti)); | |
| write_hrefmap(&data, &hrefmap); | |
| member_offset = data.size; | |
| member.res00 = 0x0001; | |
| member.res02 = 0xffff; | |
| member.res04 = 0x01; | |
| member.extra = var_count * (sizeof(struct sltg_variable) + sizeof(int)); | |
| append_data(&data, &member, sizeof(member)); | |
| if (type_enum_get_values(type)) | |
| { | |
| int i = 0; | |
| short next = member_offset; | |
| var_t *var; | |
| LIST_FOR_EACH_ENTRY(var, type_enum_get_values(type), var_t, entry) | |
| { | |
| struct sltg_variable variable; | |
| next += sizeof(variable); | |
| variable.magic = 0x2a; /* always write flags to simplify calculations */ | |
| variable.name = add_name(&typelib->name_table, var->name); | |
| variable.byte_offs = next - member_offset; | |
| variable.flags = 0x12; | |
| variable.type = VT_INT; | |
| variable.next = i < var_count - 1 ? variable.byte_offs + sizeof(int) : -1; | |
| variable.memid = 0x40000000 + i; | |
| variable.helpcontext = -2; /* 0xfffe */ | |
| variable.helpstring = -1; | |
| variable.varflags = 0; | |
| append_data(&data, &variable, sizeof(variable)); | |
| append_data(&data, &var->eval->cval, sizeof(var->eval->cval)); | |
| next += sizeof(int); | |
| i++; | |
| } | |
| } | |
| init_sltg_tail(&tail); | |
| tail.cVars = var_count; | |
| tail.vars_off = 0; | |
| tail.vars_bytes = 0; | |
| tail.cbSizeInstance = sizeof(int); | |
| tail.type_bytes = data.size - member_offset - sizeof(member); | |
| append_data(&data, &tail, sizeof(tail)); | |
| add_block(typelib, data.data, data.size, index_name); | |
| } | |
| static void add_union_typeinfo(struct sltg_typelib *typelib, type_t *type) | |
| { | |
| struct sltg_data data, *var_data = NULL; | |
| struct sltg_hrefmap hrefmap; | |
| const char *index_name; | |
| struct sltg_typeinfo_header ti; | |
| struct sltg_member_header member; | |
| struct sltg_tail tail; | |
| int member_offset, var_count = 0, var_data_size = 0, size_instance = 0; | |
| short *type_desc_offset = NULL; | |
| if (type->typelib_idx != -1) return; | |
| chat("add_union_typeinfo: type %p, type->name %s\n", type, type->name); | |
| type->typelib_idx = typelib->block_count; | |
| hrefmap.href_count = 0; | |
| hrefmap.href = NULL; | |
| if (type_union_get_cases(type)) | |
| { | |
| int i = 0; | |
| var_t *var; | |
| var_count = list_count(type_union_get_cases(type)); | |
| var_data = xmalloc(var_count * sizeof(*var_data)); | |
| type_desc_offset = xmalloc(var_count * sizeof(*type_desc_offset)); | |
| LIST_FOR_EACH_ENTRY(var, type_union_get_cases(type), var_t, entry) | |
| { | |
| short base_offset; | |
| chat("add_union_typeinfo: var %p (%s), type %p (%s)\n", | |
| var, var->name, var->declspec.type, var->declspec.type->name); | |
| init_sltg_data(&var_data[i]); | |
| base_offset = var_data_size + (i + 1) * sizeof(struct sltg_variable); | |
| type_desc_offset[i] = write_var_desc(typelib, &var_data[i], var->declspec.type, 0, 0, | |
| base_offset, &size_instance, &hrefmap); | |
| dump_var_desc(var_data[i].data, var_data[i].size); | |
| if (var_data[i].size > sizeof(short)) | |
| var_data_size += var_data[i].size; | |
| i++; | |
| } | |
| } | |
| init_sltg_data(&data); | |
| index_name = add_typeinfo_block(typelib, type, TKIND_UNION); | |
| init_typeinfo(&ti, type, TKIND_UNION, &hrefmap); | |
| append_data(&data, &ti, sizeof(ti)); | |
| write_hrefmap(&data, &hrefmap); | |
| member_offset = data.size; | |
| member.res00 = 0x0001; | |
| member.res02 = 0xffff; | |
| member.res04 = 0x01; | |
| member.extra = var_data_size + var_count * sizeof(struct sltg_variable); | |
| append_data(&data, &member, sizeof(member)); | |
| if (type_union_get_cases(type)) | |
| { | |
| int i = 0; | |
| short next = member_offset; | |
| var_t *var; | |
| LIST_FOR_EACH_ENTRY(var, type_union_get_cases(type), var_t, entry) | |
| { | |
| struct sltg_variable variable; | |
| next += sizeof(variable); | |
| variable.magic = 0x2a; /* always write flags to simplify calculations */ | |
| variable.name = add_name(&typelib->name_table, var->name); | |
| variable.byte_offs = 0; | |
| if (var_data[i].size > sizeof(short)) | |
| { | |
| variable.flags = 0; | |
| variable.type = next - member_offset + type_desc_offset[i]; | |
| next += var_data[i].size; | |
| } | |
| else | |
| { | |
| variable.flags = 0x02; | |
| variable.type = *(short *)var_data[i].data; | |
| } | |
| variable.next = i < var_count - 1 ? next - member_offset : -1; | |
| variable.memid = 0x40000000 + i; | |
| variable.helpcontext = -2; /* 0xfffe */ | |
| variable.helpstring = -1; | |
| variable.varflags = 0; | |
| append_data(&data, &variable, sizeof(variable)); | |
| if (var_data[i].size > sizeof(short)) | |
| append_data(&data, var_data[i].data, var_data[i].size); | |
| i++; | |
| } | |
| } | |
| init_sltg_tail(&tail); | |
| tail.cVars = var_count; | |
| tail.vars_off = 0; | |
| tail.vars_bytes = 0; | |
| tail.cbSizeInstance = size_instance; | |
| tail.type_bytes = data.size - member_offset - sizeof(member); | |
| append_data(&data, &tail, sizeof(tail)); | |
| add_block(typelib, data.data, data.size, index_name); | |
| } | |
| static void add_coclass_typeinfo(struct sltg_typelib *typelib, type_t *type) | |
| { | |
| error("add_coclass_typeinfo: %s not implemented\n", type->name); | |
| } | |
| static void add_type_typeinfo(struct sltg_typelib *typelib, type_t *type) | |
| { | |
| chat("add_type_typeinfo: adding %s, type %d\n", type->name, type_get_type(type)); | |
| switch (type_get_type(type)) | |
| { | |
| case TYPE_INTERFACE: | |
| add_interface_typeinfo(typelib, type); | |
| break; | |
| case TYPE_STRUCT: | |
| add_structure_typeinfo(typelib, type); | |
| break; | |
| case TYPE_ENUM: | |
| add_enum_typeinfo(typelib, type); | |
| break; | |
| case TYPE_UNION: | |
| add_union_typeinfo(typelib, type); | |
| break; | |
| case TYPE_COCLASS: | |
| add_coclass_typeinfo(typelib, type); | |
| break; | |
| case TYPE_BASIC: | |
| case TYPE_POINTER: | |
| break; | |
| default: | |
| error("add_type_typeinfo: unhandled type %d for %s\n", type_get_type(type), type->name); | |
| break; | |
| } | |
| } | |
| static void add_statement(struct sltg_typelib *typelib, const statement_t *stmt) | |
| { | |
| switch(stmt->type) | |
| { | |
| case STMT_LIBRARY: | |
| case STMT_IMPORT: | |
| case STMT_PRAGMA: | |
| case STMT_CPPQUOTE: | |
| case STMT_DECLARATION: | |
| /* not included in typelib */ | |
| break; | |
| case STMT_IMPORTLIB: | |
| /* not processed here */ | |
| break; | |
| case STMT_TYPEDEF: | |
| { | |
| typeref_t *ref; | |
| if (!stmt->u.type_list) | |
| break; | |
| LIST_FOR_EACH_ENTRY(ref, stmt->u.type_list, typeref_t, entry) | |
| { | |
| /* in old style typelibs all types are public */ | |
| add_type_typeinfo(typelib, ref->type); | |
| } | |
| break; | |
| } | |
| case STMT_MODULE: | |
| add_module_typeinfo(typelib, stmt->u.type); | |
| break; | |
| case STMT_TYPE: | |
| case STMT_TYPEREF: | |
| { | |
| type_t *type = stmt->u.type; | |
| add_type_typeinfo(typelib, type); | |
| break; | |
| } | |
| default: | |
| error("add_statement: unhandled statement type %d\n", stmt->type); | |
| break; | |
| } | |
| } | |
| static void sltg_write_header(struct sltg_typelib *sltg, int *library_block_start) | |
| { | |
| char pad[0x40]; | |
| struct sltg_header | |
| { | |
| int magic; | |
| short block_count; | |
| short res06; | |
| short size_of_index; | |
| short first_blk; | |
| GUID uuid; | |
| int res1c; | |
| int res20; | |
| } header; | |
| struct sltg_block_entry | |
| { | |
| int length; | |
| short index_string; | |
| short next; | |
| } entry; | |
| int i; | |
| header.magic = 0x47544c53; | |
| header.block_count = sltg->block_count + 1; /* 1-based */ | |
| header.res06 = 9; | |
| header.size_of_index = sltg->index.size; | |
| header.first_blk = 1; /* 1-based */ | |
| header.uuid = sltg_library_guid; | |
| header.res1c = 0x00000044; | |
| header.res20 = 0xffff0000; | |
| put_data(&header, sizeof(header)); | |
| /* library block is written separately */ | |
| for (i = 0; i < sltg->block_count - 1; i++) | |
| { | |
| entry.length = sltg->blocks[i].length; | |
| entry.index_string = sltg->blocks[i].index_string; | |
| entry.next = header.first_blk + i + 1; /* point to next block */ | |
| chat("sltg_write_header: writing block entry %d: length %#x, index_string %#x, next %#x\n", | |
| i, entry.length, entry.index_string, entry.next); | |
| put_data(&entry, sizeof(entry)); | |
| } | |
| /* library block length includes helpstrings and name table */ | |
| entry.length = sltg->blocks[sltg->block_count - 1].length + 0x40 /* pad after library block */ + | |
| sizeof(sltg->typeinfo_count) + sltg->typeinfo_size + 4 /* library block size */ + 6 /* dummy help strings */ + | |
| 12 /* name table header */ + 0x200 /* name table hash */ + | |
| sizeof(sltg->name_table.size) + sltg->name_table.size + | |
| 4 /* 0x01ffff01 */ + 4 /* 0 */; | |
| entry.index_string = sltg->blocks[sltg->block_count - 1].index_string; | |
| entry.next = 0; | |
| chat("sltg_write_header: writing library block entry %d: length %#x, index_string %#x, next %#x\n", | |
| i, entry.length, entry.index_string, entry.next); | |
| put_data(&entry, sizeof(entry)); | |
| chat("sltg_write_header: writing index: %d bytes\n", sltg->index.size); | |
| put_data(sltg->index.data, sltg->index.size); | |
| memset(pad, 0, 9); | |
| put_data(pad, 9); | |
| /* library block is written separately */ | |
| for (i = 0; i < sltg->block_count - 1; i++) | |
| { | |
| chat("sltg_write_header: writing block %d: %d bytes\n", i, sltg->blocks[i].length); | |
| put_data(sltg->blocks[i].data, sltg->blocks[i].length); | |
| } | |
| /* library block */ | |
| chat("library_block_start = %#x\n", (int)output_buffer_pos); | |
| *library_block_start = output_buffer_pos; | |
| chat("sltg_write_header: writing library block %d: %d bytes\n", i, sltg->blocks[i].length); | |
| put_data(sltg->blocks[sltg->block_count - 1].data, sltg->blocks[sltg->block_count - 1].length); | |
| chat("sltg_write_header: writing pad 0x40 bytes\n"); | |
| memset(pad, 0xff, 0x40); | |
| put_data(pad, 0x40); | |
| } | |
| static void sltg_write_typeinfo(struct sltg_typelib *typelib) | |
| { | |
| short i; | |
| put_data(&typelib->typeinfo_count, sizeof(typelib->typeinfo_count)); | |
| for (i = 0; i < typelib->typeinfo_count; i++) | |
| { | |
| chat("sltg_write_typeinfo: writing block %d: %d bytes\n", i, typelib->typeinfo[i].length); | |
| put_data(typelib->typeinfo[i].data, typelib->typeinfo[i].length); | |
| } | |
| } | |
| static void sltg_write_helpstrings(struct sltg_typelib *typelib) | |
| { | |
| static const char dummy[6]; | |
| chat("sltg_write_helpstrings: writing dummy 6 bytes\n"); | |
| put_data(dummy, sizeof(dummy)); | |
| } | |
| static void sltg_write_nametable(struct sltg_typelib *typelib) | |
| { | |
| static const short dummy[6] = { 0xffff,1,2,0xff00,0xffff,0xffff }; | |
| char pad[0x200]; | |
| chat("sltg_write_nametable: writing 12+0x200+%d bytes\n", typelib->name_table.size); | |
| put_data(dummy, sizeof(dummy)); | |
| memset(pad, 0xff, 0x200); | |
| put_data(pad, 0x200); | |
| put_data(&typelib->name_table.size, sizeof(typelib->name_table.size)); | |
| put_data(typelib->name_table.data, typelib->name_table.size); | |
| } | |
| static void sltg_write_remainder(void) | |
| { | |
| static const short dummy1[] = { 1,0xfffe,0x0a03,0,0xffff,0xffff }; | |
| static const short dummy2[] = { 0xffff,0xffff,0x0200,0,0,0 }; | |
| static const char dummy3[] = { 0xf4,0x39,0xb2,0x71,0,0,0,0,0,0,0,0,0,0,0,0 }; | |
| static const char TYPELIB[] = { 8,0,0,0,'T','Y','P','E','L','I','B',0 }; | |
| int pad; | |
| pad = 0x01ffff01; | |
| put_data(&pad, sizeof(pad)); | |
| pad = 0; | |
| put_data(&pad, sizeof(pad)); | |
| put_data(dummy1, sizeof(dummy1)); | |
| put_data(&sltg_library_guid, sizeof(sltg_library_guid)); | |
| put_data(TYPELIB, sizeof(TYPELIB)); | |
| put_data(dummy2, sizeof(dummy2)); | |
| put_data(dummy3, sizeof(dummy3)); | |
| } | |
| static void save_all_changes(struct sltg_typelib *typelib) | |
| { | |
| int library_block_start; | |
| int *name_table_offset; | |
| sltg_write_header(typelib, &library_block_start); | |
| sltg_write_typeinfo(typelib); | |
| name_table_offset = (int *)(output_buffer + output_buffer_pos); | |
| chat("name_table_offset = %#x\n", (int)output_buffer_pos); | |
| put_data(&library_block_start, sizeof(library_block_start)); | |
| sltg_write_helpstrings(typelib); | |
| *name_table_offset = output_buffer_pos - library_block_start; | |
| chat("*name_table_offset = %#x\n", *name_table_offset); | |
| sltg_write_nametable(typelib); | |
| sltg_write_remainder(); | |
| if (strendswith(typelib_name, ".res")) /* create a binary resource file */ | |
| { | |
| char typelib_id[13] = "#1"; | |
| expr_t *expr = get_attrp(typelib->typelib->attrs, ATTR_ID); | |
| if (expr) | |
| snprintf(typelib_id, sizeof(typelib_id), "#%d", expr->cval); | |
| add_output_to_resources("TYPELIB", typelib_id); | |
| if (strendswith(typelib_name, "_t.res")) /* add typelib registration */ | |
| output_typelib_regscript(typelib->typelib); | |
| } | |
| else flush_output_buffer(typelib_name); | |
| } | |
| int create_sltg_typelib(typelib_t *typelib) | |
| { | |
| struct sltg_typelib sltg; | |
| const statement_t *stmt; | |
| void *library_block; | |
| int library_block_size, library_block_index; | |
| if (pointer_size != 4) | |
| error("Only 32-bit platform is supported\n"); | |
| sltg.typelib = typelib; | |
| sltg.typeinfo_count = 0; | |
| sltg.typeinfo_size = 0; | |
| sltg.typeinfo = NULL; | |
| sltg.blocks = NULL; | |
| sltg.block_count = 0; | |
| sltg.first_block = 1; | |
| init_index(&sltg.index); | |
| init_name_table(&sltg.name_table); | |
| init_library(&sltg); | |
| library_block = create_library_block(&sltg, &library_block_size, &library_block_index); | |
| if (typelib->stmts) | |
| LIST_FOR_EACH_ENTRY(stmt, typelib->stmts, const statement_t, entry) | |
| add_statement(&sltg, stmt); | |
| add_block_index(&sltg, library_block, library_block_size, library_block_index); | |
| save_all_changes(&sltg); | |
| return 1; | |
| } |