[PATCH 1/3] oleaut32: Implement ICreateTypeLib::SaveAllChanges

Andrew Eikum aeikum at codeweavers.com
Tue Jun 4 08:18:30 CDT 2013


---

This is the big one :) The file output by this method succeeds in
loading and passing the read tests that I add later in the series on
Windows 7. For smaller typelibs, it is binary identical.

 dlls/oleaut32/typelib.c | 1251 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 1249 insertions(+), 2 deletions(-)

diff --git a/dlls/oleaut32/typelib.c b/dlls/oleaut32/typelib.c
index 77c68ab..ffb1a21 100644
--- a/dlls/oleaut32/typelib.c
+++ b/dlls/oleaut32/typelib.c
@@ -8579,11 +8579,1258 @@ static HRESULT WINAPI ICreateTypeLib2_fnSetLibFlags(ICreateTypeLib2 *iface,
     return S_OK;
 }
 
+typedef struct tagWMSFT_SegContents {
+    DWORD len;
+    void *data;
+} WMSFT_SegContents;
+
+typedef struct tagWMSFT_TLBFile {
+    MSFT_Header header;
+    WMSFT_SegContents typeinfo_seg;
+    WMSFT_SegContents impfile_seg;
+    WMSFT_SegContents impinfo_seg;
+    WMSFT_SegContents ref_seg;
+    WMSFT_SegContents lib_seg;
+    WMSFT_SegContents guid_seg;
+    WMSFT_SegContents res07_seg;
+    WMSFT_SegContents name_seg;
+    WMSFT_SegContents string_seg;
+    WMSFT_SegContents typdesc_seg;
+    WMSFT_SegContents arraydesc_seg;
+    WMSFT_SegContents custdata_seg;
+    WMSFT_SegContents cdguids_seg;
+    MSFT_SegDir segdir;
+    WMSFT_SegContents aux_seg;
+} WMSFT_TLBFile;
+
+static HRESULT WMSFT_compile_strings(ITypeLibImpl *This,
+        WMSFT_TLBFile *file)
+{
+    TLBString *str;
+    UINT last_offs;
+    char *data;
+
+    file->string_seg.len = 0;
+    LIST_FOR_EACH_ENTRY(str, &This->string_list, TLBString, entry) {
+        int size;
+
+        size = WideCharToMultiByte(CP_ACP, 0, str->str, strlenW(str->str), NULL, 0, NULL, NULL);
+        if (size == 0)
+            return E_UNEXPECTED;
+
+        size += sizeof(INT16);
+        if (size % 4)
+            size = (size + 4) & ~0x3;
+        if (size < 8)
+            size = 8;
+
+        file->string_seg.len += size;
+
+        /* temporarily use str->offset to store the length of the aligned,
+         * converted string */
+        str->offset = size;
+    }
+
+    file->string_seg.data = data = heap_alloc(file->string_seg.len);
+
+    last_offs = 0;
+    LIST_FOR_EACH_ENTRY(str, &This->string_list, TLBString, entry) {
+        int size;
+
+        size = WideCharToMultiByte(CP_ACP, 0, str->str, strlenW(str->str),
+                data + sizeof(INT16), file->string_seg.len - last_offs - sizeof(INT16), NULL, NULL);
+        if (size == 0) {
+            heap_free(file->string_seg.data);
+            return E_UNEXPECTED;
+        }
+
+        *((INT16*)data) = size;
+
+        memset(data + sizeof(INT16) + size, 0x57, str->offset - size - sizeof(INT16));
+
+        size = str->offset;
+        data += size;
+        str->offset = last_offs;
+        last_offs += size;
+    }
+
+    return S_OK;
+}
+
+static HRESULT WMSFT_compile_names(ITypeLibImpl *This,
+        WMSFT_TLBFile *file)
+{
+    TLBString *str;
+    UINT last_offs;
+    char *data;
+    MSFT_NameIntro *last_intro = NULL;
+
+    file->header.nametablecount = 0;
+    file->header.nametablechars = 0;
+
+    file->name_seg.len = 0;
+    LIST_FOR_EACH_ENTRY(str, &This->name_list, TLBString, entry) {
+        int size;
+
+        size = strlenW(str->str);
+        file->header.nametablechars += size;
+        file->header.nametablecount++;
+
+        size = WideCharToMultiByte(CP_ACP, 0, str->str, size, NULL, 0, NULL, NULL);
+        if (size == 0)
+            return E_UNEXPECTED;
+
+        size += sizeof(MSFT_NameIntro);
+        if (size % 4)
+            size = (size + 4) & ~0x3;
+        if (size < 8)
+            size = 8;
+
+        file->name_seg.len += size;
+
+        /* temporarily use str->offset to store the length of the aligned,
+         * converted string */
+        str->offset = size;
+    }
+
+    file->name_seg.data = data = heap_alloc(file->name_seg.len);
+
+    last_offs = 0;
+    LIST_FOR_EACH_ENTRY(str, &This->name_list, TLBString, entry) {
+        int size;
+        MSFT_NameIntro *intro = (MSFT_NameIntro*)data;
+
+        size = WideCharToMultiByte(CP_ACP, 0, str->str, strlenW(str->str),
+                data + sizeof(MSFT_NameIntro),
+                file->name_seg.len - last_offs - sizeof(MSFT_NameIntro), NULL, NULL);
+        if (size == 0) {
+            heap_free(file->name_seg.data);
+            return E_UNEXPECTED;
+        }
+        data[sizeof(MSFT_NameIntro) + size] = '\0';
+
+        intro->hreftype = -1; /* TODO? */
+        intro->next_hash = -1;
+        intro->namelen = size & 0xFF;
+        /* TODO: namelen & 0xFF00 == ??? maybe HREF type indicator? */
+        intro->namelen |= LHashValOfNameSysA(This->syskind,
+                This->lcid, data + sizeof(MSFT_NameIntro)) << 16;
+
+        memset(data + sizeof(MSFT_NameIntro) + size, 0x57,
+                str->offset - size - sizeof(MSFT_NameIntro));
+
+        /* update str->offset to actual value to use in other
+         * compilation functions that require positions within
+         * the string table */
+        last_intro = intro;
+        size = str->offset;
+        data += size;
+        str->offset = last_offs;
+        last_offs += size;
+    }
+
+    if(last_intro)
+        last_intro->hreftype = 0; /* last one is 0? */
+
+    return S_OK;
+}
+
+static HRESULT WMSFT_compile_guids(ITypeLibImpl *This, WMSFT_TLBFile *file)
+{
+    TLBGuid *guid;
+    MSFT_GuidEntry *entry;
+    DWORD offs;
+
+    file->guid_seg.len = sizeof(MSFT_GuidEntry) * list_count(&This->guid_list);
+    file->guid_seg.data = heap_alloc(file->guid_seg.len);
+
+    entry = file->guid_seg.data;
+    offs = 0;
+    LIST_FOR_EACH_ENTRY(guid, &This->guid_list, TLBGuid, entry){
+        memcpy(&entry->guid, &guid->guid, sizeof(GUID));
+        entry->hreftype = 0xFFFFFFFF; /* TODO */
+        entry->next_hash = 0xFFFFFFFF; /* TODO? */
+
+        guid->offset = offs;
+
+        offs += sizeof(MSFT_GuidEntry);
+        ++entry;
+    }
+
+    --entry;
+    entry->next_hash = 0; /* last one has 0? */
+
+    return S_OK;
+}
+
+static DWORD WMSFT_encode_variant(VARIANT *value, WMSFT_TLBFile *file)
+{
+    VARIANT v = *value;
+    VARTYPE arg_type = V_VT(value);
+    int mask = 0;
+    HRESULT hres;
+    DWORD ret = file->custdata_seg.len;
+
+    if(arg_type == VT_INT)
+        arg_type = VT_I4;
+    if(arg_type == VT_UINT)
+        arg_type = VT_UI4;
+
+    v = *value;
+    if(V_VT(value) != arg_type) {
+        hres = VariantChangeType(&v, value, 0, arg_type);
+        if(FAILED(hres)){
+            ERR("VariantChangeType failed: %08x\n", hres);
+            return -1;
+        }
+    }
+
+    /* Check if default value can be stored in-place */
+    switch(arg_type){
+    case VT_I4:
+    case VT_UI4:
+        mask = 0x3ffffff;
+        if(V_UI4(&v) > 0x3ffffff)
+            break;
+        /* fall through */
+    case VT_I1:
+    case VT_UI1:
+    case VT_BOOL:
+        if(!mask)
+            mask = 0xff;
+        /* fall through */
+    case VT_I2:
+    case VT_UI2:
+        if(!mask)
+            mask = 0xffff;
+        return ((0x80 + 0x4 * V_VT(value)) << 24) | (V_UI4(&v) & mask);
+    }
+
+    /* have to allocate space in custdata_seg */
+    switch(arg_type) {
+    case VT_I4:
+    case VT_R4:
+    case VT_UI4:
+    case VT_INT:
+    case VT_UINT:
+    case VT_HRESULT:
+    case VT_PTR: {
+        /* Construct the data to be allocated */
+        int *data;
+
+        if(file->custdata_seg.data){
+            file->custdata_seg.data = heap_realloc(file->custdata_seg.data, file->custdata_seg.len + sizeof(int) * 2);
+            data = (int *)(((char *)file->custdata_seg.data) + file->custdata_seg.len);
+            file->custdata_seg.len += sizeof(int) * 2;
+        }else{
+            file->custdata_seg.len = sizeof(int) * 2;
+            data = file->custdata_seg.data = heap_alloc(file->custdata_seg.len);
+        }
+
+        data[0] = V_VT(value) + (V_UI4(&v) << 16);
+        data[1] = (V_UI4(&v) >> 16) + 0x57570000;
+
+        /* TODO: Check if the encoded data is already present in custdata_seg */
+
+        return ret;
+    }
+
+    case VT_BSTR: {
+        int i, len = (6+SysStringLen(V_BSTR(&v))+3) & ~0x3;
+        char *data;
+
+        if(file->custdata_seg.data){
+            file->custdata_seg.data = heap_realloc(file->custdata_seg.data, file->custdata_seg.len + len);
+            data = ((char *)file->custdata_seg.data) + file->custdata_seg.len;
+            file->custdata_seg.len += len;
+        }else{
+            file->custdata_seg.len = len;
+            data = file->custdata_seg.data = heap_alloc(file->custdata_seg.len);
+        }
+
+        *((unsigned short *)data) = V_VT(value);
+        *((unsigned int *)(data+2)) = SysStringLen(V_BSTR(&v));
+        for(i=0; i<SysStringLen(V_BSTR(&v)); i++) {
+            if(V_BSTR(&v)[i] <= 0x7f)
+                data[i+6] = V_BSTR(&v)[i];
+            else
+                data[i+6] = '?';
+        }
+        WideCharToMultiByte(CP_ACP, 0, V_BSTR(&v), SysStringLen(V_BSTR(&v)), &data[6], len-6, NULL, NULL);
+        for(i=6+SysStringLen(V_BSTR(&v)); i<len; i++)
+            data[i] = 0x57;
+
+        /* TODO: Check if the encoded data is already present in custdata_seg */
+
+        return ret;
+    }
+    default:
+        FIXME("Argument type not yet handled\n");
+        return -1;
+    }
+}
+
+static DWORD WMSFT_append_typedesc(TYPEDESC *desc, WMSFT_TLBFile *file, DWORD *out_mix, INT16 *out_size);
+
+static DWORD WMSFT_append_arraydesc(ARRAYDESC *desc, WMSFT_TLBFile *file)
+{
+    DWORD offs = file->arraydesc_seg.len;
+    DWORD *encoded;
+    USHORT i;
+
+    /* TODO: we should check for duplicates, but that's harder because each
+     * chunk is variable length (really we should store TYPEDESC and ARRAYDESC
+     * at the library-level) */
+
+    file->arraydesc_seg.len += (2 + desc->cDims * 2) * sizeof(DWORD);
+    if(!file->arraydesc_seg.data)
+        file->arraydesc_seg.data = heap_alloc(file->arraydesc_seg.len);
+    else
+        file->arraydesc_seg.data = heap_realloc(file->arraydesc_seg.data, file->arraydesc_seg.len);
+    encoded = (DWORD*)((char *)file->arraydesc_seg.data) + offs;
+
+    encoded[0] = WMSFT_append_typedesc(&desc->tdescElem, file, NULL, NULL);
+    encoded[1] = desc->cDims | ((desc->cDims * 2 * sizeof(DWORD)) << 16);
+    for(i = 0; i < desc->cDims; ++i){
+        encoded[2 + i * 2] =  desc->rgbounds[i].cElements;
+        encoded[2 + i * 2 + 1] = desc->rgbounds[i].lLbound;
+    }
+
+    return offs;
+}
+
+static DWORD WMSFT_append_typedesc(TYPEDESC *desc, WMSFT_TLBFile *file, DWORD *out_mix, INT16 *out_size)
+{
+    DWORD junk;
+    INT16 junk2;
+    DWORD offs = 0;
+    DWORD encoded[2];
+    VARTYPE vt = desc->vt & VT_TYPEMASK, subtype;
+    char *data;
+
+    if(!out_mix)
+        out_mix = &junk;
+    if(!out_size)
+        out_size = &junk2;
+
+    switch(vt){
+    case VT_INT:
+        subtype = VT_I4;
+        break;
+    case VT_UINT:
+        subtype = VT_UI4;
+        break;
+    case VT_VOID:
+        subtype = VT_EMPTY;
+        break;
+    default:
+        subtype = vt;
+        break;
+    }
+
+    switch(vt){
+    case VT_INT:
+    case VT_UINT:
+    case VT_I1:
+    case VT_UI1:
+    case VT_I2:
+    case VT_UI2:
+    case VT_I4:
+    case VT_UI4:
+    case VT_BOOL:
+    case VT_R4:
+    case VT_ERROR:
+    case VT_BSTR:
+    case VT_HRESULT:
+    case VT_CY:
+    case VT_VOID:
+    case VT_VARIANT:
+        *out_mix = subtype;
+        return 0x80000000 | (subtype << 16) | desc->vt;
+    }
+
+    if(vt == VT_PTR || vt == VT_SAFEARRAY){
+        DWORD mix;
+        encoded[1] = WMSFT_append_typedesc(desc->u.lptdesc, file, &mix, out_size);
+        encoded[0] = desc->vt | ((mix | VT_BYREF) << 16);
+        *out_mix = 0x7FFF;
+        *out_size += 2 * sizeof(DWORD);
+    }else if(vt == VT_CARRAY){
+        encoded[0] = desc->vt | (0x7FFE << 16);
+        encoded[1] = WMSFT_append_arraydesc(desc->u.lpadesc, file);
+        *out_mix = 0x7FFE;
+    }else if(vt == VT_USERDEFINED){
+        encoded[0] = desc->vt | (0x7FFF << 16);
+        encoded[1] = desc->u.hreftype;
+        *out_mix = 0x7FFF; /* FIXME: Should get TYPEKIND of the hreftype, e.g. TKIND_ENUM => VT_I4 */
+    }else{
+        FIXME("Don't know what to do! VT: 0x%x\n", desc->vt);
+        *out_mix = desc->vt;
+        return 0x80000000 | (desc->vt << 16) | desc->vt;
+    }
+
+    data = file->typdesc_seg.data;
+    while(offs < file->typdesc_seg.len){
+        if(!memcmp(&data[offs], encoded, sizeof(encoded)))
+            return offs;
+        offs += sizeof(encoded);
+    }
+
+    file->typdesc_seg.len += sizeof(encoded);
+    if(!file->typdesc_seg.data)
+        data = file->typdesc_seg.data = heap_alloc(file->typdesc_seg.len);
+    else
+        data = file->typdesc_seg.data = heap_realloc(file->typdesc_seg.data, file->typdesc_seg.len);
+
+    memcpy(&data[offs], encoded, sizeof(encoded));
+
+    return offs;
+}
+
+static DWORD WMSFT_compile_custdata(struct list *custdata_list, WMSFT_TLBFile *file)
+{
+    WMSFT_SegContents *cdguids_seg = &file->cdguids_seg;
+    DWORD ret = cdguids_seg->len, offs;
+    MSFT_CDGuid *cdguid = cdguids_seg->data;
+    TLBCustData *cd;
+
+    if(list_empty(custdata_list))
+        return -1;
+
+    cdguids_seg->len += sizeof(MSFT_CDGuid) * list_count(custdata_list);
+    if(!cdguids_seg->data){
+        cdguid = cdguids_seg->data = heap_alloc(cdguids_seg->len);
+    }else
+        cdguids_seg->data = heap_realloc(cdguids_seg->data, cdguids_seg->len);
+
+    offs = ret + sizeof(MSFT_CDGuid);
+    LIST_FOR_EACH_ENTRY(cd, custdata_list, TLBCustData, entry){
+        cdguid->GuidOffset = cd->guid->offset;
+        cdguid->DataOffset = WMSFT_encode_variant(&cd->data, file);
+        cdguid->next = offs;
+        offs += sizeof(MSFT_CDGuid);
+        ++cdguid;
+    }
+
+    --cdguid;
+    cdguid->next = -1;
+
+    return ret;
+}
+
+static DWORD WMSFT_compile_typeinfo_aux(ITypeInfoImpl *info,
+        WMSFT_TLBFile *file)
+{
+    WMSFT_SegContents *aux_seg = &file->aux_seg;
+    DWORD ret = aux_seg->len, i, j, recorded_size = 0, extra_size = 0;
+    MSFT_VarRecord *varrecord;
+    MSFT_FuncRecord *funcrecord;
+    MEMBERID *memid;
+    DWORD *name, *offsets, offs;
+
+    for(i = 0; i < info->cFuncs; ++i){
+        TLBFuncDesc *desc = &info->funcdescs[i];
+
+        recorded_size += 6 * sizeof(INT); /* mandatory fields */
+
+        /* optional fields */
+        /* TODO: oArgCustData - FuncSetCustData not impl yet */
+        if(!list_empty(&desc->custdata_list))
+            recorded_size += 7 * sizeof(INT);
+        else if(desc->HelpStringContext != 0)
+            recorded_size += 6 * sizeof(INT);
+        /* res9? resA? */
+        else if(desc->Entry)
+            recorded_size += 3 * sizeof(INT);
+        else if(desc->HelpString)
+            recorded_size += 2 * sizeof(INT);
+        else if(desc->helpcontext)
+            recorded_size += sizeof(INT);
+
+        recorded_size += desc->funcdesc.cParams * sizeof(MSFT_ParameterInfo);
+
+        for(j = 0; j < desc->funcdesc.cParams; ++j){
+            if(desc->funcdesc.lprgelemdescParam[j].u.paramdesc.wParamFlags & PARAMFLAG_FHASDEFAULT){
+                recorded_size += desc->funcdesc.cParams * sizeof(INT);
+                break;
+            }
+        }
+
+        extra_size += 2 * sizeof(INT); /* memberid, name offs */
+    }
+
+    for(i = 0; i < info->cVars; ++i){
+        TLBVarDesc *desc = &info->vardescs[i];
+
+        recorded_size += 5 * sizeof(INT); /* mandatory fields */
+
+        /* optional fields */
+        if(desc->HelpStringContext != 0)
+            recorded_size += 5 * sizeof(INT);
+        else if(!list_empty(&desc->custdata_list))
+            recorded_size += 4 * sizeof(INT);
+        /* res9? */
+        else if(desc->HelpString)
+            recorded_size += 2 * sizeof(INT);
+        else if(desc->HelpContext != 0)
+            recorded_size += sizeof(INT);
+
+        extra_size += 2 * sizeof(INT); /* memberid, name offs */
+    }
+
+    if(!recorded_size && !extra_size)
+        return ret;
+
+    extra_size += sizeof(INT); /* total aux size for this typeinfo */
+
+    aux_seg->len += recorded_size + extra_size;
+
+    aux_seg->len += sizeof(INT) * (info->cVars + info->cFuncs); /* offsets at the end */
+
+    if(aux_seg->data)
+        aux_seg->data = heap_realloc(aux_seg->data, aux_seg->len);
+    else
+        aux_seg->data = heap_alloc(aux_seg->len);
+
+    *((DWORD*)((char *)aux_seg->data + ret)) = recorded_size;
+
+    offsets = (DWORD*)((char *)aux_seg->data + ret + recorded_size + extra_size);
+    offs = 0;
+
+    funcrecord = (MSFT_FuncRecord*)(((char *)aux_seg->data) + ret + sizeof(INT));
+    for(i = 0; i < info->cFuncs; ++i){
+        TLBFuncDesc *desc = &info->funcdescs[i];
+        DWORD size = 6 * sizeof(INT), paramdefault_size = 0, *paramdefault;
+
+        funcrecord->funcdescsize = sizeof(desc->funcdesc) + desc->funcdesc.cParams * sizeof(ELEMDESC);
+        funcrecord->DataType = WMSFT_append_typedesc(&desc->funcdesc.elemdescFunc.tdesc, file, NULL, &funcrecord->funcdescsize);
+        funcrecord->Flags = desc->funcdesc.wFuncFlags;
+        funcrecord->VtableOffset = desc->funcdesc.oVft;
+
+        /* FKCCIC:
+         * XXXX XXXX XXXX XXXX  XXXX XXXX XXXX XXXX
+         *                                      ^^^funckind
+         *                                 ^^^ ^invkind
+         *                                ^has_cust_data
+         *                           ^^^^callconv
+         *                         ^has_param_defaults
+         *                        ^oEntry_is_intresource
+         */
+        funcrecord->FKCCIC =
+            desc->funcdesc.funckind |
+            (desc->funcdesc.invkind << 3) |
+            (list_empty(&desc->custdata_list) ? 0 : 0x80) |
+            (desc->funcdesc.callconv << 8);
+
+        if(desc->Entry && desc->Entry != (TLBString*)-1 && IS_INTRESOURCE(desc->Entry))
+            funcrecord->FKCCIC |= 0x2000;
+
+        for(j = 0; j < desc->funcdesc.cParams; ++j){
+            if(desc->funcdesc.lprgelemdescParam[j].u.paramdesc.wParamFlags & PARAMFLAG_FHASDEFAULT){
+                paramdefault_size = sizeof(INT) * desc->funcdesc.cParams;
+                funcrecord->funcdescsize += sizeof(PARAMDESCEX);
+            }
+        }
+        if(paramdefault_size > 0)
+            funcrecord->FKCCIC |= 0x1000;
+
+        funcrecord->nrargs = desc->funcdesc.cParams;
+        funcrecord->nroargs = desc->funcdesc.cParamsOpt;
+
+        /* optional fields */
+        /* res9? resA? */
+        if(!list_empty(&desc->custdata_list)){
+            size += 7 * sizeof(INT);
+            funcrecord->HelpContext = desc->helpcontext;
+            if(desc->HelpString)
+                funcrecord->oHelpString = desc->HelpString->offset;
+            else
+                funcrecord->oHelpString = -1;
+            if(!desc->Entry)
+                funcrecord->oEntry = -1;
+            else if(IS_INTRESOURCE(desc->Entry))
+                funcrecord->oEntry = (INT)desc->Entry;
+            else
+                funcrecord->oEntry = desc->Entry->offset;
+            funcrecord->res9 = -1;
+            funcrecord->resA = -1;
+            funcrecord->HelpStringContext = desc->HelpStringContext;
+            funcrecord->oCustData = WMSFT_compile_custdata(&desc->custdata_list, file);
+        }else if(desc->HelpStringContext != 0){
+            size += 6 * sizeof(INT);
+            funcrecord->HelpContext = desc->helpcontext;
+            if(desc->HelpString)
+                funcrecord->oHelpString = desc->HelpString->offset;
+            else
+                funcrecord->oHelpString = -1;
+            if(!desc->Entry)
+                funcrecord->oEntry = -1;
+            else if(IS_INTRESOURCE(desc->Entry))
+                funcrecord->oEntry = (INT)desc->Entry;
+            else
+                funcrecord->oEntry = desc->Entry->offset;
+            funcrecord->res9 = -1;
+            funcrecord->resA = -1;
+            funcrecord->HelpStringContext = desc->HelpStringContext;
+        }else if(desc->Entry){
+            size += 3 * sizeof(INT);
+            funcrecord->HelpContext = desc->helpcontext;
+            if(desc->HelpString)
+                funcrecord->oHelpString = desc->HelpString->offset;
+            else
+                funcrecord->oHelpString = -1;
+            if(!desc->Entry)
+                funcrecord->oEntry = -1;
+            else if(IS_INTRESOURCE(desc->Entry))
+                funcrecord->oEntry = (INT)desc->Entry;
+            else
+                funcrecord->oEntry = desc->Entry->offset;
+        }else if(desc->HelpString){
+            size += 2 * sizeof(INT);
+            funcrecord->HelpContext = desc->helpcontext;
+            funcrecord->oHelpString = desc->HelpString->offset;
+        }else if(desc->helpcontext){
+            size += sizeof(INT);
+            funcrecord->HelpContext = desc->helpcontext;
+        }
+
+        paramdefault = (DWORD*)((char *)funcrecord + size);
+        size += paramdefault_size;
+
+        for(j = 0; j < desc->funcdesc.cParams; ++j){
+            MSFT_ParameterInfo *info = (MSFT_ParameterInfo*)(((char *)funcrecord) + size);
+
+            info->DataType = WMSFT_append_typedesc(&desc->funcdesc.lprgelemdescParam[j].tdesc, file, NULL, &funcrecord->funcdescsize);
+            if(desc->pParamDesc[j].Name)
+                info->oName = desc->pParamDesc[j].Name->offset;
+            else
+                info->oName = -1;
+            info->Flags = desc->funcdesc.lprgelemdescParam[j].u.paramdesc.wParamFlags;
+
+            if(paramdefault_size){
+                if(desc->funcdesc.lprgelemdescParam[j].u.paramdesc.wParamFlags & PARAMFLAG_FHASDEFAULT)
+                    *paramdefault = WMSFT_encode_variant(&desc->funcdesc.lprgelemdescParam[j].u.paramdesc.pparamdescex->varDefaultValue, file);
+                else if(paramdefault_size)
+                    *paramdefault = -1;
+                ++paramdefault;
+            }
+
+            size += sizeof(MSFT_ParameterInfo);
+        }
+
+        funcrecord->Info = size | (i << 16); /* is it just the index? */
+
+        *offsets = offs;
+        offs += size;
+        ++offsets;
+
+        funcrecord = (MSFT_FuncRecord*)(((char*)funcrecord) + size);
+    }
+
+    varrecord = (MSFT_VarRecord*)funcrecord;
+    for(i = 0; i < info->cVars; ++i){
+        TLBVarDesc *desc = &info->vardescs[i];
+        DWORD size = 5 * sizeof(INT);
+
+        varrecord->vardescsize = sizeof(desc->vardesc);
+        varrecord->DataType = WMSFT_append_typedesc(&desc->vardesc.elemdescVar.tdesc, file, NULL, &varrecord->vardescsize);
+        varrecord->Flags = desc->vardesc.wVarFlags;
+        varrecord->VarKind = desc->vardesc.varkind;
+
+        if(desc->vardesc.varkind == VAR_CONST){
+            varrecord->vardescsize += sizeof(VARIANT);
+            varrecord->OffsValue = WMSFT_encode_variant(desc->vardesc.u.lpvarValue, file);
+        }else
+            varrecord->OffsValue = desc->vardesc.u.oInst;
+
+        /* res9? */
+        if(desc->HelpStringContext != 0){
+            size += 5 * sizeof(INT);
+            varrecord->HelpContext = desc->HelpContext;
+            if(desc->HelpString)
+                varrecord->HelpString = desc->HelpString->offset;
+            else
+                varrecord->HelpString = -1;
+            varrecord->res9 = -1;
+            varrecord->oCustData = WMSFT_compile_custdata(&desc->custdata_list, file);
+            varrecord->HelpStringContext = desc->HelpStringContext;
+        }else if(!list_empty(&desc->custdata_list)){
+            size += 4 * sizeof(INT);
+            varrecord->HelpContext = desc->HelpContext;
+            if(desc->HelpString)
+                varrecord->HelpString = desc->HelpString->offset;
+            else
+                varrecord->HelpString = -1;
+            varrecord->res9 = -1;
+            varrecord->oCustData = WMSFT_compile_custdata(&desc->custdata_list, file);
+        }else if(desc->HelpString){
+            size += 2 * sizeof(INT);
+            varrecord->HelpContext = desc->HelpContext;
+            if(desc->HelpString)
+                varrecord->HelpString = desc->HelpString->offset;
+            else
+                varrecord->HelpString = -1;
+        }else if(desc->HelpContext != 0){
+            size += sizeof(INT);
+            varrecord->HelpContext = desc->HelpContext;
+        }
+
+        varrecord->Info = size | (i << 16);
+
+        *offsets = offs;
+        offs += size;
+        ++offsets;
+
+        varrecord = (MSFT_VarRecord*)(((char*)varrecord) + size);
+    }
+
+    memid = (MEMBERID*)varrecord;
+    for(i = 0; i < info->cFuncs; ++i){
+        TLBFuncDesc *desc = &info->funcdescs[i];
+        *memid = desc->funcdesc.memid;
+        ++memid;
+    }
+    for(i = 0; i < info->cVars; ++i){
+        TLBVarDesc *desc = &info->vardescs[i];
+        *memid = desc->vardesc.memid;
+        ++memid;
+    }
+
+    name = (UINT*)memid;
+    for(i = 0; i < info->cFuncs; ++i){
+        TLBFuncDesc *desc = &info->funcdescs[i];
+        if(desc->Name)
+            *name = desc->Name->offset;
+        else
+            *name = -1;
+        ++name;
+    }
+    for(i = 0; i < info->cVars; ++i){
+        TLBVarDesc *desc = &info->vardescs[i];
+        if(desc->Name)
+            *name = desc->Name->offset;
+        else
+            *name = -1;
+        ++name;
+    }
+
+    return ret;
+}
+
+typedef struct tagWMSFT_RefChunk {
+    DWORD href;
+    DWORD res04;
+    DWORD res08;
+    DWORD next;
+} WMSFT_RefChunk;
+
+static DWORD WMSFT_compile_typeinfo_ref(ITypeInfoImpl *info, WMSFT_TLBFile *file)
+{
+    DWORD offs = file->ref_seg.len, i;
+    WMSFT_RefChunk *chunk;
+
+    file->ref_seg.len += info->cImplTypes * sizeof(WMSFT_RefChunk);
+    if(!file->ref_seg.data)
+        file->ref_seg.data = heap_alloc(file->ref_seg.len);
+    else
+        file->ref_seg.data = heap_realloc(file->ref_seg.data, file->ref_seg.len);
+
+    chunk = (WMSFT_RefChunk*)((char*)file->ref_seg.data + offs);
+
+    for(i = 0; i < info->cImplTypes; ++i){
+        chunk->href = info->impltypes[i].hRef;
+        chunk->res04 = info->impltypes[i].implflags;
+        chunk->res08 = -1;
+        if(i < info->cImplTypes - 1)
+            chunk->next = offs + sizeof(WMSFT_RefChunk) * (i + 1);
+        else
+            chunk->next = -1;
+        ++chunk;
+    }
+
+    return offs;
+}
+
+static DWORD WMSFT_compile_typeinfo(ITypeInfoImpl *info, INT16 index, WMSFT_TLBFile *file, char *data)
+{
+    DWORD size;
+
+    size = sizeof(MSFT_TypeInfoBase);
+
+    if(data){
+        MSFT_TypeInfoBase *base = (void*)data;
+        if(info->wTypeFlags & TYPEFLAG_FDUAL)
+            base->typekind = TKIND_DISPATCH;
+        else
+            base->typekind = info->typekind;
+        base->typekind |= index << 16; /* TODO: There are some other flags here */
+        base->typekind |= (info->cbAlignment << 11) | (info->cbAlignment << 6);
+        base->memoffset = WMSFT_compile_typeinfo_aux(info, file);
+        base->res2 = 0;
+        base->res3 = 0;
+        base->res4 = 3;
+        base->res5 = 0;
+        base->cElement = (info->cVars << 16) | info->cFuncs;
+        base->res7 = 0;
+        base->res8 = 0;
+        base->res9 = 0;
+        base->resA = 0;
+        if(info->guid)
+            base->posguid = info->guid->offset;
+        else
+            base->posguid = -1;
+        base->flags = info->wTypeFlags;
+        if(info->Name)
+            base->NameOffset = info->Name->offset;
+        else
+            base->NameOffset = -1;
+        base->version = (info->wMinorVerNum << 16) | info->wMajorVerNum;
+        if(info->DocString)
+            base->docstringoffs = info->DocString->offset;
+        else
+            base->docstringoffs = -1;
+        base->helpstringcontext = info->dwHelpStringContext;
+        base->helpcontext = info->dwHelpContext;
+        base->oCustData = WMSFT_compile_custdata(&info->custdata_list, file);
+        base->cImplTypes = info->cImplTypes;
+        base->cbSizeVft = info->cbSizeVft;
+        base->size = info->cbSizeInstance;
+        if(info->typekind == TKIND_COCLASS){
+            base->datatype1 = WMSFT_compile_typeinfo_ref(info, file);
+        }else if(info->typekind == TKIND_ALIAS){
+            base->datatype1 = WMSFT_append_typedesc(&info->tdescAlias, file, NULL, NULL);
+        }else if(info->typekind == TKIND_MODULE){
+            if(info->DllName)
+                base->datatype1 = info->DllName->offset;
+            else
+                base->datatype1 = -1;
+        }else{
+            if(info->cImplTypes > 0)
+                base->datatype1 = info->impltypes[0].hRef;
+            else
+                base->datatype1 = -1;
+        }
+        base->datatype2 = index; /* FIXME: i think there's more here */
+        base->res18 = 0;
+        base->res19 = -1;
+    }
+
+    return size;
+}
+
+static void WMSFT_compile_typeinfo_seg(ITypeLibImpl *This, WMSFT_TLBFile *file, DWORD *junk)
+{
+    UINT i;
+
+    file->typeinfo_seg.len = 0;
+    for(i = 0; i < This->TypeInfoCount; ++i){
+        ITypeInfoImpl *info = This->typeinfos[i];
+        *junk = file->typeinfo_seg.len;
+        ++junk;
+        file->typeinfo_seg.len += WMSFT_compile_typeinfo(info, i, NULL, NULL);
+    }
+
+    file->typeinfo_seg.data = heap_alloc(file->typeinfo_seg.len);
+    memset(file->typeinfo_seg.data, 0x96, file->typeinfo_seg.len);
+
+    file->aux_seg.len = 0;
+    file->aux_seg.data = NULL;
+
+    file->typeinfo_seg.len = 0;
+    for(i = 0; i < This->TypeInfoCount; ++i){
+        ITypeInfoImpl *info = This->typeinfos[i];
+        file->typeinfo_seg.len += WMSFT_compile_typeinfo(info, i, file,
+                ((char *)file->typeinfo_seg.data) + file->typeinfo_seg.len);
+    }
+}
+
+typedef struct tagWMSFT_ImpFile {
+    INT guid_offs;
+    LCID lcid;
+    DWORD version;
+} WMSFT_ImpFile;
+
+static void WMSFT_compile_impfile(ITypeLibImpl *This, WMSFT_TLBFile *file)
+{
+    TLBImpLib *implib;
+    WMSFT_ImpFile *impfile;
+    char *data;
+    DWORD last_offs = 0;
+
+    file->impfile_seg.len = 0;
+    LIST_FOR_EACH_ENTRY(implib, &This->implib_list, TLBImpLib, entry){
+        int size = 0;
+
+        if(implib->name){
+            WCHAR *path = strrchrW(implib->name, '\\');
+            if(path)
+                ++path;
+            else
+                path = implib->name;
+            size = WideCharToMultiByte(CP_ACP, 0, path, strlenW(path), NULL, 0, NULL, NULL);
+            if (size == 0)
+                ERR("failed to convert wide string: %s\n", debugstr_w(path));
+        }
+
+        size += sizeof(INT16);
+        if (size % 4)
+            size = (size + 4) & ~0x3;
+        if (size < 8)
+            size = 8;
+
+        file->impfile_seg.len += sizeof(WMSFT_ImpFile) + size;
+    }
+
+    data = file->impfile_seg.data = heap_alloc(file->impfile_seg.len);
+
+    LIST_FOR_EACH_ENTRY(implib, &This->implib_list, TLBImpLib, entry){
+        int strlen = 0, size;
+
+        impfile = (WMSFT_ImpFile*)data;
+        impfile->guid_offs = implib->guid->offset;
+        impfile->lcid = implib->lcid;
+        impfile->version = (implib->wVersionMinor << 16) | implib->wVersionMajor;
+
+        data += sizeof(WMSFT_ImpFile);
+
+        if(implib->name){
+            WCHAR *path= strrchrW(implib->name, '\\');
+            if(path)
+                ++path;
+            else
+                path = implib->name;
+            strlen = WideCharToMultiByte(CP_ACP, 0, path, strlenW(path),
+                    data + sizeof(INT16), file->impfile_seg.len - last_offs - sizeof(INT16), NULL, NULL);
+            if (size == 0)
+                ERR("failed to convert wide string: %s\n", debugstr_w(path));
+        }
+
+        *((INT16*)data) = (strlen << 2) | 1; /* FIXME: is that a flag, or what? */
+
+        size = strlen + sizeof(INT16);
+        if (size % 4)
+            size = (size + 4) & ~0x3;
+        if (size < 8)
+            size = 8;
+        memset(data + sizeof(INT16) + strlen, 0x57, size - strlen - sizeof(INT16));
+
+        data += size;
+        implib->offset = last_offs;
+        last_offs += size + sizeof(WMSFT_ImpFile);
+    }
+}
+
+static void WMSFT_compile_impinfo(ITypeLibImpl *This, WMSFT_TLBFile *file)
+{
+    MSFT_ImpInfo *info;
+    TLBRefType *ref_type;
+    UINT i = 0;
+
+    WMSFT_compile_impfile(This, file);
+
+    file->impinfo_seg.len = sizeof(MSFT_ImpInfo) * list_count(&This->ref_list);
+    info = file->impinfo_seg.data = heap_alloc(file->impinfo_seg.len);
+
+    LIST_FOR_EACH_ENTRY(ref_type, &This->ref_list, TLBRefType, entry){
+        info->flags = i | ((ref_type->tkind & 0xFF) << 24);
+        if(ref_type->index == TLB_REF_USE_GUID){
+            info->flags |= MSFT_IMPINFO_OFFSET_IS_GUID;
+            info->oGuid = ref_type->guid->offset;
+        }else
+            info->oGuid = ref_type->index;
+        info->oImpFile = ref_type->pImpTLInfo->offset;
+        ++i;
+        ++info;
+    }
+}
+
+static void WMSFT_compile_lib(ITypeLibImpl *This, WMSFT_TLBFile *file)
+{
+    /* TODO: What actually goes here? */
+    file->lib_seg.len = 0x80;
+    file->lib_seg.data = heap_alloc(file->lib_seg.len);
+    memset(file->lib_seg.data, 0xFF, file->lib_seg.len);
+
+#if 0
+    /* sometimes, first element is offset to last guid, for some reason */
+    if(This->guid)
+        *(DWORD*)file->lib_seg.data =
+            (list_count(&This->guid_list) - 1) * sizeof(MSFT_GuidEntry);
+#endif
+}
+
+static void WMSFT_compile_res07(ITypeLibImpl *This, WMSFT_TLBFile *file)
+{
+    /* TODO: What actually goes here?
+     * Wild speculation:
+     * It's something to do with the Name table (and string table?). When you
+     * add a new name, the offset to that name from within the Name table gets
+     * stuck somewhere in res07, apparently starting at the end of the segment
+     * then moving backwards at a rate of 3 bytes per char in the Name. */
+    file->res07_seg.len = 0x200;
+    file->res07_seg.data = heap_alloc(file->res07_seg.len);
+    memset(file->res07_seg.data, 0xFF, file->res07_seg.len);
+}
+
+static void tmp_fill_segdir_seg(MSFT_pSeg *segdir, WMSFT_SegContents *contents, DWORD *running_offset)
+{
+    if(contents && contents->len){
+        segdir->offset = *running_offset;
+        segdir->length = contents->len;
+        *running_offset += segdir->length;
+    }else{
+        segdir->offset = -1;
+        segdir->length = 0;
+    }
+
+    /* TODO: do these ever change? */
+    segdir->res08 = -1;
+    segdir->res0c = 0xf;
+}
+
+static void WMSFT_write_segment(HANDLE outfile, WMSFT_SegContents *segment)
+{
+    DWORD written;
+    if(segment)
+        WriteFile(outfile, segment->data, segment->len, &written, NULL);
+}
+
+static HRESULT WMSFT_fixup_typeinfos(ITypeLibImpl *This, WMSFT_TLBFile *file,
+        DWORD file_len)
+{
+    DWORD i;
+    MSFT_TypeInfoBase *base = (MSFT_TypeInfoBase *)file->typeinfo_seg.data;
+
+    for(i = 0; i < This->TypeInfoCount; ++i){
+        base->memoffset += file_len;
+        ++base;
+    }
+
+    return S_OK;
+}
+
+static void WMSFT_free_file(WMSFT_TLBFile *file)
+{
+    HeapFree(GetProcessHeap(), 0, file->typeinfo_seg.data);
+    HeapFree(GetProcessHeap(), 0, file->lib_seg.data);
+    HeapFree(GetProcessHeap(), 0, file->guid_seg.data);
+    HeapFree(GetProcessHeap(), 0, file->ref_seg.data);
+    HeapFree(GetProcessHeap(), 0, file->impinfo_seg.data);
+    HeapFree(GetProcessHeap(), 0, file->impfile_seg.data);
+    HeapFree(GetProcessHeap(), 0, file->res07_seg.data);
+    HeapFree(GetProcessHeap(), 0, file->name_seg.data);
+    HeapFree(GetProcessHeap(), 0, file->string_seg.data);
+    HeapFree(GetProcessHeap(), 0, file->typdesc_seg.data);
+    HeapFree(GetProcessHeap(), 0, file->arraydesc_seg.data);
+    HeapFree(GetProcessHeap(), 0, file->custdata_seg.data);
+    HeapFree(GetProcessHeap(), 0, file->cdguids_seg.data);
+    HeapFree(GetProcessHeap(), 0, file->aux_seg.data);
+}
+
 static HRESULT WINAPI ICreateTypeLib2_fnSaveAllChanges(ICreateTypeLib2 *iface)
 {
     ITypeLibImpl *This = impl_from_ICreateTypeLib2(iface);
-    FIXME("%p - stub\n", This);
-    return E_NOTIMPL;
+    WMSFT_TLBFile file;
+    DWORD written, junk_size, junk_offs, running_offset;
+    BOOL br;
+    HANDLE outfile;
+    HRESULT hres;
+    DWORD *junk;
+
+    TRACE("%p\n", This);
+
+    memset(&file, 0, sizeof(file));
+
+    file.header.magic1 = 0x5446534D;
+    file.header.magic2 = 0x00010002;
+    file.header.lcid = This->set_lcid ? This->set_lcid : MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
+    file.header.lcid2 = This->set_lcid;
+    file.header.varflags = 0x41; /* TODO?? */
+    if (This->HelpFile)
+        file.header.varflags |= 0x10;
+    if (This->HelpStringDll)
+        file.header.varflags |= HELPDLLFLAG;
+    file.header.version = (This->ver_major << 16) | This->ver_minor;
+    file.header.flags = This->libflags;
+    file.header.helpstringcontext = 0; /* TODO - SetHelpStringContext not implemented yet */
+    file.header.helpcontext = This->dwHelpContext;
+    file.header.res44 = 0x20;
+    file.header.res48 = 0x80;
+    file.header.dispatchpos = This->dispatch_href;
+
+    /* do name and string compilation to get offsets for other compilations */
+    hres = WMSFT_compile_names(This, &file);
+    if (FAILED(hres)){
+        WMSFT_free_file(&file);
+        return hres;
+    }
+
+    hres = WMSFT_compile_strings(This, &file);
+    if (FAILED(hres)){
+        WMSFT_free_file(&file);
+        return hres;
+    }
+
+    hres = WMSFT_compile_guids(This, &file);
+    if (FAILED(hres)){
+        WMSFT_free_file(&file);
+        return hres;
+    }
+
+    if(This->HelpFile)
+        file.header.helpfile = This->HelpFile->offset;
+    else
+        file.header.helpfile = -1;
+
+    if(This->DocString)
+        file.header.helpstring = This->DocString->offset;
+    else
+        file.header.helpstring = -1;
+
+    /* do some more segment compilation */
+    file.header.nimpinfos = list_count(&This->ref_list);
+    file.header.nrtypeinfos = This->TypeInfoCount;
+
+    if(This->Name)
+        file.header.NameOffset = This->Name->offset;
+    else
+        file.header.NameOffset = -1;
+
+    file.header.CustomDataOffset = -1; /* TODO SetCustData not impl yet */
+
+    if(This->guid)
+        file.header.posguid = This->guid->offset;
+    else
+        file.header.posguid = -1;
+
+    junk_size = file.header.nrtypeinfos * sizeof(DWORD);
+    if(file.header.varflags & HELPDLLFLAG)
+        junk_size += sizeof(DWORD);
+    if(junk_size){
+        junk = heap_alloc_zero(junk_size);
+        if(file.header.varflags & HELPDLLFLAG){
+            *junk = This->HelpStringDll->offset;
+            junk_offs = 1;
+        }else
+            junk_offs = 0;
+    }else{
+        junk = NULL;
+        junk_offs = 0;
+    }
+
+    WMSFT_compile_typeinfo_seg(This, &file, junk + junk_offs);
+    WMSFT_compile_impinfo(This, &file);
+    WMSFT_compile_lib(This, &file);
+    WMSFT_compile_res07(This, &file);
+
+    running_offset = 0;
+
+    TRACE("header at: 0x%x\n", running_offset);
+    running_offset += sizeof(file.header);
+
+    TRACE("junk at: 0x%x\n", running_offset);
+    running_offset += junk_size;
+
+    TRACE("segdir at: 0x%x\n", running_offset);
+    running_offset += sizeof(file.segdir);
+
+    TRACE("typeinfo at: 0x%x\n", running_offset);
+    tmp_fill_segdir_seg(&file.segdir.pTypeInfoTab, &file.typeinfo_seg, &running_offset);
+
+    TRACE("libtab at: 0x%x\n", running_offset);
+    tmp_fill_segdir_seg(&file.segdir.pLibtab, &file.lib_seg, &running_offset);
+
+    TRACE("guidtab at: 0x%x\n", running_offset);
+    tmp_fill_segdir_seg(&file.segdir.pGuidTab, &file.guid_seg, &running_offset);
+
+    TRACE("reftab at: 0x%x\n", running_offset);
+    tmp_fill_segdir_seg(&file.segdir.pRefTab, &file.ref_seg, &running_offset);
+
+    TRACE("impinfo at: 0x%x\n", running_offset);
+    tmp_fill_segdir_seg(&file.segdir.pImpInfo, &file.impinfo_seg, &running_offset);
+
+    TRACE("impfiles at: 0x%x\n", running_offset);
+    tmp_fill_segdir_seg(&file.segdir.pImpFiles, &file.impfile_seg, &running_offset);
+
+    TRACE("res07 at: 0x%x\n", running_offset);
+    tmp_fill_segdir_seg(&file.segdir.res07, &file.res07_seg, &running_offset);
+
+    TRACE("nametab at: 0x%x\n", running_offset);
+    tmp_fill_segdir_seg(&file.segdir.pNametab, &file.name_seg, &running_offset);
+
+    TRACE("stringtab at: 0x%x\n", running_offset);
+    tmp_fill_segdir_seg(&file.segdir.pStringtab, &file.string_seg, &running_offset);
+
+    TRACE("typdesc at: 0x%x\n", running_offset);
+    tmp_fill_segdir_seg(&file.segdir.pTypdescTab, &file.typdesc_seg, &running_offset);
+
+    TRACE("arraydescriptions at: 0x%x\n", running_offset);
+    tmp_fill_segdir_seg(&file.segdir.pArrayDescriptions, &file.arraydesc_seg, &running_offset);
+
+    TRACE("custdata at: 0x%x\n", running_offset);
+    tmp_fill_segdir_seg(&file.segdir.pCustData, &file.custdata_seg, &running_offset);
+
+    TRACE("cdguids at: 0x%x\n", running_offset);
+    tmp_fill_segdir_seg(&file.segdir.pCDGuids, &file.cdguids_seg, &running_offset);
+
+    TRACE("res0e at: 0x%x\n", running_offset);
+    tmp_fill_segdir_seg(&file.segdir.res0e, NULL, &running_offset);
+
+    TRACE("res0f at: 0x%x\n", running_offset);
+    tmp_fill_segdir_seg(&file.segdir.res0f, NULL, &running_offset);
+
+    TRACE("aux_seg at: 0x%x\n", running_offset);
+
+    WMSFT_fixup_typeinfos(This, &file, running_offset);
+
+    outfile = CreateFileW(This->path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+            FILE_ATTRIBUTE_NORMAL, 0);
+    if (outfile == INVALID_HANDLE_VALUE){
+        WMSFT_free_file(&file);
+        return TYPE_E_IOERROR;
+    }
+
+    br = WriteFile(outfile, &file.header, sizeof(file.header), &written, NULL);
+    if (!br) {
+        WMSFT_free_file(&file);
+        CloseHandle(outfile);
+        return TYPE_E_IOERROR;
+    }
+
+    br = WriteFile(outfile, junk, junk_size, &written, NULL);
+    if (!br) {
+        WMSFT_free_file(&file);
+        CloseHandle(outfile);
+        return TYPE_E_IOERROR;
+    }
+
+    br = WriteFile(outfile, &file.segdir, sizeof(file.segdir), &written, NULL);
+    if (!br) {
+        WMSFT_free_file(&file);
+        CloseHandle(outfile);
+        return TYPE_E_IOERROR;
+    }
+
+    WMSFT_write_segment(outfile, &file.typeinfo_seg);
+    WMSFT_write_segment(outfile, &file.lib_seg);
+    WMSFT_write_segment(outfile, &file.guid_seg);
+    WMSFT_write_segment(outfile, &file.ref_seg);
+    WMSFT_write_segment(outfile, &file.impinfo_seg);
+    WMSFT_write_segment(outfile, &file.impfile_seg);
+    WMSFT_write_segment(outfile, &file.res07_seg);
+    WMSFT_write_segment(outfile, &file.name_seg);
+    WMSFT_write_segment(outfile, &file.string_seg);
+    WMSFT_write_segment(outfile, &file.typdesc_seg);
+    WMSFT_write_segment(outfile, &file.arraydesc_seg);
+    WMSFT_write_segment(outfile, &file.custdata_seg);
+    WMSFT_write_segment(outfile, &file.cdguids_seg);
+    WMSFT_write_segment(outfile, &file.aux_seg);
+
+    WMSFT_free_file(&file);
+
+    CloseHandle(outfile);
+
+    return S_OK;
 }
 
 static HRESULT WINAPI ICreateTypeLib2_fnDeleteTypeInfo(ICreateTypeLib2 *iface,
-- 
1.8.3





More information about the wine-patches mailing list