[01/10] msi: Move patch related functions to a separate file.

Hans Leidekker hans at codeweavers.com
Tue May 24 03:49:37 CDT 2011


---
 dlls/msi/Makefile.in |    1 +
 dlls/msi/action.c    |  751 --------------------------------------------------
 dlls/msi/msipriv.h   |    2 +
 dlls/msi/patch.c     |  719 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 722 insertions(+), 751 deletions(-)
 create mode 100644 dlls/msi/patch.c

diff --git a/dlls/msi/Makefile.in b/dlls/msi/Makefile.in
index 0f076b0..3954e9e 100644
--- a/dlls/msi/Makefile.in
+++ b/dlls/msi/Makefile.in
@@ -30,6 +30,7 @@ C_SRCS = \
 	msi_main.c \
 	msiquery.c \
 	package.c \
+	patch.c \
 	preview.c \
 	record.c \
 	registry.c \
diff --git a/dlls/msi/action.c b/dlls/msi/action.c
index f0c05d6..61c0783 100644
--- a/dlls/msi/action.c
+++ b/dlls/msi/action.c
@@ -403,751 +403,6 @@ WCHAR **msi_split_string( const WCHAR *str, WCHAR sep )
     return ret;
 }
 
-static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
-{
-    static const WCHAR szSystemLanguageID[] =
-        { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
-
-    LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
-    UINT ret = ERROR_FUNCTION_FAILED;
-
-    prod_code = msi_dup_property( package->db, szProductCode );
-    patch_product = msi_get_suminfo_product( patch );
-
-    TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
-
-    if ( strstrW( patch_product, prod_code ) )
-    {
-        MSISUMMARYINFO *si;
-        const WCHAR *p;
-
-        si = MSI_GetSummaryInformationW( patch, 0 );
-        if (!si)
-        {
-            ERR("no summary information!\n");
-            goto end;
-        }
-
-        template = msi_suminfo_dup_string( si, PID_TEMPLATE );
-        if (!template)
-        {
-            ERR("no template property!\n");
-            msiobj_release( &si->hdr );
-            goto end;
-        }
-
-        if (!template[0])
-        {
-            ret = ERROR_SUCCESS;
-            msiobj_release( &si->hdr );
-            goto end;
-        }
-
-        langid = msi_dup_property( package->db, szSystemLanguageID );
-        if (!langid)
-        {
-            msiobj_release( &si->hdr );
-            goto end;
-        }
-
-        p = strchrW( template, ';' );
-        if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
-        {
-            TRACE("applicable transform\n");
-            ret = ERROR_SUCCESS;
-        }
-
-        /* FIXME: check platform */
-
-        msiobj_release( &si->hdr );
-    }
-
-end:
-    msi_free( patch_product );
-    msi_free( prod_code );
-    msi_free( template );
-    msi_free( langid );
-
-    return ret;
-}
-
-static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
-                                 MSIDATABASE *patch_db, LPCWSTR name )
-{
-    UINT ret = ERROR_FUNCTION_FAILED;
-    IStorage *stg = NULL;
-    HRESULT r;
-
-    TRACE("%p %s\n", package, debugstr_w(name) );
-
-    if (*name++ != ':')
-    {
-        ERR("expected a colon in %s\n", debugstr_w(name));
-        return ERROR_FUNCTION_FAILED;
-    }
-
-    r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
-    if (SUCCEEDED(r))
-    {
-        ret = msi_check_transform_applicable( package, stg );
-        if (ret == ERROR_SUCCESS)
-            msi_table_apply_transform( package->db, stg );
-        else
-            TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
-        IStorage_Release( stg );
-    }
-    else
-        ERR("failed to open substorage %s\n", debugstr_w(name));
-
-    return ERROR_SUCCESS;
-}
-
-UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
-{
-    LPWSTR guid_list, *guids, product_code;
-    UINT i, ret = ERROR_FUNCTION_FAILED;
-
-    product_code = msi_dup_property( package->db, szProductCode );
-    if (!product_code)
-    {
-        /* FIXME: the property ProductCode should be written into the DB somewhere */
-        ERR("no product code to check\n");
-        return ERROR_SUCCESS;
-    }
-
-    guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
-    guids = msi_split_string( guid_list, ';' );
-    for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
-    {
-        if (!strcmpW( guids[i], product_code ))
-            ret = ERROR_SUCCESS;
-    }
-    msi_free( guids );
-    msi_free( guid_list );
-    msi_free( product_code );
-
-    return ret;
-}
-
-static UINT msi_set_media_source_prop(MSIPACKAGE *package)
-{
-    MSIQUERY *view;
-    MSIRECORD *rec = NULL;
-    LPWSTR patch;
-    LPCWSTR prop;
-    UINT r;
-
-    static const WCHAR query[] = {'S','E','L','E','C','T',' ',
-        '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
-        '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
-        '`','S','o','u','r','c','e','`',' ','I','S',' ',
-        'N','O','T',' ','N','U','L','L',0};
-
-    r = MSI_DatabaseOpenViewW(package->db, query, &view);
-    if (r != ERROR_SUCCESS)
-        return r;
-
-    r = MSI_ViewExecute(view, 0);
-    if (r != ERROR_SUCCESS)
-        goto done;
-
-    if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
-    {
-        prop = MSI_RecordGetString(rec, 1);
-        patch = msi_dup_property(package->db, szPatch);
-        msi_set_property(package->db, prop, patch);
-        msi_free(patch);
-    }
-
-done:
-    if (rec) msiobj_release(&rec->hdr);
-    msiobj_release(&view->hdr);
-
-    return r;
-}
-
-UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
-{
-    MSIPATCHINFO *pi;
-    UINT r = ERROR_SUCCESS;
-    WCHAR *p;
-
-    pi = msi_alloc_zero( sizeof(MSIPATCHINFO) );
-    if (!pi)
-        return ERROR_OUTOFMEMORY;
-
-    pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER );
-    if (!pi->patchcode)
-    {
-        msi_free( pi );
-        return ERROR_OUTOFMEMORY;
-    }
-
-    p = pi->patchcode;
-    if (*p != '{')
-    {
-        msi_free( pi->patchcode );
-        msi_free( pi );
-        return ERROR_PATCH_PACKAGE_INVALID;
-    }
-
-    p = strchrW( p + 1, '}' );
-    if (!p)
-    {
-        msi_free( pi->patchcode );
-        msi_free( pi );
-        return ERROR_PATCH_PACKAGE_INVALID;
-    }
-
-    if (p[1])
-    {
-        FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
-        p[1] = 0;
-    }
-
-    TRACE("patch code %s\n", debugstr_w(pi->patchcode));
-
-    pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
-    if (!pi->transforms)
-    {
-        msi_free( pi->patchcode );
-        msi_free( pi );
-        return ERROR_OUTOFMEMORY;
-    }
-
-    *patch = pi;
-    return r;
-}
-
-struct msi_patch_offset
-{
-    struct list entry;
-    LPWSTR Name;
-    UINT Sequence;
-};
-
-struct msi_patch_offset_list
-{
-    struct list files;
-    UINT count, min, max;
-    UINT offset_to_apply;
-};
-
-static struct msi_patch_offset_list *msi_patch_offset_list_create(void)
-{
-    struct msi_patch_offset_list *pos = msi_alloc(sizeof(struct msi_patch_offset_list));
-    list_init( &pos->files );
-    pos->count = pos->max = 0;
-    pos->min = 999999;
-
-    return pos;
-}
-
-static void msi_patch_offset_list_free(struct msi_patch_offset_list *pos)
-{
-    struct msi_patch_offset *po, *po2;
-
-    LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct msi_patch_offset, entry )
-    {
-        msi_free( po->Name );
-        msi_free( po );
-    }
-
-    msi_free( pos );
-}
-
-static void msi_patch_offset_get_patches(MSIDATABASE *db, UINT last_sequence, struct msi_patch_offset_list *pos)
-{
-    MSIQUERY *view;
-    MSIRECORD *rec;
-    UINT r;
-    static const WCHAR query_patch[] = {
-        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
-        'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
-        'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
-
-    r = MSI_DatabaseOpenViewW( db, query_patch, &view );
-    if (r != ERROR_SUCCESS)
-        return;
-
-    rec = MSI_CreateRecord( 1 );
-    MSI_RecordSetInteger(rec, 1, last_sequence);
-
-    r = MSI_ViewExecute( view, rec );
-    msiobj_release( &rec->hdr );
-    if (r != ERROR_SUCCESS)
-        return;
-
-    while (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
-    {
-        UINT sequence = MSI_RecordGetInteger( rec, 2 );
-
-        /* FIXME:
-         * We only use the max/min sequence numbers for now.
-         */
-
-        pos->min = min(pos->min, sequence);
-        pos->max = max(pos->max, sequence);
-        pos->count++;
-
-        msiobj_release( &rec->hdr );
-    }
-
-    msiobj_release( &view->hdr );
-}
-
-static void msi_patch_offset_get_files(MSIDATABASE *db, UINT last_sequence, struct msi_patch_offset_list *pos)
-{
-    MSIQUERY *view;
-    MSIRECORD *rec;
-    UINT r;
-    static const WCHAR query_files[] = {
-        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
-        'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
-        'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
-
-    r = MSI_DatabaseOpenViewW( db, query_files, &view );
-    if (r != ERROR_SUCCESS)
-        return;
-
-    rec = MSI_CreateRecord( 1 );
-    MSI_RecordSetInteger(rec, 1, last_sequence);
-
-    r = MSI_ViewExecute( view, rec );
-    msiobj_release( &rec->hdr );
-    if (r != ERROR_SUCCESS)
-        return;
-
-    while (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
-    {
-        UINT attributes = MSI_RecordGetInteger( rec, 7 );
-        if (attributes & msidbFileAttributesPatchAdded)
-        {
-            struct msi_patch_offset *po = msi_alloc(sizeof(struct msi_patch_offset));
-
-            po->Name = msi_dup_record_field( rec, 1 );
-            po->Sequence = MSI_RecordGetInteger( rec, 8 );
-
-            pos->min = min(pos->min, po->Sequence);
-            pos->max = max(pos->max, po->Sequence);
-
-            list_add_tail( &pos->files, &po->entry );
-            pos->count++;
-        }
-        msiobj_release( &rec->hdr );
-    }
-
-    msiobj_release( &view->hdr );
-}
-
-static UINT msi_patch_offset_modify_db(MSIDATABASE *db, struct msi_patch_offset_list *pos)
-{
-    static const WCHAR query_files[] =
-        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
-         'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','>','=',' ','?',' ',
-         'A','N','D',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
-         'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
-    struct msi_patch_offset *po;
-    MSIQUERY *view;
-    MSIRECORD *rec;
-    UINT r;
-
-    r = MSI_DatabaseOpenViewW( db, query_files, &view );
-    if (r != ERROR_SUCCESS)
-        return ERROR_SUCCESS;
-
-    rec = MSI_CreateRecord( 2 );
-    MSI_RecordSetInteger( rec, 1, pos->min );
-    MSI_RecordSetInteger( rec, 2, pos->max );
-
-    r = MSI_ViewExecute( view, rec );
-    msiobj_release( &rec->hdr );
-    if (r != ERROR_SUCCESS)
-        goto done;
-
-    LIST_FOR_EACH_ENTRY( po, &pos->files, struct msi_patch_offset, entry )
-    {
-        UINT r_fetch;
-        while ( (r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS )
-        {
-            LPCWSTR file = MSI_RecordGetString( rec, 1 );
-            UINT seq;
-
-            if (!strcmpiW(file, po->Name))
-            {
-                /* Update record */
-                seq = MSI_RecordGetInteger( rec, 8 );
-                MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
-                r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
-                if (r != ERROR_SUCCESS)
-                    ERR("Failed to update offset for file %s.\n", debugstr_w(file));
-
-                msiobj_release( &rec->hdr );
-                break;
-            }
-
-            msiobj_release( &rec->hdr );
-        }
-
-        if (r_fetch != ERROR_SUCCESS)
-            break;
-    }
-
-done:
-    msiobj_release( &view->hdr );
-
-    return ERROR_SUCCESS;
-}
-
-static const WCHAR patch_media_query[] = {
-    'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
-    'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
-    'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
-    'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
-
-struct patch_media
-{
-    struct list entry;
-    UINT    disk_id;
-    UINT    last_sequence;
-    WCHAR  *prompt;
-    WCHAR  *cabinet;
-    WCHAR  *volume;
-    WCHAR  *source;
-};
-
-static UINT msi_add_patch_media( MSIPACKAGE *package, IStorage *patch )
-{
-    static const WCHAR delete_query[] = {
-        'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
-        'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
-    static const WCHAR insert_query[] = {
-        'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
-        '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
-        '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
-        '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
-        'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
-    MSIQUERY *view;
-    MSIRECORD *rec = NULL;
-    UINT r, disk_id;
-    struct list media_list;
-    struct patch_media *media, *next;
-
-    r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view );
-    if (r != ERROR_SUCCESS) return r;
-
-    r = MSI_ViewExecute( view, 0 );
-    if (r != ERROR_SUCCESS)
-    {
-        msiobj_release( &view->hdr );
-        TRACE("query failed %u\n", r);
-        return r;
-    }
-
-    list_init( &media_list );
-    while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
-    {
-        disk_id = MSI_RecordGetInteger( rec, 1 );
-        TRACE("disk_id %u\n", disk_id);
-        if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
-        {
-            msiobj_release( &rec->hdr );
-            continue;
-        }
-        if (!(media = msi_alloc( sizeof( *media )))) goto done;
-        media->disk_id = disk_id;
-        media->last_sequence = MSI_RecordGetInteger( rec, 2 );
-        media->prompt  = msi_dup_record_field( rec, 3 );
-        media->cabinet = msi_dup_record_field( rec, 4 );
-        media->volume  = msi_dup_record_field( rec, 5 );
-        media->source  = msi_dup_record_field( rec, 6 );
-
-        list_add_tail( &media_list, &media->entry );
-        msiobj_release( &rec->hdr );
-    }
-    LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry )
-    {
-        MSIQUERY *delete_view, *insert_view;
-
-        r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view );
-        if (r != ERROR_SUCCESS) goto done;
-
-        rec = MSI_CreateRecord( 1 );
-        MSI_RecordSetInteger( rec, 1, media->disk_id );
-
-        r = MSI_ViewExecute( delete_view, rec );
-        msiobj_release( &delete_view->hdr );
-        msiobj_release( &rec->hdr );
-        if (r != ERROR_SUCCESS) goto done;
-
-        r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view );
-        if (r != ERROR_SUCCESS) goto done;
-
-        disk_id = package->db->media_transform_disk_id;
-        TRACE("disk id       %u\n", disk_id);
-        TRACE("last sequence %u\n", media->last_sequence);
-        TRACE("prompt        %s\n", debugstr_w(media->prompt));
-        TRACE("cabinet       %s\n", debugstr_w(media->cabinet));
-        TRACE("volume        %s\n", debugstr_w(media->volume));
-        TRACE("source        %s\n", debugstr_w(media->source));
-
-        rec = MSI_CreateRecord( 6 );
-        MSI_RecordSetInteger( rec, 1, disk_id );
-        MSI_RecordSetInteger( rec, 2, media->last_sequence );
-        MSI_RecordSetStringW( rec, 3, media->prompt );
-        MSI_RecordSetStringW( rec, 4, media->cabinet );
-        MSI_RecordSetStringW( rec, 5, media->volume );
-        MSI_RecordSetStringW( rec, 6, media->source );
-
-        r = MSI_ViewExecute( insert_view, rec );
-        msiobj_release( &insert_view->hdr );
-        msiobj_release( &rec->hdr );
-        if (r != ERROR_SUCCESS) goto done;
-
-        r = msi_add_cabinet_stream( package, disk_id, patch, media->cabinet );
-        if (r != ERROR_SUCCESS) WARN("failed to add cabinet stream %u\n", r);
-        package->db->media_transform_disk_id++;
-    }
-
-done:
-    msiobj_release( &view->hdr );
-    LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry )
-    {
-        list_remove( &media->entry );
-        msi_free( media->prompt );
-        msi_free( media->cabinet );
-        msi_free( media->volume );
-        msi_free( media->source );
-        msi_free( media );
-    }
-    return r;
-}
-
-static UINT msi_set_patch_offsets(MSIDATABASE *db)
-{
-    MSIQUERY *view;
-    MSIRECORD *rec = NULL;
-    UINT r;
-
-    r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
-    if (r != ERROR_SUCCESS)
-        return r;
-
-    r = MSI_ViewExecute( view, 0 );
-    if (r != ERROR_SUCCESS)
-        goto done;
-
-    while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
-    {
-        UINT last_sequence = MSI_RecordGetInteger( rec, 2 );
-        struct msi_patch_offset_list *pos;
-
-        /* FIXME: Set/Check Source field instead? */
-        if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
-        {
-            msiobj_release( &rec->hdr );
-            continue;
-        }
-
-        pos = msi_patch_offset_list_create();
-
-        msi_patch_offset_get_files( db, last_sequence, pos );
-        msi_patch_offset_get_patches( db, last_sequence, pos );
-
-        if (pos->count)
-        {
-            UINT offset = db->media_transform_offset - pos->min;
-            last_sequence = offset + pos->max;
-
-            /* FIXME:
-             * This is for the patch table, which is not yet properly transformed.
-             */
-            last_sequence += pos->min;
-
-            pos->offset_to_apply = offset;
-            msi_patch_offset_modify_db( db, pos );
-
-            MSI_RecordSetInteger( rec, 2, last_sequence );
-            r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
-            if (r != ERROR_SUCCESS)
-                ERR("Failed to update Media table entry, expect breakage (%u).\n", r);
-
-            db->media_transform_offset = last_sequence + 1;
-        }
-
-        msi_patch_offset_list_free( pos );
-        msiobj_release( &rec->hdr );
-    }
-
-done:
-    msiobj_release( &view->hdr );
-    return r;
-}
-
-UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
-{
-    UINT i, r = ERROR_SUCCESS;
-    WCHAR **substorage;
-
-    /* apply substorage transforms */
-    substorage = msi_split_string( patch->transforms, ';' );
-    for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
-    {
-        r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
-        if (r == ERROR_SUCCESS)
-        {
-            msi_add_patch_media( package, patch_db->storage );
-            msi_set_patch_offsets( package->db );
-        }
-    }
-
-    msi_free( substorage );
-    if (r != ERROR_SUCCESS)
-        return r;
-
-    msi_set_media_source_prop( package );
-
-    patch->state = MSIPATCHSTATE_APPLIED;
-    list_add_tail( &package->patches, &patch->entry );
-    return ERROR_SUCCESS;
-}
-
-static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
-{
-    static const WCHAR dotmsp[] = {'.','m','s','p',0};
-    MSIDATABASE *patch_db = NULL;
-    WCHAR localfile[MAX_PATH];
-    MSISUMMARYINFO *si;
-    MSIPATCHINFO *patch = NULL;
-    UINT r = ERROR_SUCCESS;
-
-    TRACE("%p %s\n", package, debugstr_w( file ) );
-
-    r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
-    if ( r != ERROR_SUCCESS )
-    {
-        ERR("failed to open patch collection %s\n", debugstr_w( file ) );
-        return r;
-    }
-
-    si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
-    if (!si)
-    {
-        msiobj_release( &patch_db->hdr );
-        return ERROR_FUNCTION_FAILED;
-    }
-
-    r = msi_check_patch_applicable( package, si );
-    if (r != ERROR_SUCCESS)
-    {
-        TRACE("patch not applicable\n");
-        r = ERROR_SUCCESS;
-        goto done;
-    }
-
-    r = msi_parse_patch_summary( si, &patch );
-    if ( r != ERROR_SUCCESS )
-        goto done;
-
-    r = msi_get_local_package_name( localfile, dotmsp );
-    if ( r != ERROR_SUCCESS )
-        goto done;
-
-    TRACE("copying to local package %s\n", debugstr_w(localfile));
-
-    if (!CopyFileW( file, localfile, FALSE ))
-    {
-        ERR("Unable to copy package (%s -> %s) (error %u)\n",
-            debugstr_w(file), debugstr_w(localfile), GetLastError());
-        r = GetLastError();
-        goto done;
-    }
-    patch->localfile = strdupW( localfile );
-
-    r = msi_apply_patch_db( package, patch_db, patch );
-    if ( r != ERROR_SUCCESS )
-        WARN("patch failed to apply %u\n", r);
-
-done:
-    msiobj_release( &si->hdr );
-    msiobj_release( &patch_db->hdr );
-    if (patch && r != ERROR_SUCCESS)
-    {
-        if (patch->localfile)
-            DeleteFileW( patch->localfile );
-
-        msi_free( patch->patchcode );
-        msi_free( patch->transforms );
-        msi_free( patch->localfile );
-        msi_free( patch );
-    }
-    return r;
-}
-
-/* get the PATCH property, and apply all the patches it specifies */
-static UINT msi_apply_patches( MSIPACKAGE *package )
-{
-    LPWSTR patch_list, *patches;
-    UINT i, r = ERROR_SUCCESS;
-
-    patch_list = msi_dup_property( package->db, szPatch );
-
-    TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
-
-    patches = msi_split_string( patch_list, ';' );
-    for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
-        r = msi_apply_patch_package( package, patches[i] );
-
-    msi_free( patches );
-    msi_free( patch_list );
-
-    return r;
-}
-
-static UINT msi_apply_transforms( MSIPACKAGE *package )
-{
-    static const WCHAR szTransforms[] = {
-        'T','R','A','N','S','F','O','R','M','S',0 };
-    LPWSTR xform_list, *xforms;
-    UINT i, r = ERROR_SUCCESS;
-
-    xform_list = msi_dup_property( package->db, szTransforms );
-    xforms = msi_split_string( xform_list, ';' );
-
-    for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
-    {
-        if (xforms[i][0] == ':')
-            r = msi_apply_substorage_transform( package, package->db, xforms[i] );
-        else
-        {
-            WCHAR *transform;
-
-            if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
-            else
-            {
-                WCHAR *p = strrchrW( package->PackagePath, '\\' );
-                DWORD len = p - package->PackagePath + 1;
-
-                if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
-                {
-                    msi_free( xforms );
-                    msi_free( xform_list );
-                    return ERROR_OUTOFMEMORY;
-                }
-                memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
-                memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
-            }
-            r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
-            if (transform != xforms[i]) msi_free( transform );
-        }
-    }
-
-    msi_free( xforms );
-    msi_free( xform_list );
-
-    return r;
-}
-
 static BOOL ui_sequence_exists( MSIPACKAGE *package )
 {
     MSIQUERY *view;
@@ -4916,12 +4171,6 @@ done:
     return r;
 }
 
-/*
- * 99% of the work done here is only done for 
- * advertised installs. However this is where the
- * Icon table is processed and written out
- * so that is what I am going to do here.
- */
 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
 {
     UINT rc;
diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h
index 26c2c4f..acef756 100644
--- a/dlls/msi/msipriv.h
+++ b/dlls/msi/msipriv.h
@@ -755,11 +755,13 @@ extern UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg ) DECLSPEC
 extern UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db,
                  LPCWSTR szTransformFile, int iErrorCond ) DECLSPEC_HIDDEN;
 extern void append_storage_to_db( MSIDATABASE *db, IStorage *stg ) DECLSPEC_HIDDEN;
+extern UINT msi_apply_transforms( MSIPACKAGE *package ) DECLSPEC_HIDDEN;
 
 /* patch functions */
 extern UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si ) DECLSPEC_HIDDEN;
 extern UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch ) DECLSPEC_HIDDEN;
 extern UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch ) DECLSPEC_HIDDEN;
+extern UINT msi_apply_patches( MSIPACKAGE *package ) DECLSPEC_HIDDEN;
 
 /* action internals */
 extern UINT MSI_InstallPackage( MSIPACKAGE *, LPCWSTR, LPCWSTR ) DECLSPEC_HIDDEN;
diff --git a/dlls/msi/patch.c b/dlls/msi/patch.c
new file mode 100644
index 0000000..00281a8
--- /dev/null
+++ b/dlls/msi/patch.c
@@ -0,0 +1,719 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2004,2005 Aric Stewart for CodeWeavers
+ * Copyright 2011 Hans Leidekker for CodeWeavers
+ *
+ * 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 <stdarg.h>
+#define COBJMACROS
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "objbase.h"
+#include "shlwapi.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "msipriv.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+static UINT check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
+{
+    static const WCHAR szSystemLanguageID[] = {
+        'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0};
+    LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
+    UINT ret = ERROR_FUNCTION_FAILED;
+
+    prod_code = msi_dup_property( package->db, szProductCode );
+    patch_product = msi_get_suminfo_product( patch );
+
+    TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
+
+    if (strstrW( patch_product, prod_code ))
+    {
+        MSISUMMARYINFO *si;
+        const WCHAR *p;
+
+        si = MSI_GetSummaryInformationW( patch, 0 );
+        if (!si)
+        {
+            ERR("no summary information!\n");
+            goto end;
+        }
+        template = msi_suminfo_dup_string( si, PID_TEMPLATE );
+        if (!template)
+        {
+            ERR("no template property!\n");
+            msiobj_release( &si->hdr );
+            goto end;
+        }
+        if (!template[0])
+        {
+            ret = ERROR_SUCCESS;
+            msiobj_release( &si->hdr );
+            goto end;
+        }
+        langid = msi_dup_property( package->db, szSystemLanguageID );
+        if (!langid)
+        {
+            msiobj_release( &si->hdr );
+            goto end;
+        }
+        p = strchrW( template, ';' );
+        if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, szZero )))
+        {
+            TRACE("applicable transform\n");
+            ret = ERROR_SUCCESS;
+        }
+        /* FIXME: check platform */
+        msiobj_release( &si->hdr );
+    }
+
+end:
+    msi_free( patch_product );
+    msi_free( prod_code );
+    msi_free( template );
+    msi_free( langid );
+    return ret;
+}
+
+static UINT apply_substorage_transform( MSIPACKAGE *package, MSIDATABASE *patch_db, LPCWSTR name )
+{
+    UINT ret = ERROR_FUNCTION_FAILED;
+    IStorage *stg = NULL;
+    HRESULT r;
+
+    TRACE("%p %s\n", package, debugstr_w(name));
+
+    if (*name++ != ':')
+    {
+        ERR("expected a colon in %s\n", debugstr_w(name));
+        return ERROR_FUNCTION_FAILED;
+    }
+    r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
+    if (SUCCEEDED(r))
+    {
+        ret = check_transform_applicable( package, stg );
+        if (ret == ERROR_SUCCESS)
+            msi_table_apply_transform( package->db, stg );
+        else
+            TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
+        IStorage_Release( stg );
+    }
+    else
+    {
+        ERR("failed to open substorage %s\n", debugstr_w(name));
+    }
+    return ERROR_SUCCESS;
+}
+
+UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
+{
+    LPWSTR guid_list, *guids, product_code;
+    UINT i, ret = ERROR_FUNCTION_FAILED;
+
+    product_code = msi_dup_property( package->db, szProductCode );
+    if (!product_code)
+    {
+        /* FIXME: the property ProductCode should be written into the DB somewhere */
+        ERR("no product code to check\n");
+        return ERROR_SUCCESS;
+    }
+    guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
+    guids = msi_split_string( guid_list, ';' );
+    for (i = 0; guids[i] && ret != ERROR_SUCCESS; i++)
+    {
+        if (!strcmpW( guids[i], product_code )) ret = ERROR_SUCCESS;
+    }
+    msi_free( guids );
+    msi_free( guid_list );
+    msi_free( product_code );
+    return ret;
+}
+
+UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
+{
+    MSIPATCHINFO *pi;
+    UINT r = ERROR_SUCCESS;
+    WCHAR *p;
+
+    if (!(pi = msi_alloc_zero( sizeof(MSIPATCHINFO) )))
+    {
+        return ERROR_OUTOFMEMORY;
+    }
+    if (!(pi->patchcode = msi_suminfo_dup_string( si, PID_REVNUMBER )))
+    {
+        msi_free( pi );
+        return ERROR_OUTOFMEMORY;
+    }
+    p = pi->patchcode;
+    if (*p != '{')
+    {
+        msi_free( pi->patchcode );
+        msi_free( pi );
+        return ERROR_PATCH_PACKAGE_INVALID;
+    }
+    if (!(p = strchrW( p + 1, '}' )))
+    {
+        msi_free( pi->patchcode );
+        msi_free( pi );
+        return ERROR_PATCH_PACKAGE_INVALID;
+    }
+    if (p[1])
+    {
+        FIXME("patch obsoletes %s\n", debugstr_w(p + 1));
+        p[1] = 0;
+    }
+    TRACE("patch code %s\n", debugstr_w(pi->patchcode));
+
+    if (!(pi->transforms = msi_suminfo_dup_string( si, PID_LASTAUTHOR )))
+    {
+        msi_free( pi->patchcode );
+        msi_free( pi );
+        return ERROR_OUTOFMEMORY;
+    }
+    *patch = pi;
+    return r;
+}
+
+static UINT patch_set_media_source_prop( MSIPACKAGE *package )
+{
+    static const WCHAR query[] = {
+        'S','E','L','E','C','T',' ','`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
+        '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ',
+        'I','S',' ','N','O','T',' ','N','U','L','L',0};
+    MSIQUERY *view;
+    MSIRECORD *rec;
+    const WCHAR *property;
+    WCHAR *patch;
+    UINT r;
+
+    r = MSI_DatabaseOpenViewW( package->db, query, &view );
+    if (r != ERROR_SUCCESS)
+        return r;
+
+    r = MSI_ViewExecute( view, 0 );
+    if (r != ERROR_SUCCESS)
+        goto done;
+
+    if (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
+    {
+        property = MSI_RecordGetString( rec, 1 );
+        patch = msi_dup_property( package->db, szPatch );
+        msi_set_property( package->db, property, patch );
+        msi_free( patch );
+        msiobj_release( &rec->hdr );
+    }
+
+done:
+    msiobj_release( &view->hdr );
+    return r;
+}
+
+struct patch_offset
+{
+    struct list entry;
+    WCHAR *name;
+    UINT sequence;
+};
+
+struct patch_offset_list
+{
+    struct list files;
+    UINT count, min, max;
+    UINT offset_to_apply;
+};
+
+static struct patch_offset_list *patch_offset_list_create( void )
+{
+    struct patch_offset_list *pos = msi_alloc( sizeof(struct patch_offset_list) );
+    list_init( &pos->files );
+    pos->count = pos->max = 0;
+    pos->min = 999999;
+    return pos;
+}
+
+static void patch_offset_list_free( struct patch_offset_list *pos )
+{
+    struct patch_offset *po, *po2;
+
+    LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct patch_offset, entry )
+    {
+        msi_free( po->name );
+        msi_free( po );
+    }
+    msi_free( pos );
+}
+
+static void patch_offset_get_patches( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
+{
+    static const WCHAR query_patch[] = {
+        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ',
+        'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
+        'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
+    MSIQUERY *view;
+    MSIRECORD *rec;
+    UINT r;
+
+    r = MSI_DatabaseOpenViewW( db, query_patch, &view );
+    if (r != ERROR_SUCCESS)
+        return;
+
+    rec = MSI_CreateRecord( 1 );
+    MSI_RecordSetInteger( rec, 1, last_sequence );
+
+    r = MSI_ViewExecute( view, rec );
+    msiobj_release( &rec->hdr );
+    if (r != ERROR_SUCCESS)
+        return;
+
+    while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
+    {
+        UINT sequence = MSI_RecordGetInteger( rec, 2 );
+
+        /* FIXME: we only use the max/min sequence numbers for now */
+        pos->min = min( pos->min, sequence );
+        pos->max = max( pos->max, sequence );
+        pos->count++;
+        msiobj_release( &rec->hdr );
+    }
+    msiobj_release( &view->hdr );
+}
+
+static void patch_offset_get_files( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos )
+{
+    static const WCHAR query_files[] = {
+        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
+        'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
+        'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
+    MSIQUERY *view;
+    MSIRECORD *rec;
+    UINT r;
+
+    r = MSI_DatabaseOpenViewW( db, query_files, &view );
+    if (r != ERROR_SUCCESS)
+        return;
+
+    rec = MSI_CreateRecord( 1 );
+    MSI_RecordSetInteger( rec, 1, last_sequence );
+
+    r = MSI_ViewExecute( view, rec );
+    msiobj_release( &rec->hdr );
+    if (r != ERROR_SUCCESS)
+        return;
+
+    while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
+    {
+        UINT attributes = MSI_RecordGetInteger( rec, 7 );
+        if (attributes & msidbFileAttributesPatchAdded)
+        {
+            struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) );
+
+            po->name     = msi_dup_record_field( rec, 1 );
+            po->sequence = MSI_RecordGetInteger( rec, 8 );
+            pos->min     = min( pos->min, po->sequence );
+            pos->max     = max( pos->max, po->sequence );
+            list_add_tail( &pos->files, &po->entry );
+            pos->count++;
+        }
+        msiobj_release( &rec->hdr );
+    }
+    msiobj_release( &view->hdr );
+}
+
+static UINT patch_offset_modify_db( MSIDATABASE *db, struct patch_offset_list *pos )
+{
+    static const WCHAR query_files[] = {
+        'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ',
+        'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','>','=',' ','?',' ',
+        'A','N','D',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ',
+        'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0};
+    struct patch_offset *po;
+    MSIRECORD *rec;
+    MSIQUERY *view;
+    UINT r;
+
+    r = MSI_DatabaseOpenViewW( db, query_files, &view );
+    if (r != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rec = MSI_CreateRecord( 2 );
+    MSI_RecordSetInteger( rec, 1, pos->min );
+    MSI_RecordSetInteger( rec, 2, pos->max );
+
+    r = MSI_ViewExecute( view, rec );
+    msiobj_release( &rec->hdr );
+    if (r != ERROR_SUCCESS)
+        goto done;
+
+    LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry )
+    {
+        UINT r_fetch;
+        while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS)
+        {
+            const WCHAR *file = MSI_RecordGetString( rec, 1 );
+            UINT seq;
+
+            if (!strcmpiW( file, po->name ))
+            {
+                /* update record */
+                seq = MSI_RecordGetInteger( rec, 8 );
+                MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply );
+                r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
+                if (r != ERROR_SUCCESS)
+                    ERR("Failed to update offset for file %s\n", debugstr_w(file));
+                msiobj_release( &rec->hdr );
+                break;
+            }
+            msiobj_release( &rec->hdr );
+        }
+        if (r_fetch != ERROR_SUCCESS) break;
+    }
+
+done:
+    msiobj_release( &view->hdr );
+    return ERROR_SUCCESS;
+}
+
+static const WCHAR patch_media_query[] = {
+    'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
+    'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
+    'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ',
+    'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0};
+
+struct patch_media
+{
+    struct list entry;
+    UINT    disk_id;
+    UINT    last_sequence;
+    WCHAR  *prompt;
+    WCHAR  *cabinet;
+    WCHAR  *volume;
+    WCHAR  *source;
+};
+
+static UINT add_patch_media( MSIPACKAGE *package, IStorage *patch )
+{
+    static const WCHAR delete_query[] = {
+        'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ',
+        'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0};
+    static const WCHAR insert_query[] = {
+        'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ',
+        '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',',
+        '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',',
+        '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ',
+        'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0};
+    MSIQUERY *view;
+    MSIRECORD *rec;
+    UINT r, disk_id;
+    struct list media_list;
+    struct patch_media *media, *next;
+
+    r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view );
+    if (r != ERROR_SUCCESS) return r;
+
+    r = MSI_ViewExecute( view, 0 );
+    if (r != ERROR_SUCCESS)
+    {
+        msiobj_release( &view->hdr );
+        TRACE("query failed %u\n", r);
+        return r;
+    }
+    list_init( &media_list );
+    while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
+    {
+        disk_id = MSI_RecordGetInteger( rec, 1 );
+        TRACE("disk_id %u\n", disk_id);
+        if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
+        {
+            msiobj_release( &rec->hdr );
+            continue;
+        }
+        if (!(media = msi_alloc( sizeof( *media )))) goto done;
+        media->disk_id = disk_id;
+        media->last_sequence = MSI_RecordGetInteger( rec, 2 );
+        media->prompt  = msi_dup_record_field( rec, 3 );
+        media->cabinet = msi_dup_record_field( rec, 4 );
+        media->volume  = msi_dup_record_field( rec, 5 );
+        media->source  = msi_dup_record_field( rec, 6 );
+
+        list_add_tail( &media_list, &media->entry );
+        msiobj_release( &rec->hdr );
+    }
+    LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry )
+    {
+        MSIQUERY *delete_view, *insert_view;
+
+        r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view );
+        if (r != ERROR_SUCCESS) goto done;
+
+        rec = MSI_CreateRecord( 1 );
+        MSI_RecordSetInteger( rec, 1, media->disk_id );
+
+        r = MSI_ViewExecute( delete_view, rec );
+        msiobj_release( &delete_view->hdr );
+        msiobj_release( &rec->hdr );
+        if (r != ERROR_SUCCESS) goto done;
+
+        r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view );
+        if (r != ERROR_SUCCESS) goto done;
+
+        disk_id = package->db->media_transform_disk_id;
+        TRACE("disk id       %u\n", disk_id);
+        TRACE("last sequence %u\n", media->last_sequence);
+        TRACE("prompt        %s\n", debugstr_w(media->prompt));
+        TRACE("cabinet       %s\n", debugstr_w(media->cabinet));
+        TRACE("volume        %s\n", debugstr_w(media->volume));
+        TRACE("source        %s\n", debugstr_w(media->source));
+
+        rec = MSI_CreateRecord( 6 );
+        MSI_RecordSetInteger( rec, 1, disk_id );
+        MSI_RecordSetInteger( rec, 2, media->last_sequence );
+        MSI_RecordSetStringW( rec, 3, media->prompt );
+        MSI_RecordSetStringW( rec, 4, media->cabinet );
+        MSI_RecordSetStringW( rec, 5, media->volume );
+        MSI_RecordSetStringW( rec, 6, media->source );
+
+        r = MSI_ViewExecute( insert_view, rec );
+        msiobj_release( &insert_view->hdr );
+        msiobj_release( &rec->hdr );
+        if (r != ERROR_SUCCESS) goto done;
+
+        r = msi_add_cabinet_stream( package, disk_id, patch, media->cabinet );
+        if (r != ERROR_SUCCESS) WARN("failed to add cabinet stream %u\n", r);
+        package->db->media_transform_disk_id++;
+    }
+
+done:
+    msiobj_release( &view->hdr );
+    LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry )
+    {
+        list_remove( &media->entry );
+        msi_free( media->prompt );
+        msi_free( media->cabinet );
+        msi_free( media->volume );
+        msi_free( media->source );
+        msi_free( media );
+    }
+    return r;
+}
+
+static UINT set_patch_offsets( MSIDATABASE *db )
+{
+    MSIQUERY *view;
+    MSIRECORD *rec;
+    UINT r;
+
+    r = MSI_DatabaseOpenViewW( db, patch_media_query, &view );
+    if (r != ERROR_SUCCESS)
+        return r;
+
+    r = MSI_ViewExecute( view, 0 );
+    if (r != ERROR_SUCCESS)
+        goto done;
+
+    while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS)
+    {
+        UINT last_sequence = MSI_RecordGetInteger( rec, 2 );
+        struct patch_offset_list *pos;
+
+        /* FIXME: set/check Source field instead? */
+        if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET)
+        {
+            msiobj_release( &rec->hdr );
+            continue;
+        }
+        pos = patch_offset_list_create();
+        patch_offset_get_files( db, last_sequence, pos );
+        patch_offset_get_patches( db, last_sequence, pos );
+        if (pos->count)
+        {
+            UINT offset = db->media_transform_offset - pos->min;
+            last_sequence = offset + pos->max;
+
+            /* FIXME: this is for the patch table, which is not yet properly transformed */
+            last_sequence += pos->min;
+            pos->offset_to_apply = offset;
+            patch_offset_modify_db( db, pos );
+
+            MSI_RecordSetInteger( rec, 2, last_sequence );
+            r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec );
+            if (r != ERROR_SUCCESS)
+                ERR("Failed to update Media table entry, expect breakage (%u)\n", r);
+            db->media_transform_offset = last_sequence + 1;
+        }
+        patch_offset_list_free( pos );
+        msiobj_release( &rec->hdr );
+    }
+
+done:
+    msiobj_release( &view->hdr );
+    return r;
+}
+
+UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
+{
+    UINT i, r = ERROR_SUCCESS;
+    WCHAR **substorage;
+
+    /* apply substorage transforms */
+    substorage = msi_split_string( patch->transforms, ';' );
+    for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
+    {
+        r = apply_substorage_transform( package, patch_db, substorage[i] );
+        if (r == ERROR_SUCCESS)
+        {
+            add_patch_media( package, patch_db->storage );
+            set_patch_offsets( package->db );
+        }
+    }
+    msi_free( substorage );
+    if (r != ERROR_SUCCESS)
+        return r;
+
+    patch_set_media_source_prop( package );
+
+    patch->state = MSIPATCHSTATE_APPLIED;
+    list_add_tail( &package->patches, &patch->entry );
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_apply_patch_package( MSIPACKAGE *package, const WCHAR *file )
+{
+    static const WCHAR dotmsp[] = {'.','m','s','p',0};
+    MSIDATABASE *patch_db = NULL;
+    WCHAR localfile[MAX_PATH];
+    MSISUMMARYINFO *si;
+    MSIPATCHINFO *patch = NULL;
+    UINT r = ERROR_SUCCESS;
+
+    TRACE("%p %s\n", package, debugstr_w(file));
+
+    r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &patch_db );
+    if (r != ERROR_SUCCESS)
+    {
+        ERR("failed to open patch collection %s\n", debugstr_w( file ) );
+        return r;
+    }
+    if (!(si = MSI_GetSummaryInformationW( patch_db->storage, 0 )))
+    {
+        msiobj_release( &patch_db->hdr );
+        return ERROR_FUNCTION_FAILED;
+    }
+    r = msi_check_patch_applicable( package, si );
+    if (r != ERROR_SUCCESS)
+    {
+        TRACE("patch not applicable\n");
+        r = ERROR_SUCCESS;
+        goto done;
+    }
+    r = msi_parse_patch_summary( si, &patch );
+    if ( r != ERROR_SUCCESS )
+        goto done;
+
+    r = msi_get_local_package_name( localfile, dotmsp );
+    if ( r != ERROR_SUCCESS )
+        goto done;
+
+    TRACE("copying to local package %s\n", debugstr_w(localfile));
+
+    if (!CopyFileW( file, localfile, FALSE ))
+    {
+        ERR("Unable to copy package (%s -> %s) (error %u)\n",
+            debugstr_w(file), debugstr_w(localfile), GetLastError());
+        r = GetLastError();
+        goto done;
+    }
+    patch->localfile = strdupW( localfile );
+
+    r = msi_apply_patch_db( package, patch_db, patch );
+    if (r != ERROR_SUCCESS) WARN("patch failed to apply %u\n", r);
+
+done:
+    msiobj_release( &si->hdr );
+    msiobj_release( &patch_db->hdr );
+    if (patch && r != ERROR_SUCCESS)
+    {
+        if (patch->localfile) DeleteFileW( patch->localfile );
+        msi_free( patch->patchcode );
+        msi_free( patch->transforms );
+        msi_free( patch->localfile );
+        msi_free( patch );
+    }
+    return r;
+}
+
+/* get the PATCH property, and apply all the patches it specifies */
+UINT msi_apply_patches( MSIPACKAGE *package )
+{
+    LPWSTR patch_list, *patches;
+    UINT i, r = ERROR_SUCCESS;
+
+    patch_list = msi_dup_property( package->db, szPatch );
+
+    TRACE("patches to be applied: %s\n", debugstr_w(patch_list));
+
+    patches = msi_split_string( patch_list, ';' );
+    for (i = 0; patches && patches[i] && r == ERROR_SUCCESS; i++)
+        r = msi_apply_patch_package( package, patches[i] );
+
+    msi_free( patches );
+    msi_free( patch_list );
+    return r;
+}
+
+UINT msi_apply_transforms( MSIPACKAGE *package )
+{
+    static const WCHAR szTransforms[] = {'T','R','A','N','S','F','O','R','M','S',0};
+    LPWSTR xform_list, *xforms;
+    UINT i, r = ERROR_SUCCESS;
+
+    xform_list = msi_dup_property( package->db, szTransforms );
+    xforms = msi_split_string( xform_list, ';' );
+
+    for (i = 0; xforms && xforms[i] && r == ERROR_SUCCESS; i++)
+    {
+        if (xforms[i][0] == ':')
+            r = apply_substorage_transform( package, package->db, xforms[i] );
+        else
+        {
+            WCHAR *transform;
+
+            if (!PathIsRelativeW( xforms[i] )) transform = xforms[i];
+            else
+            {
+                WCHAR *p = strrchrW( package->PackagePath, '\\' );
+                DWORD len = p - package->PackagePath + 1;
+
+                if (!(transform = msi_alloc( (len + strlenW( xforms[i] ) + 1) * sizeof(WCHAR)) ))
+                {
+                    msi_free( xforms );
+                    msi_free( xform_list );
+                    return ERROR_OUTOFMEMORY;
+                }
+                memcpy( transform, package->PackagePath, len * sizeof(WCHAR) );
+                memcpy( transform + len, xforms[i], (strlenW( xforms[i] ) + 1) * sizeof(WCHAR) );
+            }
+            r = MSI_DatabaseApplyTransformW( package->db, transform, 0 );
+            if (transform != xforms[i]) msi_free( transform );
+        }
+    }
+    msi_free( xforms );
+    msi_free( xform_list );
+    return r;
+}
-- 
1.7.4.1







More information about the wine-patches mailing list