From 8cfc4d7d949ff0f48e9fc9b64c953c57b7d91ca2 Mon Sep 17 00:00:00 2001
From: Dmitry Timoshkov <dmitry@baikal.ru>
Date: Thu, 7 Jan 2016 17:38:17 +0800
Subject: [PATCH] widl: Add support for structures.

---
 tools/widl/write_sltg.c | 584 +++++++++++++++++++++++++++++++++++++---
 1 file changed, 551 insertions(+), 33 deletions(-)

diff --git a/tools/widl/write_sltg.c b/tools/widl/write_sltg.c
index c4ddaed9441..a03e9688bd2 100644
--- a/tools/widl/write_sltg.c
+++ b/tools/widl/write_sltg.c
@@ -1,7 +1,7 @@
 /*
  * Typelib (SLTG) generation
  *
- * Copyright 2015 Dmitry Timoshkov
+ * 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
@@ -42,16 +42,10 @@
 
 static const GUID sltg_library_guid = { 0x204ff,0,0,{ 0xc0,0,0,0,0,0,0,0x46 } };
 
-struct sltg_index
+struct sltg_data
 {
     int size, allocated;
-    char *names;
-};
-
-struct sltg_name_table
-{
-    int size, allocated;
-    char *names;
+    char *data;
 };
 
 struct sltg_library
@@ -78,69 +72,152 @@ struct sltg_block
 struct sltg_typelib
 {
     typelib_t *typelib;
-    struct sltg_index index;
-    struct sltg_name_table name_table;
+    struct sltg_data index;
+    struct sltg_data name_table;
     struct sltg_library library;
     struct sltg_block *blocks;
     int n_file_blocks;
     int first_block;
     int typeinfo_count;
+    int typeinfo_size;
+    struct sltg_block *typeinfo;
 };
 
-static int add_index(struct sltg_index *index, const char *name)
+#include "pshpack1.h"
+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 & 0x02 */
+};
+
+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; /* always ffff */
+    short res26; /* always ffff */
+    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 */
+};
+#include "poppack.h"
+
+static void init_sltg_data(struct sltg_data *data)
+{
+    data->size = 0;
+    data->allocated = 0x10;
+    data->data = xmalloc(0x10);
+}
+
+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 = max(index->allocated * 2, new_size);
-        index->names = xrealloc(index->names, index->allocated);
+        index->data = xrealloc(index->data, index->allocated);
     }
 
-    strcpy(index->names + index->size, name);
+    strcpy(index->data + index->size, name);
     index->size = new_size;
 
     return name_offset;
 }
 
-static void init_index(struct sltg_index *index)
+static void init_index(struct sltg_data *index)
 {
     static const char compobj[] = { 1,'C','o','m','p','O','b','j',0 };
 
-    index->size = 0;
-    index->allocated = 0x10;
-    index->names = xmalloc(0x10);
+    init_sltg_data(index);
 
     add_index(index, compobj);
 }
 
-static int add_name(struct sltg_name_table *name_table, const char *name)
+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;
 
+    chat("add_name: %s\n", name);
+
     new_size = (new_size + 1) & ~1; /* align */
 
     if (new_size > name_table->allocated)
     {
         name_table->allocated = max(name_table->allocated * 2, new_size);
-        name_table->names = xrealloc(name_table->names, name_table->allocated);
+        name_table->data = xrealloc(name_table->data, name_table->allocated);
     }
 
-    memset(name_table->names + name_table->size, 0xff, 8);
-    strcpy(name_table->names + name_table->size + 8, name);
+    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->names[name_table->size - 1] = 0; /* clear alignment */
+    name_table->data[name_table->size - 1] = 0; /* clear alignment */
 
     return name_offset;
 }
 
-static void init_name_table(struct sltg_name_table *name_table)
+static void init_name_table(struct sltg_data *name_table)
 {
-    name_table->size = 0;
-    name_table->allocated = 0x10;
-    name_table->names = xmalloc(0x10);
+    init_sltg_data(name_table);
 }
 
 static void init_library(struct sltg_typelib *sltg)
@@ -204,6 +281,8 @@ static void add_block(struct sltg_typelib *sltg, void *data, int size, const cha
 {
     struct sltg_block *block = xmalloc(sizeof(*block));
 
+    chat("add_block: %p,%d,\"%s\"\n", data, size, name);
+
     block->length = size;
     block->data = data;
     block->index_string = add_index(&sltg->index, name);
@@ -259,6 +338,67 @@ static void add_library_block(struct sltg_typelib *typelib)
     add_block(typelib, block, size, "dir");
 }
 
+static const char *new_index_name(void)
+{
+    static char name[11] = "0000000000";
+    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 size, const char *name)
+{
+    struct sltg_block *block = xmalloc(sizeof(*block));
+
+    chat("sltg_add_typeinfo: %p,%d,%s\n", data, size, name);
+
+    block->length = size;
+    block->data = data;
+    block->index_string = 0;
+    block->next = NULL;
+
+    if (sltg->typeinfo)
+    {
+        struct sltg_block *typeinfo = sltg->typeinfo;
+
+        while (typeinfo && typeinfo->next)
+            typeinfo = typeinfo->next;
+
+        typeinfo->next = block;
+    }
+    else
+        sltg->typeinfo = block;
+
+    sltg->typeinfo_count++;
+    sltg->typeinfo_size += size;
+}
+
+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_typedef_typeinfo(struct sltg_typelib *typelib, type_t *type)
 {
     error("add_typedef_typeinfo: %s not implemented\n", type->name);
@@ -274,9 +414,359 @@ static void add_interface_typeinfo(struct sltg_typelib *typelib, type_t *type)
     error("add_interface_typeinfo: %s not implemented\n", type->name);
 }
 
-static void add_structure_typeinfo(struct sltg_typelib *typelib, type_t *type)
+static const char *add_typeinfo_block(struct sltg_typelib *typelib, const type_t *type)
+{
+    const char *index_name, *other_name;
+    void *block;
+    short *p;
+    int size, 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;
+
+    size = sizeof(short) * 8 + 10 /* index_name */ * 2 + sizeof(int) + sizeof(GUID);
+
+    block = xmalloc(size);
+    p = block;
+    *p++ = strlen(index_name);
+    strcpy((char *)p, index_name);
+    p = (short *)((char *)p + strlen(index_name));
+    *p++ = strlen(other_name);
+    strcpy((char *)p, other_name);
+    p = (short *)((char *)p + strlen(other_name));
+    *p++ = -1; /* res1a */
+    *p++ = add_name(&typelib->name_table, type->name); /* name offset */
+    *p++ = 0; /* FIXME: helpstring */
+    *p++ = -1; /* res20 */
+    *(int *)p = helpcontext;
+    p += 2;
+    *p++ = -1; /* res26 */
+    *(GUID *)p = guid;
+    p += sizeof(GUID)/2;
+    *p = type_get_type(type);
+
+    sltg_add_typeinfo(typelib, block, size, index_name);
+
+    return index_name;
+}
+
+static void init_typeinfo(struct sltg_typeinfo_header *ti, const type_t *type, int kind)
+{
+    ti->magic = 0x0501;
+    ti->href_offset = -1;
+    ti->res06 = -1;
+    ti->member_offset = sizeof(*ti);
+    ti->res0e = -1;
+    ti->version = get_attrv(type->attrs, ATTR_VERSION);
+    ti->res16 = 0xfffe0000;
+    ti->misc.unknown1 = 0x02;
+    ti->misc.flags = 0; /* FIXME */
+    ti->misc.unknown2 = 0x02;
+    ti->misc.typekind = kind;
+    ti->res1e = -1;
+}
+
+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)
 {
-    error("add_structure_typeinfo: %s not implemented\n", type->name);
+    int vt = get_type_vt(type);
+
+    switch (vt)
+    {
+    case VT_I1:
+    case VT_UI1:
+        return 1;
+
+    case VT_INT:
+    case VT_UINT:
+        return /* typelib_kind == SYS_WIN16 ? 2 : */ 4;
+
+    case VT_UI2:
+    case VT_I2:
+    case VT_BOOL:
+        return 2;
+
+    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 pointer_size;
+
+    default:
+        error("get_element_size: unrecognized vt %d\n", vt);
+        break;
+    }
+
+    return 0;
+}
+
+static short write_var_desc(struct sltg_data *data, type_t *type, short flags, short base_offset)
+{
+    short vt, 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;
+        type_t *atype;
+        struct
+        {
+            short cDims;
+            short fFeatures;
+            int cbElements;
+            int cLocks;
+            void *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(type_array_get_element_type(type));
+        array->cLocks = 0;
+        array->pvData = NULL;
+
+        bound = array->bound;
+        atype = type;
+
+        while (is_array(atype) && !type_array_is_decl_as_ptr(atype))
+        {
+            bound[0] = type_array_get_dim(atype);
+            bound[1] = 0;
+            bound += 2;
+
+            atype = type_array_get_element_type(atype);
+        }
+
+        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\n");
+            vt = VT_PTR | 0x0400;
+            append_data(data, &vt, sizeof(vt));
+            write_var_desc(data, ref, 0, base_offset);
+        }
+        else
+            write_var_desc(data, ref, 0x0e00, base_offset);
+        return desc_offset;
+    }
+
+    chat("write_var_desc: vt %d, flags %04x\n", vt, flags);
+
+    vt |= flags;
+    append_data(data, &vt, sizeof(vt));
+
+    return desc_offset;
+}
+
+static void add_structure_typeinfo(struct sltg_typelib *typelib, const type_t *type)
+{
+    struct sltg_data data, *var_data = NULL;
+    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;
+    short *type_desc_offset = NULL;
+
+    chat("add_structure_typeinfo: %s\n", type->name);
+
+    init_sltg_data(&data);
+
+    index_name = add_typeinfo_block(typelib, type);
+
+    init_typeinfo(&ti, type, TKIND_RECORD);
+    append_data(&data, &ti, sizeof(ti));
+
+    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)
+        {
+            chat("add_structure_typeinfo: var %p, name %s, type %p\n",
+                 var, var->name, var->declspec.type);
+
+            init_sltg_data(&var_data[i]);
+
+            var_data_size += sizeof(struct sltg_variable);
+
+            type_desc_offset[i] = write_var_desc(&var_data[i], var->declspec.type, 0, var_data_size);
+            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++;
+        }
+    }
+
+    member_offset = data.size;
+
+    member.res00 = 0x0001;
+    member.res02 = 0xffff;
+    member.res04 = 0x01;
+    member.extra = var_data_size;
+    append_data(&data, &member, sizeof(member));
+
+    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;
+                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++;
+        }
+    }
+
+    tail.cFuncs = 0;
+    tail.cVars = var_count;
+    tail.cImplTypes = 0;
+    tail.res06 = 0;
+    tail.funcs_off = -1;
+    tail.vars_off = 0;
+    tail.impls_off = -1;
+    tail.funcs_bytes = -1;
+    tail.vars_bytes = var_data_size;
+    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 = 0;
+    tail.res26 = 0;
+    tail.cbSizeVft = 0;
+    tail.res2a = -1;
+    tail.res2c = -1;
+    tail.res2e = -1;
+    tail.res30 = -1;
+    tail.res32 = 0;
+    tail.type_bytes = data.size - member_offset;
+    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)
@@ -413,7 +903,9 @@ static void sltg_write_header(struct sltg_typelib *sltg, int *library_block_star
 
         entry.length = block->length;
         entry.index_string = block->index_string;
-        entry.next = header.first_blk + i;
+        entry.next = header.first_blk + i + 1;
+        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));
 
         block = block->next;
@@ -422,12 +914,15 @@ static void sltg_write_header(struct sltg_typelib *sltg, int *library_block_star
     assert(block->next == NULL);
 
     /* library block length includes helpstrings and name table */
-    entry.length = block->length + 0x40 + 2 + 4 + 6 + 12 + 0x200 + sltg->name_table.size + 12;
+    entry.length = block->length + 0x40 + 2 + sltg->typeinfo_size + 4 + 6 + 12 + 0x200 + sltg->name_table.size + 12;
     entry.index_string = block->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));
 
-    put_data(sltg->index.names, sltg->index.size);
+    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);
 
@@ -443,24 +938,41 @@ static void sltg_write_header(struct sltg_typelib *sltg, int *library_block_star
     assert(block->next == NULL);
 
     /* library block */
+    chat("library_block_start = %#lx\n", (SIZE_T)output_buffer_pos);
     *library_block_start = output_buffer_pos;
+    chat("sltg_write_header: writing library block %d: %d bytes\n", i, block->length);
     put_data(block->data, block->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)
 {
+    int i;
+    struct sltg_block *block;
     short count = typelib->typeinfo_count;
 
     put_data(&count, sizeof(count));
+
+    block = typelib->typeinfo;
+    for (i = 0; i < typelib->typeinfo_count; i++)
+    {
+        chat("sltg_write_typeinfo: writing block %d: %d bytes\n", i, block->length);
+
+        put_data(block->data, block->length);
+        block = block->next;
+    }
+    assert(block == NULL);
 }
 
 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));
 }
 
@@ -469,11 +981,13 @@ 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.names, typelib->name_table.size);
+    put_data(typelib->name_table.data, typelib->name_table.size);
 }
 
 static void sltg_write_remainder(void)
@@ -508,11 +1022,13 @@ static void save_all_changes(struct sltg_typelib *typelib)
     sltg_write_typeinfo(typelib);
 
     name_table_offset = (int *)(output_buffer + output_buffer_pos);
+    chat("name_table_offset = %#lx\n", (SIZE_T)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();
@@ -537,6 +1053,8 @@ int create_sltg_typelib(typelib_t *typelib)
 
     sltg.typelib = typelib;
     sltg.typeinfo_count = 0;
+    sltg.typeinfo_size = 0;
+    sltg.typeinfo = NULL;
     sltg.blocks = NULL;
     sltg.n_file_blocks = 0;
     sltg.first_block = 1;
-- 
2.17.1

