diff --git a/patches/wusa-MSU_Package_Installer/0001-wusa-Implement-basic-installation-logic.patch b/patches/wusa-MSU_Package_Installer/0001-wusa-Implement-basic-installation-logic.patch
deleted file mode 100644
index b5e84a80..00000000
--- a/patches/wusa-MSU_Package_Installer/0001-wusa-Implement-basic-installation-logic.patch
+++ /dev/null
@@ -1,1973 +0,0 @@
-From 0ed6209063d3c679af561e16cc6b716412842640 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
-Date: Mon, 14 Dec 2015 00:39:54 +0100
-Subject: [PATCH] wusa: Implement basic installation logic.
-
----
- programs/wusa/Makefile.in |    4 +-
- programs/wusa/main.c      | 1043 ++++++++++++++++++++++++++++++++++++-
- programs/wusa/manifest.c  |  704 +++++++++++++++++++++++++
- programs/wusa/wusa.h      |  159 ++++++
- 4 files changed, 1904 insertions(+), 6 deletions(-)
- create mode 100644 programs/wusa/manifest.c
- create mode 100644 programs/wusa/wusa.h
-
-diff --git a/programs/wusa/Makefile.in b/programs/wusa/Makefile.in
-index 3042e86bf85..bf902734114 100644
---- a/programs/wusa/Makefile.in
-+++ b/programs/wusa/Makefile.in
-@@ -1,6 +1,8 @@
- MODULE    = wusa.exe
- 
- EXTRADLLFLAGS = -mconsole -municode -mno-cygwin
-+IMPORTS   = cabinet shlwapi ole32 oleaut32 advapi32
- 
- C_SRCS = \
--	main.c
-+	main.c \
-+	manifest.c
-diff --git a/programs/wusa/main.c b/programs/wusa/main.c
-index c3e3dfd38ce..894184b5dfb 100644
---- a/programs/wusa/main.c
-+++ b/programs/wusa/main.c
-@@ -1,5 +1,7 @@
- /*
-  * Copyright 2012 Austin English
-+ * Copyright 2015 Michael Müller
-+ * Copyright 2015 Sebastian Lackner
-  *
-  * This library is free software; you can redistribute it and/or
-  * modify it under the terms of the GNU Lesser General Public
-@@ -16,18 +18,1049 @@
-  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
-  */
- 
-+#include "windows.h"
-+#include "shlwapi.h"
-+#include "fdi.h"
-+#include "wusa.h"
- #include "wine/debug.h"
- 
- WINE_DEFAULT_DEBUG_CHANNEL(wusa);
- 
-+/* from msvcrt/fcntl.h */
-+#define _O_RDONLY      0
-+#define _O_WRONLY      1
-+#define _O_RDWR        2
-+#define _O_ACCMODE     (_O_RDONLY|_O_WRONLY|_O_RDWR)
-+#define _O_APPEND      0x0008
-+#define _O_RANDOM      0x0010
-+#define _O_SEQUENTIAL  0x0020
-+#define _O_TEMPORARY   0x0040
-+#define _O_NOINHERIT   0x0080
-+#define _O_CREAT       0x0100
-+#define _O_TRUNC       0x0200
-+#define _O_EXCL        0x0400
-+#define _O_SHORT_LIVED 0x1000
-+#define _O_TEXT        0x4000
-+#define _O_BINARY      0x8000
-+
-+struct strbuf
-+{
-+    WCHAR *buf;
-+    DWORD pos;
-+    DWORD len;
-+};
-+
-+struct installer_tempdir
-+{
-+    struct list entry;
-+    WCHAR *path;
-+};
-+
-+struct installer_state
-+{
-+    BOOL norestart;
-+    BOOL quiet;
-+    struct list tempdirs;
-+    struct list assemblies;
-+    struct list updates;
-+};
-+
-+static BOOL strbuf_init(struct strbuf *buf)
-+{
-+    buf->pos = 0;
-+    buf->len = 64;
-+    buf->buf = heap_alloc(buf->len * sizeof(WCHAR));
-+    return buf->buf != NULL;
-+}
-+
-+static void strbuf_free(struct strbuf *buf)
-+{
-+    heap_free(buf->buf);
-+    buf->buf = NULL;
-+}
-+
-+static BOOL strbuf_append(struct strbuf *buf, const WCHAR *str, DWORD len)
-+{
-+    DWORD new_len;
-+    WCHAR *new_buf;
-+
-+    if (!buf->buf) return FALSE;
-+    if (!str) return TRUE;
-+
-+    if (len == ~0U) len = lstrlenW(str);
-+    if (buf->pos + len + 1 > buf->len)
-+    {
-+        new_len = max(buf->pos + len + 1, buf->len * 2);
-+        new_buf = heap_realloc(buf->buf, new_len * sizeof(WCHAR));
-+        if (!new_buf)
-+        {
-+            strbuf_free(buf);
-+            return FALSE;
-+        }
-+        buf->buf = new_buf;
-+        buf->len = new_len;
-+    }
-+
-+    memcpy(&buf->buf[buf->pos], str, len * sizeof(WCHAR));
-+    buf->buf[buf->pos + len] = 0;
-+    buf->pos += len;
-+    return TRUE;
-+}
-+
-+static BOOL str_ends_with(const WCHAR *str, const WCHAR *suffix)
-+{
-+    DWORD str_len = lstrlenW(str), suffix_len = lstrlenW(suffix);
-+    if (suffix_len > str_len) return FALSE;
-+    return !_wcsicmp(str + str_len - suffix_len, suffix);
-+}
-+
-+static WCHAR *path_combine(const WCHAR *path, const WCHAR *filename)
-+{
-+    static const WCHAR backslashW[] = {'\\',0};
-+    WCHAR *result;
-+    DWORD length;
-+
-+    if (!path || !filename) return NULL;
-+    length = lstrlenW(path) + lstrlenW(filename) + 2;
-+    if (!(result = heap_alloc(length * sizeof(WCHAR)))) return NULL;
-+
-+    lstrcpyW(result, path);
-+    if (result[0] && result[lstrlenW(result) - 1] != '\\')
-+        lstrcatW(result, backslashW);
-+    lstrcatW(result, filename);
-+    return result;
-+}
-+
-+static BOOL is_directory(const WCHAR *path)
-+{
-+    DWORD attrs = GetFileAttributesW(path);
-+    if (attrs == INVALID_FILE_ATTRIBUTES) return FALSE;
-+    return (attrs & FILE_ATTRIBUTE_DIRECTORY) != 0;
-+}
-+
-+static BOOL create_directory(const WCHAR *path)
-+{
-+    if (is_directory(path)) return TRUE;
-+    if (CreateDirectoryW(path, NULL)) return TRUE;
-+    return (GetLastError() == ERROR_ALREADY_EXISTS);
-+}
-+
-+static BOOL create_parent_directory(const WCHAR *filename)
-+{
-+    WCHAR *p, *path = strdupW(filename);
-+    BOOL ret = FALSE;
-+
-+    if (!path) return FALSE;
-+    if (!PathRemoveFileSpecW(path)) goto done;
-+    if (is_directory(path))
-+    {
-+        ret = TRUE;
-+        goto done;
-+    }
-+
-+    for (p = path; *p; p++)
-+    {
-+        if (*p != '\\') continue;
-+        *p = 0;
-+        if (!create_directory(path)) goto done;
-+        *p = '\\';
-+    }
-+    ret = create_directory(path);
-+
-+done:
-+    heap_free(path);
-+    return ret;
-+}
-+
-+static BOOL delete_directory(const WCHAR *path)
-+{
-+    static const WCHAR starW[] = {'*',0};
-+    static const WCHAR dotW[] = {'.', 0};
-+    static const WCHAR dotdotW[] = {'.','.', 0};
-+    WIN32_FIND_DATAW data;
-+    WCHAR *full_path;
-+    HANDLE search;
-+
-+    if (!(full_path = path_combine(path, starW))) return FALSE;
-+    search = FindFirstFileW(full_path, &data);
-+    heap_free(full_path);
-+
-+    if (search != INVALID_HANDLE_VALUE)
-+    {
-+        do
-+        {
-+            if (!lstrcmpW(data.cFileName, dotW)) continue;
-+            if (!lstrcmpW(data.cFileName, dotdotW)) continue;
-+            if (!(full_path = path_combine(path, data.cFileName))) continue;
-+            if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
-+                delete_directory(full_path);
-+            else
-+                DeleteFileW(full_path);
-+            heap_free(full_path);
-+        }
-+        while (FindNextFileW(search, &data));
-+        FindClose(search);
-+    }
-+
-+    return RemoveDirectoryW(path);
-+}
-+
-+static WCHAR *get_uncompressed_path(PFDINOTIFICATION pfdin)
-+{
-+    WCHAR *file = strdupAtoW(pfdin->psz1);
-+    WCHAR *path = path_combine(pfdin->pv, file);
-+    heap_free(file);
-+    return path;
-+}
-+
-+static void * CDECL cabinet_alloc(ULONG cb)
-+{
-+    return heap_alloc(cb);
-+}
-+
-+static void CDECL cabinet_free(void *pv)
-+{
-+    heap_free(pv);
-+}
-+
-+static INT_PTR CDECL cabinet_open(char *pszFile, int oflag, int pmode)
-+{
-+    DWORD dwAccess = 0;
-+    DWORD dwShareMode = 0;
-+    DWORD dwCreateDisposition = OPEN_EXISTING;
-+
-+    switch (oflag & _O_ACCMODE)
-+    {
-+    case _O_RDONLY:
-+        dwAccess = GENERIC_READ;
-+        dwShareMode = FILE_SHARE_READ | FILE_SHARE_DELETE;
-+        break;
-+    case _O_WRONLY:
-+        dwAccess = GENERIC_WRITE;
-+        dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
-+        break;
-+    case _O_RDWR:
-+        dwAccess = GENERIC_READ | GENERIC_WRITE;
-+        dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
-+        break;
-+    }
-+
-+    if ((oflag & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL))
-+        dwCreateDisposition = CREATE_NEW;
-+    else if (oflag & _O_CREAT)
-+        dwCreateDisposition = CREATE_ALWAYS;
-+
-+    return (INT_PTR)CreateFileA(pszFile, dwAccess, dwShareMode, NULL,
-+                                dwCreateDisposition, 0, NULL);
-+}
-+
-+static UINT CDECL cabinet_read(INT_PTR hf, void *pv, UINT cb)
-+{
-+    HANDLE handle = (HANDLE)hf;
-+    DWORD read;
-+
-+    if (ReadFile(handle, pv, cb, &read, NULL))
-+        return read;
-+
-+    return 0;
-+}
-+
-+static UINT CDECL cabinet_write(INT_PTR hf, void *pv, UINT cb)
-+{
-+    HANDLE handle = (HANDLE)hf;
-+    DWORD written;
-+
-+    if (WriteFile(handle, pv, cb, &written, NULL))
-+        return written;
-+
-+    return 0;
-+}
-+
-+static int CDECL cabinet_close(INT_PTR hf)
-+{
-+    HANDLE handle = (HANDLE)hf;
-+
-+    return CloseHandle(handle) ? 0 : -1;
-+}
-+
-+static LONG CDECL cabinet_seek(INT_PTR hf, LONG dist, int seektype)
-+{
-+    HANDLE handle = (HANDLE)hf;
-+    /* flags are compatible and so are passed straight through */
-+    return SetFilePointer(handle, dist, NULL, seektype);
-+}
-+
-+static INT_PTR cabinet_copy_file(FDINOTIFICATIONTYPE fdint,
-+                                 PFDINOTIFICATION pfdin)
-+{
-+    HANDLE handle = INVALID_HANDLE_VALUE;
-+    WCHAR *file;
-+    DWORD attrs;
-+
-+    if (!(file = get_uncompressed_path(pfdin)))
-+        return -1;
-+
-+    TRACE("extracting %s -> %s\n", debugstr_a(pfdin->psz1), debugstr_w(file));
-+
-+    if (create_parent_directory(file))
-+    {
-+        attrs = pfdin->attribs;
-+        if (!attrs) attrs = FILE_ATTRIBUTE_NORMAL;
-+        handle = CreateFileW(file, GENERIC_READ | GENERIC_WRITE, 0,
-+                             NULL, CREATE_ALWAYS, attrs, NULL);
-+    }
-+
-+    heap_free(file);
-+    return (handle != INVALID_HANDLE_VALUE) ? (INT_PTR)handle : -1;
-+}
-+
-+static INT_PTR cabinet_close_file_info(FDINOTIFICATIONTYPE fdint,
-+                                       PFDINOTIFICATION pfdin)
-+{
-+    HANDLE handle = (HANDLE)pfdin->hf;
-+    CloseHandle(handle);
-+    return 1;
-+}
-+
-+static INT_PTR CDECL cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
-+{
-+    switch (fdint)
-+    {
-+    case fdintPARTIAL_FILE:
-+        FIXME("fdintPARTIAL_FILE not implemented\n");
-+        return 0;
-+
-+    case fdintNEXT_CABINET:
-+        FIXME("fdintNEXT_CABINET not implemented\n");
-+        return 0;
-+
-+    case fdintCOPY_FILE:
-+        return cabinet_copy_file(fdint, pfdin);
-+
-+    case fdintCLOSE_FILE_INFO:
-+        return cabinet_close_file_info(fdint, pfdin);
-+
-+    default:
-+        return 0;
-+    }
-+}
-+
-+static BOOL extract_cabinet(const WCHAR *filename, const WCHAR *destination)
-+{
-+    char *filenameA = NULL;
-+    BOOL ret = FALSE;
-+    HFDI hfdi;
-+    ERF erf;
-+
-+    hfdi = FDICreate(cabinet_alloc, cabinet_free, cabinet_open, cabinet_read,
-+                     cabinet_write, cabinet_close, cabinet_seek, 0, &erf);
-+    if (!hfdi) return FALSE;
-+
-+    if ((filenameA = strdupWtoA(filename)))
-+    {
-+        ret = FDICopy(hfdi, filenameA, NULL, 0, cabinet_notify, NULL, (void *)destination);
-+        heap_free(filenameA);
-+    }
-+
-+    FDIDestroy(hfdi);
-+    return ret;
-+}
-+
-+static WCHAR *lookup_expression(struct assembly_entry *assembly, const WCHAR *key)
-+{
-+    static const WCHAR runtime_system32[] = {'r','u','n','t','i','m','e','.','s','y','s','t','e','m','3','2',0};
-+    static const WCHAR runtime_windows[] = {'r','u','n','t','i','m','e','.','w','i','n','d','o','w','s',0};
-+    WCHAR path[MAX_PATH];
-+
-+    if (!lstrcmpW(key, runtime_system32))
-+    {
-+        GetSystemDirectoryW(path, sizeof(path)/sizeof(path[0]));
-+        return strdupW(path);
-+    }
-+    if (!lstrcmpW(key, runtime_windows))
-+    {
-+        GetWindowsDirectoryW(path, sizeof(path)/sizeof(path[0]));
-+        return strdupW(path);
-+    }
-+
-+    WINE_FIXME("Unknown expression %s\n", debugstr_w(key));
-+    return NULL;
-+}
-+
-+static WCHAR *expand_expression(struct assembly_entry *assembly, const WCHAR *expression)
-+{
-+    static const WCHAR beginW[] = {'$','(',0};
-+    static const WCHAR endW[] = {')',0};
-+    const WCHAR *pos, *next;
-+    WCHAR *key, *value;
-+    struct strbuf buf;
-+
-+    if (!expression || !strbuf_init(&buf)) return NULL;
-+
-+    for (pos = expression; (next = wcsstr(pos, beginW)); pos = next + 1)
-+    {
-+        strbuf_append(&buf, pos, next - pos);
-+        pos = next + 2;
-+        if (!(next = wcsstr(pos, endW)))
-+        {
-+            strbuf_append(&buf, beginW, 2);
-+            break;
-+        }
-+
-+        if (!(key = strdupWn(pos, next - pos))) goto error;
-+        value = lookup_expression(assembly, key);
-+        heap_free(key);
-+        if (!value) goto error;
-+        strbuf_append(&buf, value, ~0U);
-+        heap_free(value);
-+    }
-+
-+    strbuf_append(&buf, pos, ~0U);
-+    return buf.buf;
-+
-+error:
-+    WINE_FIXME("Couldn't resolve expression %s\n", debugstr_w(expression));
-+    strbuf_free(&buf);
-+    return NULL;
-+}
-+
-+static WCHAR *get_assembly_source(struct assembly_entry *assembly)
-+{
-+    WCHAR *p, *path = strdupW(assembly->filename);
-+    if (path && (p = wcsrchr(path, '.'))) *p = 0;
-+    return path;
-+}
-+
-+static BOOL install_files_copy(struct assembly_entry *assembly, const WCHAR *source_path, struct fileop_entry *fileop, BOOL dryrun)
-+{
-+    WCHAR *target_path, *target, *source = NULL;
-+    BOOL ret = FALSE;
-+
-+    if (!(target_path = expand_expression(assembly, fileop->target))) return FALSE;
-+    if (!(target = path_combine(target_path, fileop->source))) goto error;
-+    if (!(source = path_combine(source_path, fileop->source))) goto error;
-+
-+    if (dryrun)
-+    {
-+        if (!(ret = PathFileExistsW(source)))
-+        {
-+            WINE_ERR("Required file %s not found\n", debugstr_w(source));
-+            goto error;
-+        }
-+    }
-+    else
-+    {
-+        if (!create_parent_directory(target))
-+        {
-+            WINE_ERR("Failed to create parent directory for %s\n", debugstr_w(target));
-+            goto error;
-+        }
-+        if (!(ret = CopyFileExW(source, target, NULL, NULL, NULL, 0)))
-+        {
-+            WINE_ERR("Failed to copy %s to %s\n", debugstr_w(source), debugstr_w(target));
-+            goto error;
-+        }
-+    }
-+
-+error:
-+    heap_free(target_path);
-+    heap_free(target);
-+    heap_free(source);
-+    return ret;
-+}
-+
-+static BOOL install_files(struct assembly_entry *assembly, BOOL dryrun)
-+{
-+    struct fileop_entry *fileop;
-+    WCHAR *source_path;
-+    BOOL ret = TRUE;
-+
-+    if (!(source_path = get_assembly_source(assembly)))
-+    {
-+        WINE_ERR("Failed to get assembly source directory\n");
-+        return FALSE;
-+    }
-+
-+    LIST_FOR_EACH_ENTRY(fileop, &assembly->fileops, struct fileop_entry, entry)
-+    {
-+        if (!(ret = install_files_copy(assembly, source_path, fileop, dryrun))) break;
-+    }
-+
-+    heap_free(source_path);
-+    return ret;
-+}
-+
-+static WCHAR *split_registry_key(WCHAR *key, HKEY *root)
-+{
-+    static const WCHAR hkey_classes_rootW[] = {'H','K','E','Y','_','C','L','A','S','S','E','S','_','R','O','O','T',0};
-+    static const WCHAR hkey_current_configW[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','C','O','N','F','I','G',0};
-+    static const WCHAR hkey_current_userW[] = {'H','K','E','Y','_','C','U','R','R','E','N','T','_','U','S','E','R',0};
-+    static const WCHAR hkey_local_machineW[] = {'H','K','E','Y','_','L','O','C','A','L','_','M','A','C','H','I','N','E',0};
-+    static const WCHAR hkey_usersW[] = {'H','K','E','Y','_','U','S','E','R','S',0};
-+
-+    DWORD size;
-+    WCHAR *p;
-+
-+    p = wcschr(key, '\\');
-+    if (!p) return NULL;
-+
-+    size = p - key;
-+
-+    if (lstrlenW(hkey_classes_rootW) == size && !wcsncmp(key, hkey_classes_rootW, size))
-+        *root = HKEY_CLASSES_ROOT;
-+    else if (lstrlenW(hkey_current_configW) == size && !wcsncmp(key, hkey_current_configW, size))
-+        *root = HKEY_CURRENT_CONFIG;
-+    else if (lstrlenW(hkey_current_userW) == size && !wcsncmp(key, hkey_current_userW, size))
-+        *root = HKEY_CURRENT_USER;
-+    else if (lstrlenW(hkey_local_machineW) == size && !wcsncmp(key, hkey_local_machineW, size))
-+        *root = HKEY_LOCAL_MACHINE;
-+    else if (lstrlenW(hkey_usersW) == size && !wcsncmp(key, hkey_usersW, size))
-+        *root = HKEY_USERS;
-+    else
-+    {
-+        WINE_FIXME("Unknown root key %s\n", debugstr_wn(key, size));
-+        return NULL;
-+    }
-+
-+    return p + 1;
-+}
-+
-+static BOOL install_registry_string(struct assembly_entry *assembly, HKEY key, struct registrykv_entry *registrykv, DWORD type, BOOL dryrun)
-+{
-+    DWORD value_size;
-+    WCHAR *value = expand_expression(assembly, registrykv->value);
-+    BOOL ret = TRUE;
-+
-+    if (registrykv->value && !value)
-+        return FALSE;
-+
-+    value_size = value ? (lstrlenW(value) + 1) * sizeof(WCHAR) : 0;
-+    if (!dryrun && RegSetValueExW(key, registrykv->name, 0, type, (void *)value, value_size))
-+    {
-+        WINE_ERR("Failed to set registry key %s\n", debugstr_w(registrykv->name));
-+        ret = FALSE;
-+    }
-+
-+    heap_free(value);
-+    return ret;
-+}
-+
-+static WCHAR *parse_multisz(const WCHAR *input, DWORD *size)
-+{
-+    static const WCHAR quoteW[] = {'"',0};
-+    static const WCHAR emptyW[] = {0};
-+    const WCHAR *pos, *next;
-+    struct strbuf buf;
-+
-+    *size = 0;
-+    if (!input || !input[0] || !strbuf_init(&buf)) return NULL;
-+
-+    for (pos = input; pos[0] == '"'; pos++)
-+    {
-+        pos++;
-+        if (!(next = wcsstr(pos, quoteW))) goto error;
-+        strbuf_append(&buf, pos, next - pos);
-+        strbuf_append(&buf, emptyW, sizeof(emptyW)/sizeof(emptyW[0]));
-+
-+        pos = next + 1;
-+        if (!pos[0]) break;
-+        if (pos[0] != ',')
-+        {
-+            WINE_FIXME("Error while parsing REG_MULTI_SZ string: Expected comma but got '%c'\n", pos[0]);
-+            goto error;
-+        }
-+    }
-+
-+    if (pos[0])
-+    {
-+        WINE_FIXME("Error while parsing REG_MULTI_SZ string: Garbage at end of string\n");
-+        goto error;
-+    }
-+
-+    strbuf_append(&buf, emptyW, sizeof(emptyW)/sizeof(emptyW[0]));
-+    *size = buf.pos * sizeof(WCHAR);
-+    return buf.buf;
-+
-+error:
-+    strbuf_free(&buf);
-+    return NULL;
-+}
-+
-+static BOOL install_registry_multisz(struct assembly_entry *assembly, HKEY key, struct registrykv_entry *registrykv, BOOL dryrun)
-+{
-+    DWORD value_size;
-+    WCHAR *value = parse_multisz(registrykv->value, &value_size);
-+    BOOL ret = TRUE;
-+
-+    if (registrykv->value && registrykv->value[0] && !value)
-+        return FALSE;
-+
-+    if (!dryrun && RegSetValueExW(key, registrykv->name, 0, REG_MULTI_SZ, (void *)value, value_size))
-+    {
-+        WINE_ERR("Failed to set registry key %s\n", debugstr_w(registrykv->name));
-+        ret = FALSE;
-+    }
-+
-+    heap_free(value);
-+    return ret;
-+}
-+
-+static BOOL install_registry_dword(struct assembly_entry *assembly, HKEY key, struct registrykv_entry *registrykv, BOOL dryrun)
-+{
-+    DWORD value = registrykv->value_type ? wcstoul(registrykv->value_type, NULL, 16) : 0;
-+    BOOL ret = TRUE;
-+
-+    if (!dryrun && RegSetValueExW(key, registrykv->name, 0, REG_DWORD, (void *)&value, sizeof(value)))
-+    {
-+        WINE_ERR("Failed to set registry key %s\n", debugstr_w(registrykv->name));
-+        ret = FALSE;
-+    }
-+
-+    return ret;
-+}
-+
-+static BYTE *parse_hex(const WCHAR *input, DWORD *size)
-+{
-+    WCHAR number[3] = {0, 0, 0};
-+    BYTE *output, *p;
-+    int length;
-+
-+    *size = 0;
-+    if (!input) return NULL;
-+    length = lstrlenW(input);
-+    if (length & 1) return NULL;
-+    length >>= 1;
-+
-+    if (!(output = heap_alloc(length))) return NULL;
-+    for (p = output; *input; input += 2)
-+    {
-+        number[0] = input[0];
-+        number[1] = input[1];
-+        *p++ = wcstoul(number, 0, 16);
-+    }
-+    *size = length;
-+    return output;
-+}
-+
-+static BOOL install_registry_binary(struct assembly_entry *assembly, HKEY key, struct registrykv_entry *registrykv, BOOL dryrun)
-+{
-+    DWORD value_size;
-+    BYTE *value = parse_hex(registrykv->value, &value_size);
-+    BOOL ret = TRUE;
-+
-+    if (registrykv->value && !value)
-+        return FALSE;
-+
-+    if (!dryrun && RegSetValueExW(key, registrykv->name, 0, REG_BINARY, value, value_size))
-+    {
-+        WINE_ERR("Failed to set registry key %s\n", debugstr_w(registrykv->name));
-+        ret = FALSE;
-+    }
-+
-+    heap_free(value);
-+    return ret;
-+}
-+
-+static BOOL install_registry_value(struct assembly_entry *assembly, HKEY key, struct registrykv_entry *registrykv, BOOL dryrun)
-+{
-+    static const WCHAR reg_szW[] = {'R','E','G','_','S','Z',0};
-+    static const WCHAR reg_expand_szW[] = {'R','E','G','_','E','X','P','A','N','D','_','S','Z',0};
-+    static const WCHAR reg_multi_szW[] = {'R','E','G','_','M','U','L','T','I','_','S','Z',0};
-+    static const WCHAR reg_dwordW[] = {'R','E','G','_','D','W','O','R','D',0};
-+    static const WCHAR reg_binaryW[] = {'R','E','G','_','B','I','N','A','R','Y',0};
-+
-+    if (!lstrcmpW(registrykv->value_type, reg_szW))
-+        return install_registry_string(assembly, key, registrykv, REG_SZ, dryrun);
-+    if (!lstrcmpW(registrykv->value_type, reg_expand_szW))
-+        return install_registry_string(assembly, key, registrykv, REG_EXPAND_SZ, dryrun);
-+    if (!lstrcmpW(registrykv->value_type, reg_multi_szW))
-+        return install_registry_multisz(assembly, key, registrykv, dryrun);
-+    if (!lstrcmpW(registrykv->value_type, reg_dwordW))
-+        return install_registry_dword(assembly, key, registrykv, dryrun);
-+    if (!lstrcmpW(registrykv->value_type, reg_binaryW))
-+        return install_registry_binary(assembly, key, registrykv, dryrun);
-+
-+    WINE_FIXME("Unsupported registry value type %s\n", debugstr_w(registrykv->value_type));
-+    return FALSE;
-+}
-+
-+static BOOL install_registry(struct assembly_entry *assembly, BOOL dryrun)
-+{
-+    struct registryop_entry *registryop;
-+    struct registrykv_entry *registrykv;
-+    HKEY root, subkey;
-+    WCHAR *path;
-+    BOOL ret = TRUE;
-+
-+    LIST_FOR_EACH_ENTRY(registryop, &assembly->registryops, struct registryop_entry, entry)
-+    {
-+        if (!(path = split_registry_key(registryop->key, &root)))
-+        {
-+            ret = FALSE;
-+            break;
-+        }
-+
-+        if (!dryrun && RegCreateKeyExW(root, path, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &subkey, NULL))
-+        {
-+            WINE_ERR("Failed to open registry key %s\n", debugstr_w(registryop->key));
-+            ret = FALSE;
-+            break;
-+        }
-+
-+        LIST_FOR_EACH_ENTRY(registrykv, &registryop->keyvalues, struct registrykv_entry, entry)
-+        {
-+            if (!(ret = install_registry_value(assembly, subkey, registrykv, dryrun))) break;
-+        }
-+
-+        if (!dryrun) RegCloseKey(subkey);
-+        if (!ret) break;
-+    }
-+
-+    return ret;
-+}
-+
-+static BOOL compare_assembly_string(const WCHAR *str1, const WCHAR *str2)
-+{
-+    static const WCHAR placeholderW[] = {'*',0};
-+    return !lstrcmpW(str1, str2) || !lstrcmpW(str1, placeholderW) || !lstrcmpW(str2, placeholderW);
-+}
-+
-+static struct assembly_entry *lookup_assembly(struct list *manifest_list, struct assembly_identity *identity)
-+{
-+    struct assembly_entry *assembly;
-+
-+    LIST_FOR_EACH_ENTRY(assembly, manifest_list, struct assembly_entry, entry)
-+    {
-+        if (_wcsicmp(assembly->identity.name, identity->name)) continue;
-+        if (!compare_assembly_string(assembly->identity.architecture, identity->architecture)) continue;
-+        if (!compare_assembly_string(assembly->identity.language, identity->language)) continue;
-+        if (!compare_assembly_string(assembly->identity.pubkey_token, identity->pubkey_token)) continue;
-+        if (!compare_assembly_string(assembly->identity.version, identity->version))
-+        {
-+            WINE_WARN("Ignoring version difference for %s (expected %s, found %s)\n",
-+                      debugstr_w(identity->name), debugstr_w(identity->version), debugstr_w(assembly->identity.version));
-+        }
-+        return assembly;
-+    }
-+
-+    return NULL;
-+}
-+
-+static BOOL install_assembly(struct list *manifest_list, struct assembly_identity *identity, BOOL dryrun)
-+{
-+    struct dependency_entry *dependency;
-+    struct assembly_entry *assembly;
-+    const WCHAR *name;
-+
-+    if (!(assembly = lookup_assembly(manifest_list, identity)))
-+    {
-+        WINE_FIXME("Assembly %s not found\n", debugstr_w(identity->name));
-+        return FALSE;
-+    }
-+
-+    name = assembly->identity.name;
-+
-+    if (assembly->status == ASSEMBLY_STATUS_INSTALLED)
-+    {
-+        WINE_TRACE("Assembly %s already installed\n", debugstr_w(name));
-+        return TRUE;
-+    }
-+    if (assembly->status == ASSEMBLY_STATUS_IN_PROGRESS)
-+    {
-+        WINE_ERR("Assembly %s caused circular dependency\n", debugstr_w(name));
-+        return FALSE;
-+    }
-+
-+    assembly->status = ASSEMBLY_STATUS_IN_PROGRESS;
-+
-+    LIST_FOR_EACH_ENTRY(dependency, &assembly->dependencies, struct dependency_entry, entry)
-+    {
-+        if (!install_assembly(manifest_list, &dependency->identity, dryrun)) return FALSE;
-+    }
-+
-+    if (!install_files(assembly, dryrun))
-+    {
-+        WINE_ERR("Failed to install all files for %s\n", debugstr_w(name));
-+        return FALSE;
-+    }
-+
-+    if (!install_registry(assembly, dryrun))
-+    {
-+        WINE_ERR("Failed to install registry keys for %s\n", debugstr_w(name));
-+        return FALSE;
-+    }
-+
-+    assembly->status = ASSEMBLY_STATUS_INSTALLED;
-+    return TRUE;
-+}
-+
-+static const WCHAR *create_temp_directory(struct installer_state *state)
-+{
-+    static const WCHAR msuW[] = {'m','s','u',0};
-+    static UINT id;
-+    struct installer_tempdir *entry;
-+    WCHAR tmp[MAX_PATH];
-+
-+    if (!GetTempPathW(sizeof(tmp)/sizeof(WCHAR), tmp)) return NULL;
-+    if (!(entry = heap_alloc(sizeof(*entry)))) return NULL;
-+    if (!(entry->path = heap_alloc((MAX_PATH + 20) * sizeof(WCHAR))))
-+    {
-+        heap_free(entry);
-+        return NULL;
-+    }
-+    for (;;)
-+    {
-+        if (!GetTempFileNameW(tmp, msuW, ++id, entry->path))
-+        {
-+            heap_free(entry->path);
-+            heap_free(entry);
-+            return NULL;
-+        }
-+        if (CreateDirectoryW(entry->path, NULL)) break;
-+    }
-+
-+    list_add_tail(&state->tempdirs, &entry->entry);
-+    return entry->path;
-+}
-+
-+static void installer_cleanup(struct installer_state *state)
-+{
-+    struct installer_tempdir *tempdir, *tempdir2;
-+    struct assembly_entry *assembly, *assembly2;
-+    struct dependency_entry *dependency, *dependency2;
-+
-+    LIST_FOR_EACH_ENTRY_SAFE(tempdir, tempdir2, &state->tempdirs, struct installer_tempdir, entry)
-+    {
-+        list_remove(&tempdir->entry);
-+        delete_directory(tempdir->path);
-+        heap_free(tempdir->path);
-+        heap_free(tempdir);
-+    }
-+    LIST_FOR_EACH_ENTRY_SAFE(assembly, assembly2, &state->assemblies, struct assembly_entry, entry)
-+    {
-+        list_remove(&assembly->entry);
-+        free_assembly(assembly);
-+    }
-+    LIST_FOR_EACH_ENTRY_SAFE(dependency, dependency2, &state->updates, struct dependency_entry, entry)
-+    {
-+        list_remove(&dependency->entry);
-+        free_dependency(dependency);
-+    }
-+}
-+
-+static BOOL load_assemblies_from_cab(const WCHAR *filename, struct installer_state *state)
-+{
-+    static const WCHAR manifestW[] = {'.','m','a','n','i','f','e','s','t',0};
-+    static const WCHAR mumW[] = {'.','m','u','m',0};
-+    static const WCHAR starW[] = {'*',0};
-+    struct assembly_entry *assembly;
-+    const WCHAR *temp_path;
-+    WIN32_FIND_DATAW data;
-+    HANDLE search;
-+    WCHAR *path;
-+
-+    WINE_TRACE("Processing cab file %s\n", debugstr_w(filename));
-+
-+    if (!(temp_path = create_temp_directory(state))) return FALSE;
-+    if (!extract_cabinet(filename, temp_path))
-+    {
-+        WINE_ERR("Failed to extract %s\n", debugstr_w(filename));
-+        return FALSE;
-+    }
-+
-+    if (!(path = path_combine(temp_path, starW))) return FALSE;
-+    search = FindFirstFileW(path, &data);
-+    heap_free(path);
-+
-+    if (search != INVALID_HANDLE_VALUE)
-+    {
-+        do
-+        {
-+            if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue;
-+            if (!str_ends_with(data.cFileName, manifestW) &&
-+                !str_ends_with(data.cFileName, mumW)) continue;
-+            if (!(path = path_combine(temp_path, data.cFileName))) continue;
-+            if ((assembly = load_manifest(path)))
-+                list_add_tail(&state->assemblies, &assembly->entry);
-+            heap_free(path);
-+        }
-+        while (FindNextFileW(search, &data));
-+        FindClose(search);
-+    }
-+
-+    return TRUE;
-+}
-+
-+static BOOL install_updates(struct installer_state *state, BOOL dryrun)
-+{
-+    struct dependency_entry *dependency;
-+    LIST_FOR_EACH_ENTRY(dependency, &state->updates, struct dependency_entry, entry)
-+    {
-+        if (!install_assembly(&state->assemblies, &dependency->identity, dryrun))
-+        {
-+            WINE_ERR("Failed to install update %s\n", debugstr_w(dependency->identity.name));
-+            return FALSE;
-+        }
-+    }
-+    return TRUE;
-+}
-+
-+static void set_assembly_status(struct list *manifest_list, DWORD status)
-+{
-+    struct assembly_entry *assembly;
-+    LIST_FOR_EACH_ENTRY(assembly, manifest_list, struct assembly_entry, entry)
-+    {
-+        assembly->status = status;
-+    }
-+}
-+
-+static BOOL install_msu(WCHAR *filename, struct installer_state *state)
-+{
-+    static const WCHAR wsusscanW[] = {'W','S','U','S','S','C','A','N','.','c','a','b',0};
-+    static const WCHAR cabW[] = {'*','.','c','a','b',0};
-+    static const WCHAR xmlW[] = {'*','.','x','m','l',0};
-+    const WCHAR *temp_path;
-+    WIN32_FIND_DATAW data;
-+    HANDLE search;
-+    WCHAR *path;
-+    BOOL ret = FALSE;
-+
-+    list_init(&state->tempdirs);
-+    list_init(&state->assemblies);
-+    list_init(&state->updates);
-+    CoInitialize(NULL);
-+
-+    WINE_TRACE("Processing msu file %s\n", debugstr_w(filename));
-+
-+    if (!(temp_path = create_temp_directory(state))) return 1;
-+    if (!extract_cabinet(filename, temp_path))
-+    {
-+        WINE_ERR("Failed to extract %s\n", debugstr_w(filename));
-+        goto done;
-+    }
-+
-+    /* load all manifests from contained cabinet archives */
-+    if (!(path = path_combine(temp_path, cabW))) goto done;
-+    search = FindFirstFileW(path, &data);
-+    heap_free(path);
-+
-+    if (search != INVALID_HANDLE_VALUE)
-+    {
-+        do
-+        {
-+            if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue;
-+            if (!_wcsicmp(data.cFileName, wsusscanW)) continue;
-+            if (!(path = path_combine(temp_path, data.cFileName))) continue;
-+            if (!load_assemblies_from_cab(path, state))
-+                WINE_ERR("Failed to load all manifests from %s, ignoring\n", debugstr_w(path));
-+            heap_free(path);
-+        }
-+        while (FindNextFileW(search, &data));
-+        FindClose(search);
-+    }
-+
-+    /* load all update descriptions */
-+    if (!(path = path_combine(temp_path, xmlW))) goto done;
-+    search = FindFirstFileW(path, &data);
-+    heap_free(path);
-+
-+    if (search != INVALID_HANDLE_VALUE)
-+    {
-+        do
-+        {
-+            if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue;
-+            if (!(path = path_combine(temp_path, data.cFileName))) continue;
-+            if (!load_update(path, &state->updates))
-+                WINE_ERR("Failed to load all updates from %s, ignoring\n", debugstr_w(path));
-+            heap_free(path);
-+        }
-+        while (FindNextFileW(search, &data));
-+        FindClose(search);
-+    }
-+
-+    /* dump package information (for debugging) */
-+    if (WINE_TRACE_ON(wusa))
-+    {
-+        struct dependency_entry *dependency;
-+        struct assembly_entry *assembly;
-+
-+        WINE_TRACE("List of updates:\n");
-+        LIST_FOR_EACH_ENTRY(dependency, &state->updates, struct dependency_entry, entry)
-+            WINE_TRACE(" * %s\n", debugstr_w(dependency->identity.name));
-+
-+        WINE_TRACE("List of manifests (with dependencies):\n");
-+        LIST_FOR_EACH_ENTRY(assembly, &state->assemblies, struct assembly_entry, entry)
-+        {
-+            WINE_TRACE(" * %s\n", debugstr_w(assembly->identity.name));
-+            LIST_FOR_EACH_ENTRY(dependency, &assembly->dependencies, struct dependency_entry, entry)
-+                WINE_TRACE("   -> %s\n", debugstr_w(dependency->identity.name));
-+        }
-+    }
-+
-+    /* perform dryrun */
-+    set_assembly_status(&state->assemblies, ASSEMBLY_STATUS_NONE);
-+    if (!install_updates(state, TRUE))
-+    {
-+        WINE_ERR("Dryrun failed, aborting installation\n");
-+        goto done;
-+    }
-+
-+    /* installation */
-+    set_assembly_status(&state->assemblies, ASSEMBLY_STATUS_NONE);
-+    if (!install_updates(state, FALSE))
-+    {
-+        WINE_ERR("Installation failed\n");
-+        goto done;
-+    }
-+
-+    ret = TRUE;
-+
-+done:
-+    installer_cleanup(state);
-+    return ret;
-+}
-+
- int __cdecl wmain(int argc, WCHAR *argv[])
- {
-+    static const WCHAR norestartW[] = {'/','n','o','r','e','s','t','a','r','t',0};
-+    static const WCHAR quietW[] = {'/','q','u','i','e','t',0};
-+    struct installer_state state;
-+    WCHAR *filename = NULL;
-     int i;
- 
--    WINE_FIXME("stub:");
--    for (i = 0; i < argc; i++)
--        WINE_FIXME(" %s", wine_dbgstr_w(argv[i]));
--    WINE_FIXME("\n");
-+    state.norestart = FALSE;
-+    state.quiet = FALSE;
- 
--    return 0;
-+    if (TRACE_ON(wusa))
-+    {
-+        WINE_TRACE("Command line:");
-+        for (i = 0; i < argc; i++)
-+            WINE_TRACE(" %s", wine_dbgstr_w(argv[i]));
-+        WINE_TRACE("\n");
-+    }
-+
-+    for (i = 1; i < argc; i++)
-+    {
-+        if (argv[i][0] == '/')
-+        {
-+            if (!lstrcmpW(argv[i], norestartW))
-+                state.norestart = TRUE;
-+            else if (!lstrcmpW(argv[i], quietW))
-+                state.quiet = TRUE;
-+            else
-+                WINE_FIXME("Unknown option: %s\n", wine_dbgstr_w(argv[i]));
-+        }
-+        else if (!filename)
-+            filename = argv[i];
-+        else
-+            WINE_FIXME("Unknown option: %s\n", wine_dbgstr_w(argv[i]));
-+    }
-+
-+    if (!filename)
-+    {
-+        WINE_FIXME("Missing filename argument\n");
-+        return 1;
-+    }
-+
-+    return !install_msu(filename, &state);
- }
-diff --git a/programs/wusa/manifest.c b/programs/wusa/manifest.c
-new file mode 100644
-index 00000000000..d88b5f98a94
---- /dev/null
-+++ b/programs/wusa/manifest.c
-@@ -0,0 +1,704 @@
-+/*
-+ * Manifest parser for WUSA
-+ *
-+ * Copyright 2015 Michael Müller
-+ * Copyright 2015 Sebastian Lackner
-+ *
-+ * 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 "windows.h"
-+#define COBJMACROS
-+#include "initguid.h"
-+#include "msxml.h"
-+#include "wusa.h"
-+#include "wine/debug.h"
-+
-+
-+WINE_DEFAULT_DEBUG_CHANNEL(wusa);
-+
-+static struct dependency_entry *alloc_dependency(void)
-+{
-+    struct dependency_entry *entry = heap_alloc_zero(sizeof(*entry));
-+    if (!entry) WINE_ERR("failed to allocate memory for dependency\n");
-+    return entry;
-+}
-+
-+static struct fileop_entry *alloc_fileop(void)
-+{
-+    struct fileop_entry *entry = heap_alloc_zero(sizeof(*entry));
-+    if (!entry) WINE_ERR("failed to allocate memory for fileop\n");
-+    return entry;
-+}
-+
-+static struct registrykv_entry *alloc_registrykv(void)
-+{
-+    struct registrykv_entry *entry = heap_alloc_zero(sizeof(*entry));
-+    if (!entry) WINE_ERR("failed to allocate memory for registrykv\n");
-+    return entry;
-+}
-+
-+static struct registryop_entry *alloc_registryop(void)
-+{
-+    struct registryop_entry *entry = heap_alloc_zero(sizeof(*entry));
-+    if (!entry) WINE_ERR("failed to allocate memory for registryop\n");
-+    else
-+    {
-+        list_init(&entry->keyvalues);
-+    }
-+    return entry;
-+}
-+
-+static struct assembly_entry *alloc_assembly(void)
-+{
-+    struct assembly_entry *entry = heap_alloc_zero(sizeof(*entry));
-+    if (!entry) WINE_ERR("failed to allocate memory for assembly\n");
-+    else
-+    {
-+        list_init(&entry->dependencies);
-+        list_init(&entry->fileops);
-+        list_init(&entry->registryops);
-+    }
-+    return entry;
-+}
-+
-+static void clear_identity(struct assembly_identity *entry)
-+{
-+    heap_free(entry->name);
-+    heap_free(entry->version);
-+    heap_free(entry->architecture);
-+    heap_free(entry->language);
-+    heap_free(entry->pubkey_token);
-+}
-+
-+void free_dependency(struct dependency_entry *entry)
-+{
-+    clear_identity(&entry->identity);
-+    heap_free(entry);
-+}
-+
-+static void free_fileop(struct fileop_entry *entry)
-+{
-+    heap_free(entry->source);
-+    heap_free(entry->target);
-+    heap_free(entry);
-+}
-+
-+static void free_registrykv(struct registrykv_entry *entry)
-+{
-+    heap_free(entry->name);
-+    heap_free(entry->value_type);
-+    heap_free(entry->value);
-+    heap_free(entry);
-+}
-+
-+static void free_registryop(struct registryop_entry *entry)
-+{
-+    struct registrykv_entry *keyvalue, *keyvalue2;
-+
-+    heap_free(entry->key);
-+
-+    LIST_FOR_EACH_ENTRY_SAFE(keyvalue, keyvalue2, &entry->keyvalues, struct registrykv_entry, entry)
-+    {
-+        list_remove(&keyvalue->entry);
-+        free_registrykv(keyvalue);
-+    }
-+
-+    heap_free(entry);
-+}
-+
-+void free_assembly(struct assembly_entry *entry)
-+{
-+    struct dependency_entry *dependency, *dependency2;
-+    struct fileop_entry *fileop, *fileop2;
-+    struct registryop_entry *registryop, *registryop2;
-+
-+    heap_free(entry->filename);
-+    heap_free(entry->displayname);
-+    clear_identity(&entry->identity);
-+
-+    LIST_FOR_EACH_ENTRY_SAFE(dependency, dependency2, &entry->dependencies, struct dependency_entry, entry)
-+    {
-+        list_remove(&dependency->entry);
-+        free_dependency(dependency);
-+    }
-+    LIST_FOR_EACH_ENTRY_SAFE(fileop, fileop2, &entry->fileops, struct fileop_entry, entry)
-+    {
-+        list_remove(&fileop->entry);
-+        free_fileop(fileop);
-+    }
-+    LIST_FOR_EACH_ENTRY_SAFE(registryop, registryop2, &entry->registryops, struct registryop_entry, entry)
-+    {
-+        list_remove(&registryop->entry);
-+        free_registryop(registryop);
-+    }
-+
-+    heap_free(entry);
-+}
-+
-+static WCHAR *get_xml_attribute(IXMLDOMElement *root, const WCHAR *name)
-+{
-+    WCHAR *ret = NULL;
-+    VARIANT var;
-+    BSTR bstr;
-+
-+    if ((bstr = SysAllocString(name)))
-+    {
-+        VariantInit(&var);
-+        if (SUCCEEDED(IXMLDOMElement_getAttribute(root, bstr, &var)))
-+        {
-+            ret = (V_VT(&var) == VT_BSTR) ? strdupW(V_BSTR(&var)) : NULL;
-+            VariantClear(&var);
-+        }
-+        SysFreeString(bstr);
-+    }
-+
-+    return ret;
-+}
-+
-+static BOOL check_xml_tagname(IXMLDOMElement *root, const WCHAR *tagname)
-+{
-+    BOOL ret = FALSE;
-+    BSTR bstr;
-+
-+    if (SUCCEEDED(IXMLDOMElement_get_tagName(root, &bstr)))
-+    {
-+        ret = !lstrcmpW(bstr, tagname);
-+        SysFreeString(bstr);
-+    }
-+
-+    return ret;
-+}
-+
-+static IXMLDOMElement *select_xml_node(IXMLDOMElement *root, const WCHAR *name)
-+{
-+    IXMLDOMElement *ret = NULL;
-+    IXMLDOMNode *node;
-+    BSTR bstr;
-+
-+    if ((bstr = SysAllocString(name)))
-+    {
-+        if (SUCCEEDED(IXMLDOMElement_selectSingleNode(root, bstr, &node)))
-+        {
-+            if (FAILED(IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void **)&ret)))
-+                ret = NULL;
-+            IXMLDOMNode_Release(node);
-+        }
-+        SysFreeString(bstr);
-+    }
-+
-+    return ret;
-+}
-+
-+static BOOL call_xml_callbacks(IXMLDOMElement *root, BOOL (*func)(IXMLDOMElement *child, WCHAR *tagname, void *context), void *context)
-+{
-+    IXMLDOMNodeList *children;
-+    IXMLDOMElement *child;
-+    IXMLDOMNode *node;
-+    BSTR tagname;
-+    BOOL ret = TRUE;
-+
-+    if (FAILED(IXMLDOMElement_get_childNodes(root, &children)))
-+        return FALSE;
-+
-+    while (ret && IXMLDOMNodeList_nextNode(children, &node) == S_OK)
-+    {
-+        if (SUCCEEDED(IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void **)&child)))
-+        {
-+            if (SUCCEEDED(IXMLDOMElement_get_tagName(child, &tagname)))
-+            {
-+                ret = func(child, tagname, context);
-+                SysFreeString(tagname);
-+            }
-+            IXMLDOMElement_Release(child);
-+        }
-+        IXMLDOMNode_Release(node);
-+    }
-+
-+    IXMLDOMNodeList_Release(children);
-+    return ret;
-+}
-+
-+static IXMLDOMElement *load_xml(const WCHAR *filename)
-+{
-+    IXMLDOMDocument *document = NULL;
-+    IXMLDOMElement *root = NULL;
-+    VARIANT_BOOL success;
-+    VARIANT variant;
-+    BSTR bstr;
-+
-+    WINE_TRACE("Loading XML from %s\n", debugstr_w(filename));
-+
-+    if (!(bstr = SysAllocString(filename)))
-+        return FALSE;
-+
-+    if (SUCCEEDED(CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void **)&document)))
-+    {
-+        VariantInit(&variant);
-+        V_VT(&variant) = VT_BSTR;
-+        V_BSTR(&variant) = bstr;
-+
-+        if (SUCCEEDED(IXMLDOMDocument_load(document, variant, &success)) && success)
-+        {
-+            if (FAILED(IXMLDOMDocument_get_documentElement(document, &root)))
-+                root = NULL;
-+        }
-+        IXMLDOMDocument_Release(document);
-+    }
-+
-+    SysFreeString(bstr);
-+    return root;
-+}
-+
-+static BOOL read_identity(IXMLDOMElement *root, struct assembly_identity *identity)
-+{
-+    static const WCHAR nameW[] = {'n','a','m','e',0};
-+    static const WCHAR versionW[] = {'v','e','r','s','i','o','n',0};
-+    static const WCHAR processorArchitectureW[] = {'p','r','o','c','e','s','s','o','r','A','r','c','h','i','t','e','c','t','u','r','e',0};
-+    static const WCHAR languageW[] = {'l','a','n','g','u','a','g','e',0};
-+    static const WCHAR publicKeyTokenW[] = {'p','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
-+
-+    memset(identity, 0, sizeof(*identity));
-+    if (!(identity->name            = get_xml_attribute(root, nameW))) goto error;
-+    if (!(identity->version         = get_xml_attribute(root, versionW))) goto error;
-+    if (!(identity->architecture    = get_xml_attribute(root, processorArchitectureW))) goto error;
-+    if (!(identity->language        = get_xml_attribute(root, languageW))) goto error;
-+    if (!(identity->pubkey_token    = get_xml_attribute(root, publicKeyTokenW))) goto error;
-+    return TRUE;
-+
-+error:
-+    clear_identity(identity);
-+    return FALSE;
-+}
-+
-+/* <assembly><dependency><dependentAssembly> */
-+static BOOL read_dependent_assembly(IXMLDOMElement *root, struct assembly_identity *identity)
-+{
-+    static const WCHAR dependencyTypeW[] = {'d','e','p','e','n','d','e','n','c','y','T','y','p','e',0};
-+    static const WCHAR installW[] = {'i','n','s','t','a','l','l',0};
-+    static const WCHAR select_assemblyIdentityW[] = {'.','/','/','a','s','s','e','m','b','l','y','I','d','e','n','t','i','t','y',0};
-+    IXMLDOMElement *child = NULL;
-+    WCHAR *dependency_type;
-+    BOOL ret = FALSE;
-+
-+    if (!(dependency_type = get_xml_attribute(root, dependencyTypeW)))
-+    {
-+        WINE_ERR("Failed to get dependency type\n");
-+        return FALSE;
-+    }
-+    if (lstrcmpW(dependency_type, installW))
-+    {
-+        WINE_FIXME("Unimplemented dependency type %s\n", debugstr_w(dependency_type));
-+        goto error;
-+    }
-+    if (!(child = select_xml_node(root, select_assemblyIdentityW)))
-+    {
-+        WINE_FIXME("Failed to find assemblyIdentity child node\n");
-+        goto error;
-+    }
-+
-+    ret = read_identity(child, identity);
-+
-+error:
-+    if (child) IXMLDOMElement_Release(child);
-+    heap_free(dependency_type);
-+    return ret;
-+}
-+
-+/* <assembly><dependency> */
-+static BOOL read_dependency(IXMLDOMElement *child, WCHAR *tagname, void *context)
-+{
-+    static const WCHAR dependentAssemblyW[] = {'d','e','p','e','n','d','e','n','t','A','s','s','e','m','b','l','y',0};
-+    struct assembly_entry *assembly = context;
-+    struct dependency_entry *entry;
-+
-+    if (lstrcmpW(tagname, dependentAssemblyW))
-+    {
-+        WINE_FIXME("Don't know how to handle dependency tag %s\n", debugstr_w(tagname));
-+        return FALSE;
-+    }
-+
-+    if ((entry = alloc_dependency()))
-+    {
-+        if (read_dependent_assembly(child, &entry->identity))
-+        {
-+            WINE_TRACE("Found dependency %s\n", debugstr_w(entry->identity.name));
-+            list_add_tail(&assembly->dependencies, &entry->entry);
-+            return TRUE;
-+        }
-+        free_dependency(entry);
-+    }
-+
-+    return FALSE;
-+}
-+static BOOL iter_dependency(IXMLDOMElement *root, struct assembly_entry *assembly)
-+{
-+    return call_xml_callbacks(root, read_dependency, assembly);
-+}
-+
-+/* <assembly><package><update><component> */
-+/* <assembly><package><update><package> */
-+static BOOL read_components(IXMLDOMElement *child, WCHAR *tagname, void *context)
-+{
-+    static const WCHAR assemblyIdentityW[] = {'a','s','s','e','m','b','l','y','I','d','e','n','t','i','t','y',0};
-+    struct assembly_entry *assembly = context;
-+    struct dependency_entry *entry;
-+
-+    if (lstrcmpW(tagname, assemblyIdentityW))
-+    {
-+        WINE_FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
-+        return TRUE;
-+    }
-+
-+    if ((entry = alloc_dependency()))
-+    {
-+        if (read_identity(child, &entry->identity))
-+        {
-+            WINE_TRACE("Found identity %s\n", debugstr_w(entry->identity.name));
-+            list_add_tail(&assembly->dependencies, &entry->entry);
-+            return TRUE;
-+        }
-+        free_dependency(entry);
-+    }
-+
-+    return FALSE;
-+}
-+static BOOL iter_components(IXMLDOMElement *root, struct assembly_entry *assembly)
-+{
-+    return call_xml_callbacks(root, read_components, assembly);
-+}
-+
-+/* <assembly><package><update> */
-+static BOOL read_update(IXMLDOMElement *child, WCHAR *tagname, void *context)
-+{
-+    static const WCHAR applicableW[] = {'a','p','p','l','i','c','a','b','l','e',0};
-+    static const WCHAR componentW[] = {'c','o','m','p','o','n','e','n','t',0};
-+    static const WCHAR packageW[] = {'p','a','c','k','a','g','e',0};
-+    struct assembly_entry *assembly = context;
-+
-+    if (!lstrcmpW(tagname, componentW))
-+        return iter_components(child, assembly);
-+    if (!lstrcmpW(tagname, packageW))
-+        return iter_components(child, assembly);
-+    if (!lstrcmpW(tagname, applicableW))
-+        return TRUE;
-+
-+    WINE_FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
-+    return FALSE;
-+}
-+static BOOL iter_update(IXMLDOMElement *root, struct assembly_entry *assembly)
-+{
-+    return call_xml_callbacks(root, read_update, assembly);
-+}
-+
-+/* <assembly><package> */
-+static BOOL read_package(IXMLDOMElement *child, WCHAR *tagname, void *context)
-+{
-+    static const WCHAR updateW[] = {'u','p','d','a','t','e',0};
-+    static const WCHAR parentW[] = {'p','a','r','e','n','t',0};
-+    struct assembly_entry *assembly = context;
-+
-+    if (!lstrcmpW(tagname, updateW))
-+        return iter_update(child, assembly);
-+    if (!lstrcmpW(tagname, parentW))
-+        return TRUE;
-+
-+    WINE_FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
-+    return TRUE;
-+}
-+static BOOL iter_package(IXMLDOMElement *root, struct assembly_entry *assembly)
-+{
-+    return call_xml_callbacks(root, read_package, assembly);
-+}
-+
-+/* <assembly><file> */
-+static BOOL read_file(IXMLDOMElement *root, struct assembly_entry *assembly)
-+{
-+    static const WCHAR sourceNameW[] = {'s','o','u','r','c','e','N','a','m','e',0};
-+    static const WCHAR destinationPathW[] = {'d','e','s','t','i','n','a','t','i','o','n','P','a','t','h',0};
-+    struct fileop_entry *entry;
-+
-+    if (!(entry = alloc_fileop()))
-+        return FALSE;
-+
-+    if (!(entry->source = get_xml_attribute(root, sourceNameW))) goto error;
-+    if (!(entry->target = get_xml_attribute(root, destinationPathW))) goto error;
-+
-+    WINE_TRACE("Found fileop %s -> %s\n", debugstr_w(entry->source), debugstr_w(entry->target));
-+    list_add_tail(&assembly->fileops, &entry->entry);
-+    return TRUE;
-+
-+error:
-+    free_fileop(entry);
-+    return FALSE;
-+}
-+
-+/* <assembly><registryKeys><registryKey> */
-+static BOOL read_registry_key(IXMLDOMElement *child, WCHAR *tagname, void *context)
-+{
-+    static const WCHAR securityDescriptorW[] = {'s','e','c','u','r','i','t','y','D','e','s','c','r','i','p','t','o','r',0};
-+    static const WCHAR registryValueW[] = {'r','e','g','i','s','t','r','y','V','a','l','u','e',0};
-+    static const WCHAR nameW[] = {'n','a','m','e',0};
-+    static const WCHAR valueTypeW[] = {'v','a','l','u','e','T','y','p','e',0};
-+    static const WCHAR valueW[] = {'v','a','l','u','e',0};
-+    struct registryop_entry *registryop = context;
-+    struct registrykv_entry *entry;
-+
-+    if (!lstrcmpW(tagname, securityDescriptorW)) return TRUE;
-+    if (lstrcmpW(tagname, registryValueW))
-+    {
-+        WINE_FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
-+        return TRUE;
-+    }
-+
-+    if (!(entry = alloc_registrykv()))
-+        return FALSE;
-+
-+    if (!(entry->value_type = get_xml_attribute(child, valueTypeW))) goto error;
-+    entry->name = get_xml_attribute(child, nameW);      /* optional */
-+    entry->value = get_xml_attribute(child, valueW);    /* optional */
-+
-+    WINE_TRACE("Found registry %s -> %s\n", debugstr_w(entry->name), debugstr_w(entry->value));
-+    list_add_tail(&registryop->keyvalues, &entry->entry);
-+    return TRUE;
-+
-+error:
-+    free_registrykv(entry);
-+    return FALSE;
-+}
-+static BOOL iter_registry_key(IXMLDOMElement *root, struct registryop_entry *registryop)
-+{
-+    return call_xml_callbacks(root, read_registry_key, registryop);
-+}
-+
-+/* <assembly><registryKeys> */
-+static BOOL read_registry_keys(IXMLDOMElement *child, WCHAR *tagname, void *context)
-+{
-+    static const WCHAR registryKeyW[] = {'r','e','g','i','s','t','r','y','K','e','y',0};
-+    static const WCHAR keyNameW[] = {'k','e','y','N','a','m','e',0};
-+    struct assembly_entry *assembly = context;
-+    struct registryop_entry *entry;
-+    WCHAR *keyname;
-+
-+    if (lstrcmpW(tagname, registryKeyW))
-+    {
-+        WINE_FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
-+        return TRUE;
-+    }
-+
-+    if (!(keyname = get_xml_attribute(child, keyNameW)))
-+    {
-+        WINE_FIXME("RegistryKey tag doesn't specify keyName\n");
-+        return FALSE;
-+    }
-+
-+    if ((entry = alloc_registryop()))
-+    {
-+        list_init(&entry->keyvalues);
-+        if (iter_registry_key(child, entry))
-+        {
-+            entry->key = keyname;
-+            WINE_TRACE("Found registryop %s\n", debugstr_w(entry->key));
-+            list_add_tail(&assembly->registryops, &entry->entry);
-+            return TRUE;
-+        }
-+        free_registryop(entry);
-+    }
-+
-+    heap_free(keyname);
-+    return FALSE;
-+}
-+static BOOL iter_registry_keys(IXMLDOMElement *root, struct assembly_entry *assembly)
-+{
-+    return call_xml_callbacks(root, read_registry_keys, assembly);
-+}
-+
-+/* <assembly> */
-+static BOOL read_assembly(IXMLDOMElement *child, WCHAR *tagname, void *context)
-+{
-+    static const WCHAR assemblyIdentityW[] = {'a','s','s','e','m','b','l','y','I','d','e','n','t','i','t','y',0};
-+    static const WCHAR dependencyW[] = {'d','e','p','e','n','d','e','n','c','y',0};
-+    static const WCHAR packageW[] = {'p','a','c','k','a','g','e',0};
-+    static const WCHAR fileW[] = {'f','i','l','e',0};
-+    static const WCHAR registryKeysW[] = {'r','e','g','i','s','t','r','y','K','e','y','s',0};
-+    static const WCHAR trustInfoW[] = {'t','r','u','s','t','I','n','f','o',0};
-+    static const WCHAR configurationW[] = {'c','o','n','f','i','g','u','r','a','t','i','o','n',0};
-+    static const WCHAR deploymentW[] = {'d','e','p','l','o','y','m','e','n','t',0};
-+    struct assembly_entry *assembly = context;
-+
-+    if (!lstrcmpW(tagname, assemblyIdentityW) && !assembly->identity.name)
-+        return read_identity(child, &assembly->identity);
-+    if (!lstrcmpW(tagname, dependencyW))
-+        return iter_dependency(child, assembly);
-+    if (!lstrcmpW(tagname, packageW))
-+        return iter_package(child, assembly);
-+    if (!lstrcmpW(tagname, fileW))
-+        return read_file(child, assembly);
-+    if (!lstrcmpW(tagname, registryKeysW))
-+        return iter_registry_keys(child, assembly);
-+    if (!lstrcmpW(tagname, trustInfoW))
-+        return TRUE;
-+    if (!lstrcmpW(tagname, configurationW))
-+        return TRUE;
-+    if (!lstrcmpW(tagname, deploymentW))
-+        return TRUE;
-+
-+    WINE_FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
-+    return TRUE;
-+}
-+static BOOL iter_assembly(IXMLDOMElement *root, struct assembly_entry *assembly)
-+{
-+    return call_xml_callbacks(root, read_assembly, assembly);
-+}
-+
-+struct assembly_entry *load_manifest(const WCHAR *filename)
-+{
-+    static const WCHAR assemblyW[] = {'a','s','s','e','m','b','l','y',0};
-+    static const WCHAR displaynameW[] = {'d','i','s','p','l','a','y','N','a','m','e',0};
-+    struct assembly_entry *entry = NULL;
-+    IXMLDOMElement *root = NULL;
-+
-+    WINE_TRACE("Loading manifest %s\n", debugstr_w(filename));
-+
-+    if (!(root = load_xml(filename))) return NULL;
-+    if (!check_xml_tagname(root, assemblyW))
-+    {
-+        WINE_FIXME("Didn't find assembly root node?\n");
-+        goto done;
-+    }
-+
-+    if ((entry = alloc_assembly()))
-+    {
-+        entry->filename = strdupW(filename);
-+        entry->displayname = get_xml_attribute(root, displaynameW);
-+        if (iter_assembly(root, entry)) goto done;
-+        free_assembly(entry);
-+        entry = NULL;
-+    }
-+
-+done:
-+    IXMLDOMElement_Release(root);
-+    return entry;
-+}
-+
-+/* <unattend><servicing><package> */
-+static BOOL read_update_package(IXMLDOMElement *child, WCHAR *tagname, void *context)
-+{
-+    static const WCHAR sourceW[] = {'s','o','u','r','c','e',0};
-+    static const WCHAR assemblyIdentityW[] = {'a','s','s','e','m','b','l','y','I','d','e','n','t','i','t','y',0};
-+    struct dependency_entry *entry;
-+    struct list *update_list = context;
-+
-+    if (!lstrcmpW(tagname, sourceW)) return TRUE;
-+    if (lstrcmpW(tagname, assemblyIdentityW))
-+    {
-+        WINE_TRACE("Ignoring unexpected tag %s\n", debugstr_w(tagname));
-+        return TRUE;
-+    }
-+
-+    if ((entry = alloc_dependency()))
-+    {
-+        if (read_identity(child, &entry->identity))
-+        {
-+            WINE_TRACE("Found update %s\n", debugstr_w(entry->identity.name));
-+            list_add_tail(update_list, &entry->entry);
-+            return TRUE;
-+        }
-+        free_dependency(entry);
-+    }
-+
-+    return FALSE;
-+}
-+static BOOL iter_update_package(IXMLDOMElement *root, struct list *update_list)
-+{
-+    return call_xml_callbacks(root, read_update_package, update_list);
-+}
-+
-+/* <unattend><servicing> */
-+static BOOL read_servicing(IXMLDOMElement *child, WCHAR *tagname, void *context)
-+{
-+    static const WCHAR packageW[] = {'p','a','c','k','a','g','e',0};
-+    static const WCHAR installW[] = {'i','n','s','t','a','l','l',0};
-+    static const WCHAR actionW[] = {'a','c','t','i','o','n',0};
-+    struct list *update_list = context;
-+    WCHAR *action;
-+    BOOL ret = TRUE;
-+
-+    if (lstrcmpW(tagname, packageW))
-+    {
-+        WINE_FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
-+        return TRUE;
-+    }
-+
-+    if (!(action = get_xml_attribute(child, actionW)))
-+    {
-+        WINE_FIXME("Servicing tag doesn't specify action\n");
-+        return FALSE;
-+    }
-+
-+    if (!lstrcmpW(action, installW))
-+        ret = iter_update_package(child, update_list);
-+    else
-+        WINE_FIXME("action %s not supported\n", debugstr_w(action));
-+
-+    heap_free(action);
-+    return ret;
-+}
-+static BOOL iter_servicing(IXMLDOMElement *root, struct list *update_list)
-+{
-+    return call_xml_callbacks(root, read_servicing, update_list);
-+}
-+
-+/* <unattend> */
-+static BOOL read_unattend(IXMLDOMElement *child, WCHAR *tagname, void *context)
-+{
-+    static const WCHAR servicingW[] = {'s','e','r','v','i','c','i','n','g',0};
-+    struct list *update_list = context;
-+
-+    if (lstrcmpW(tagname, servicingW))
-+    {
-+        WINE_FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
-+        return TRUE;
-+    }
-+
-+    return iter_servicing(child, update_list);
-+
-+}
-+static BOOL iter_unattend(IXMLDOMElement *root, struct list *update_list)
-+{
-+    return call_xml_callbacks(root, read_unattend, update_list);
-+}
-+
-+BOOL load_update(const WCHAR *filename, struct list *update_list)
-+{
-+    static const WCHAR unattendW[] = {'u','n','a','t','t','e','n','d',0};
-+    IXMLDOMElement *root = NULL;
-+    BOOL ret = FALSE;
-+
-+    WINE_TRACE("Reading update %s\n", debugstr_w(filename));
-+
-+    if (!(root = load_xml(filename))) return FALSE;
-+    if (!check_xml_tagname(root, unattendW))
-+    {
-+        WINE_FIXME("Didn't find unattend root node?\n");
-+        goto done;
-+    }
-+
-+    ret = iter_unattend(root, update_list);
-+
-+done:
-+    IXMLDOMElement_Release(root);
-+    return ret;
-+}
-diff --git a/programs/wusa/wusa.h b/programs/wusa/wusa.h
-new file mode 100644
-index 00000000000..0b2b616a7c6
---- /dev/null
-+++ b/programs/wusa/wusa.h
-@@ -0,0 +1,159 @@
-+/*
-+ * Copyright 2015 Michael Müller
-+ * Copyright 2015 Sebastian Lackner
-+ *
-+ * 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 "wine/list.h"
-+#include <windows.h>
-+
-+enum
-+{
-+    ASSEMBLY_STATUS_NONE,
-+    ASSEMBLY_STATUS_IN_PROGRESS,
-+    ASSEMBLY_STATUS_INSTALLED,
-+};
-+
-+struct assembly_identity
-+{
-+    WCHAR                       *name;
-+    WCHAR                       *version;
-+    WCHAR                       *architecture;
-+    WCHAR                       *language;
-+    WCHAR                       *pubkey_token;
-+};
-+
-+struct dependency_entry
-+{
-+    struct list                 entry;
-+    struct assembly_identity    identity;
-+};
-+
-+struct fileop_entry
-+{
-+    struct list                 entry;
-+    WCHAR                       *source;
-+    WCHAR                       *target;
-+};
-+
-+struct registrykv_entry
-+{
-+    struct list                 entry;
-+    WCHAR                      *name;
-+    WCHAR                      *value_type;
-+    WCHAR                      *value;
-+};
-+
-+struct registryop_entry
-+{
-+    struct list                 entry;
-+    WCHAR                       *key;
-+    struct list                 keyvalues;
-+};
-+
-+struct assembly_entry
-+{
-+    struct list                 entry;
-+    DWORD                       status;
-+    WCHAR                       *filename;
-+    WCHAR                       *displayname;
-+    struct assembly_identity    identity;
-+    struct list                 dependencies;
-+
-+    struct list                 fileops;
-+    struct list                 registryops;
-+};
-+
-+void free_assembly(struct assembly_entry *entry) DECLSPEC_HIDDEN;
-+void free_dependency(struct dependency_entry *entry) DECLSPEC_HIDDEN;
-+struct assembly_entry *load_manifest(const WCHAR *filename) DECLSPEC_HIDDEN;
-+BOOL load_update(const WCHAR *filename, struct list *update_list) DECLSPEC_HIDDEN;
-+
-+static void *heap_alloc(size_t len) __WINE_ALLOC_SIZE(1);
-+static inline void *heap_alloc(size_t len)
-+{
-+    return HeapAlloc(GetProcessHeap(), 0, len);
-+}
-+
-+static void *heap_alloc_zero(size_t len) __WINE_ALLOC_SIZE(1);
-+static inline void *heap_alloc_zero(size_t len)
-+{
-+    return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
-+}
-+
-+static void *heap_realloc(void *mem, size_t len) __WINE_ALLOC_SIZE(2);
-+static inline void *heap_realloc(void *mem, size_t len)
-+{
-+    return HeapReAlloc(GetProcessHeap(), 0, mem, len);
-+}
-+
-+static void *heap_realloc_zero(void *mem, size_t len) __WINE_ALLOC_SIZE(2);
-+static inline void *heap_realloc_zero(void *mem, size_t len)
-+{
-+    return HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, mem, len);
-+}
-+
-+static inline BOOL heap_free(void *mem)
-+{
-+    return HeapFree(GetProcessHeap(), 0, mem);
-+}
-+
-+static inline char *strdupWtoA(const WCHAR *str)
-+{
-+    char *ret = NULL;
-+    DWORD len;
-+
-+    if (!str) return ret;
-+    len = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL);
-+    if ((ret = heap_alloc(len)))
-+        WideCharToMultiByte(CP_ACP, 0, str, -1, ret, len, NULL, NULL);
-+    return ret;
-+}
-+
-+static inline WCHAR *strdupAtoW(const char *str)
-+{
-+    WCHAR *ret = NULL;
-+    DWORD len;
-+
-+    if (!str) return ret;
-+    len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
-+    if ((ret = heap_alloc(len * sizeof(WCHAR))))
-+        MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
-+    return ret;
-+}
-+
-+static inline WCHAR *strdupW(const WCHAR *str)
-+{
-+    WCHAR *ret;
-+    if (!str) return NULL;
-+    ret = heap_alloc((lstrlenW(str) + 1) * sizeof(WCHAR));
-+    if (ret)
-+        lstrcpyW(ret, str);
-+    return ret;
-+}
-+
-+static inline WCHAR *strdupWn(const WCHAR *str, DWORD len)
-+{
-+    WCHAR *ret;
-+    if (!str) return NULL;
-+    ret = heap_alloc((len + 1) * sizeof(WCHAR));
-+    if (ret)
-+    {
-+        memcpy(ret, str, len * sizeof(WCHAR));
-+        ret[len] = 0;
-+    }
-+    return ret;
-+}
--- 
-2.17.1
-
diff --git a/patches/wusa-MSU_Package_Installer/0002-wusa-Ignore-systemProtection-subkey-of-registry-key.patch b/patches/wusa-MSU_Package_Installer/0002-wusa-Ignore-systemProtection-subkey-of-registry-key.patch
deleted file mode 100644
index ace28000..00000000
--- a/patches/wusa-MSU_Package_Installer/0002-wusa-Ignore-systemProtection-subkey-of-registry-key.patch
+++ /dev/null
@@ -1,32 +0,0 @@
-From 117b12b0bda9b2a5dd4e9e70bfe7807bcb20ce26 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
-Date: Mon, 21 Dec 2015 01:45:07 +0100
-Subject: [PATCH 2/7] wusa: Ignore systemProtection subkey of registry key.
-
----
- programs/wusa/manifest.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/programs/wusa/manifest.c b/programs/wusa/manifest.c
-index d88b5f98a9..9b43d5e246 100644
---- a/programs/wusa/manifest.c
-+++ b/programs/wusa/manifest.c
-@@ -449,6 +449,7 @@ error:
- static BOOL read_registry_key(IXMLDOMElement *child, WCHAR *tagname, void *context)
- {
-     static const WCHAR securityDescriptorW[] = {'s','e','c','u','r','i','t','y','D','e','s','c','r','i','p','t','o','r',0};
-+    static const WCHAR systemProtectionW[] = {'s','y','s','t','e','m','P','r','o','t','e','c','t','i','o','n',0};
-     static const WCHAR registryValueW[] = {'r','e','g','i','s','t','r','y','V','a','l','u','e',0};
-     static const WCHAR nameW[] = {'n','a','m','e',0};
-     static const WCHAR valueTypeW[] = {'v','a','l','u','e','T','y','p','e',0};
-@@ -457,6 +458,7 @@ static BOOL read_registry_key(IXMLDOMElement *child, WCHAR *tagname, void *conte
-     struct registrykv_entry *entry;
- 
-     if (!lstrcmpW(tagname, securityDescriptorW)) return TRUE;
-+    if (!lstrcmpW(tagname, systemProtectionW)) return TRUE;
-     if (lstrcmpW(tagname, registryValueW))
-     {
-         WINE_FIXME("Ignoring unexpected tag %s\n", debugstr_w(tagname));
--- 
-2.23.0.rc1
-
diff --git a/patches/wusa-MSU_Package_Installer/0003-wusa-Treat-empty-update-list-as-error.patch b/patches/wusa-MSU_Package_Installer/0003-wusa-Treat-empty-update-list-as-error.patch
deleted file mode 100644
index 0c3cddea..00000000
--- a/patches/wusa-MSU_Package_Installer/0003-wusa-Treat-empty-update-list-as-error.patch
+++ /dev/null
@@ -1,29 +0,0 @@
-From ae3350ac731b45fd0b47d50f0e64525ec48803f9 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
-Date: Mon, 21 Dec 2015 01:46:34 +0100
-Subject: [PATCH 3/7] wusa: Treat empty update list as error.
-
----
- programs/wusa/main.c | 6 ++++++
- 1 file changed, 6 insertions(+)
-
-diff --git a/programs/wusa/main.c b/programs/wusa/main.c
-index 0d6e5c345c..837729554f 100644
---- a/programs/wusa/main.c
-+++ b/programs/wusa/main.c
-@@ -997,6 +997,12 @@ static BOOL install_msu(WCHAR *filename, struct installer_state *state)
-         }
-     }
- 
-+    if (list_empty(&state->updates))
-+    {
-+        WINE_ERR("No updates found, probably incompatible MSU file format?\n");
-+        goto done;
-+    }
-+
-     /* perform dryrun */
-     set_assembly_status(&state->assemblies, ASSEMBLY_STATUS_NONE);
-     if (!install_updates(state, TRUE))
--- 
-2.23.0.rc1
-
diff --git a/patches/wusa-MSU_Package_Installer/0004-wusa-Implement-WOW64-support.patch b/patches/wusa-MSU_Package_Installer/0004-wusa-Implement-WOW64-support.patch
deleted file mode 100644
index be093117..00000000
--- a/patches/wusa-MSU_Package_Installer/0004-wusa-Implement-WOW64-support.patch
+++ /dev/null
@@ -1,129 +0,0 @@
-From b9426dcd2d1a01b5f7ca857a5332c102e59dbc40 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
-Date: Mon, 21 Dec 2015 01:47:59 +0100
-Subject: [PATCH] wusa: Implement WOW64 support.
-
----
- programs/wusa/main.c | 62 +++++++++++++++++++++++++++++++++++++++++++-
- 1 file changed, 61 insertions(+), 1 deletion(-)
-
-diff --git a/programs/wusa/main.c b/programs/wusa/main.c
-index db29a3e5078..78b4e18483a 100644
---- a/programs/wusa/main.c
-+++ b/programs/wusa/main.c
-@@ -65,6 +65,12 @@ struct installer_state
-     struct list updates;
- };
- 
-+#if defined(__x86_64__)
-+static const WCHAR x86W[] = {'x','8','6',0};
-+#elif defined(__i386__)
-+static const WCHAR amd64W[] = {'a','m','d','6','4',0};
-+#endif
-+
- static BOOL strbuf_init(struct strbuf *buf)
- {
-     buf->pos = 0;
-@@ -374,6 +380,14 @@ static WCHAR *lookup_expression(struct assembly_entry *assembly, const WCHAR *ke
- 
-     if (!lstrcmpW(key, runtime_system32))
-     {
-+    #if defined(__x86_64__)
-+        if (!lstrcmpW(assembly->identity.architecture, x86W))
-+        {
-+            GetSystemWow64DirectoryW(path, sizeof(path)/sizeof(path[0]));
-+            return strdupW(path);
-+        }
-+    #endif
-+
-         GetSystemDirectoryW(path, sizeof(path)/sizeof(path[0]));
-         return strdupW(path);
-     }
-@@ -690,8 +704,13 @@ static BOOL install_registry(struct assembly_entry *assembly, BOOL dryrun)
-     struct registrykv_entry *registrykv;
-     HKEY root, subkey;
-     WCHAR *path;
-+    REGSAM sam = KEY_ALL_ACCESS;
-     BOOL ret = TRUE;
- 
-+#if defined(__x86_64__)
-+    if (!lstrcmpW(assembly->identity.architecture, x86W)) sam |= KEY_WOW64_32KEY;
-+#endif
-+
-     LIST_FOR_EACH_ENTRY(registryop, &assembly->registryops, struct registryop_entry, entry)
-     {
-         if (!(path = split_registry_key(registryop->key, &root)))
-@@ -700,7 +719,7 @@ static BOOL install_registry(struct assembly_entry *assembly, BOOL dryrun)
-             break;
-         }
- 
--        if (!dryrun && RegCreateKeyExW(root, path, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &subkey, NULL))
-+        if (!dryrun && RegCreateKeyExW(root, path, 0, NULL, REG_OPTION_NON_VOLATILE, sam, NULL, &subkey, NULL))
-         {
-             WINE_ERR("Failed to open registry key %s\n", debugstr_w(registryop->key));
-             ret = FALSE;
-@@ -771,6 +790,14 @@ static BOOL install_assembly(struct list *manifest_list, struct assembly_identit
-         return FALSE;
-     }
- 
-+#if defined(__i386__)
-+    if (!lstrcmpW(assembly->identity.architecture, amd64W))
-+    {
-+        WINE_ERR("Can not install amd64 assembly in 32 bit prefix\n");
-+        return FALSE;
-+    }
-+#endif
-+
-     assembly->status = ASSEMBLY_STATUS_IN_PROGRESS;
- 
-     LIST_FOR_EACH_ENTRY(dependency, &assembly->dependencies, struct dependency_entry, entry)
-@@ -1026,6 +1053,37 @@ done:
-     return ret;
- }
- 
-+static void restart_as_x86_64(void)
-+{
-+    WCHAR filename[MAX_PATH];
-+    PROCESS_INFORMATION pi;
-+    STARTUPINFOW si;
-+    DWORD exit_code = 1;
-+    BOOL is_wow64;
-+    void *redir;
-+
-+    if (!IsWow64Process(GetCurrentProcess(), &is_wow64) || !is_wow64)
-+        return;
-+
-+    memset(&si, 0, sizeof(si));
-+    si.cb = sizeof(si);
-+    GetModuleFileNameW(0, filename, MAX_PATH);
-+
-+    Wow64DisableWow64FsRedirection(&redir);
-+    if (CreateProcessW(filename, GetCommandLineW(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
-+    {
-+        WINE_TRACE("Restarting %s\n", wine_dbgstr_w(filename));
-+        WaitForSingleObject(pi.hProcess, INFINITE);
-+        GetExitCodeProcess(pi.hProcess, &exit_code);
-+        CloseHandle(pi.hProcess);
-+        CloseHandle(pi.hThread);
-+    }
-+    else WINE_ERR("Failed to restart 64-bit %s, err %d\n", wine_dbgstr_w(filename), GetLastError());
-+    Wow64RevertWow64FsRedirection(redir);
-+
-+    ExitProcess(exit_code);
-+}
-+
- int __cdecl wmain(int argc, WCHAR *argv[])
- {
-     static const WCHAR norestartW[] = {'/','n','o','r','e','s','t','a','r','t',0};
-@@ -1034,6 +1092,8 @@ int __cdecl wmain(int argc, WCHAR *argv[])
-     WCHAR *filename = NULL;
-     int i;
- 
-+    restart_as_x86_64();
-+
-     state.norestart = FALSE;
-     state.quiet = FALSE;
- 
--- 
-2.17.1
-
diff --git a/patches/wusa-MSU_Package_Installer/0005-wusa-Add-workaround-to-be-compatible-with-Vista-pack.patch b/patches/wusa-MSU_Package_Installer/0005-wusa-Add-workaround-to-be-compatible-with-Vista-pack.patch
index 7bbf6342..3067ce16 100644
--- a/patches/wusa-MSU_Package_Installer/0005-wusa-Add-workaround-to-be-compatible-with-Vista-pack.patch
+++ b/patches/wusa-MSU_Package_Installer/0005-wusa-Add-workaround-to-be-compatible-with-Vista-pack.patch
@@ -1,8 +1,7 @@
-From 47c2670271bfb03dd6cfac0febb16bdf1d894600 Mon Sep 17 00:00:00 2001
+From b3478336224263e3a09fb3d1324af261b0b5ec07 Mon Sep 17 00:00:00 2001
 From: Sebastian Lackner <sebastian@fds-team.de>
 Date: Mon, 21 Dec 2015 03:01:05 +0100
-Subject: [PATCH 5/7] wusa: Add workaround to be compatible with Vista
- packages.
+Subject: [PATCH] wusa: Add workaround to be compatible with Vista packages.
 
 ---
  programs/wusa/main.c     | 21 +++++++++++++++++++++
@@ -11,11 +10,11 @@ Subject: [PATCH 5/7] wusa: Add workaround to be compatible with Vista
  3 files changed, 44 insertions(+)
 
 diff --git a/programs/wusa/main.c b/programs/wusa/main.c
-index 3598c39660..b612828855 100644
+index 49e7dc753..323b16e93 100644
 --- a/programs/wusa/main.c
 +++ b/programs/wusa/main.c
-@@ -113,6 +113,13 @@ static BOOL strbuf_append(struct strbuf *buf, const WCHAR *str, DWORD len)
-     return TRUE;
+@@ -346,6 +346,13 @@ static void installer_cleanup(struct installer_state *state)
+     }
  }
  
 +static BOOL str_starts_with(const WCHAR *str, const WCHAR *prefix)
@@ -28,7 +27,7 @@ index 3598c39660..b612828855 100644
  static BOOL str_ends_with(const WCHAR *str, const WCHAR *suffix)
  {
      DWORD str_len = lstrlenW(str), suffix_len = lstrlenW(suffix);
-@@ -1005,6 +1012,20 @@ static BOOL install_msu(WCHAR *filename, struct installer_state *state)
+@@ -964,6 +971,20 @@ static BOOL install_msu(const WCHAR *filename, struct installer_state *state)
          FindClose(search);
      }
  
@@ -47,13 +46,13 @@ index 3598c39660..b612828855 100644
 +    }
 +
      /* dump package information (for debugging) */
-     if (WINE_TRACE_ON(wusa))
+     if (TRACE_ON(wusa))
      {
 diff --git a/programs/wusa/manifest.c b/programs/wusa/manifest.c
-index 9b43d5e246..2ca92c2a17 100644
+index e80c11998..0f61afa45 100644
 --- a/programs/wusa/manifest.c
 +++ b/programs/wusa/manifest.c
-@@ -704,3 +704,25 @@ done:
+@@ -671,3 +671,25 @@ done:
      IXMLDOMElement_Release(root);
      return ret;
  }
@@ -80,10 +79,10 @@ index 9b43d5e246..2ca92c2a17 100644
 +    return FALSE;
 +}
 diff --git a/programs/wusa/wusa.h b/programs/wusa/wusa.h
-index 0b2b616a7c..d7f1a471b4 100644
+index dcd784e76..ad9523776 100644
 --- a/programs/wusa/wusa.h
 +++ b/programs/wusa/wusa.h
-@@ -81,6 +81,7 @@ void free_assembly(struct assembly_entry *entry) DECLSPEC_HIDDEN;
+@@ -77,6 +77,7 @@ void free_assembly(struct assembly_entry *entry) DECLSPEC_HIDDEN;
  void free_dependency(struct dependency_entry *entry) DECLSPEC_HIDDEN;
  struct assembly_entry *load_manifest(const WCHAR *filename) DECLSPEC_HIDDEN;
  BOOL load_update(const WCHAR *filename, struct list *update_list) DECLSPEC_HIDDEN;
@@ -92,5 +91,5 @@ index 0b2b616a7c..d7f1a471b4 100644
  static void *heap_alloc(size_t len) __WINE_ALLOC_SIZE(1);
  static inline void *heap_alloc(size_t len)
 -- 
-2.23.0.rc1
+2.23.0
 
diff --git a/patches/wusa-MSU_Package_Installer/0006-wusa-Improve-tracing-of-installation-process.patch b/patches/wusa-MSU_Package_Installer/0006-wusa-Improve-tracing-of-installation-process.patch
deleted file mode 100644
index 4380b0a6..00000000
--- a/patches/wusa-MSU_Package_Installer/0006-wusa-Improve-tracing-of-installation-process.patch
+++ /dev/null
@@ -1,69 +0,0 @@
-From 72d2b2df1ec557664d5266b5b05e160e4d565993 Mon Sep 17 00:00:00 2001
-From: Sebastian Lackner <sebastian@fds-team.de>
-Date: Mon, 21 Dec 2015 04:27:02 +0100
-Subject: [PATCH 6/7] wusa: Improve tracing of installation process.
-
----
- programs/wusa/main.c | 11 +++++++++++
- 1 file changed, 11 insertions(+)
-
-diff --git a/programs/wusa/main.c b/programs/wusa/main.c
-index b612828855..847fd8b2ce 100644
---- a/programs/wusa/main.c
-+++ b/programs/wusa/main.c
-@@ -471,6 +471,8 @@ static BOOL install_files_copy(struct assembly_entry *assembly, const WCHAR *sou
-     }
-     else
-     {
-+        WINE_TRACE("Copying %s -> %s\n", debugstr_w(source), debugstr_w(target));
-+
-         if (!create_parent_directory(target))
-         {
-             WINE_ERR("Failed to create parent directory for %s\n", debugstr_w(target));
-@@ -690,6 +692,8 @@ static BOOL install_registry_value(struct assembly_entry *assembly, HKEY key, st
-     static const WCHAR reg_dwordW[] = {'R','E','G','_','D','W','O','R','D',0};
-     static const WCHAR reg_binaryW[] = {'R','E','G','_','B','I','N','A','R','Y',0};
- 
-+    WINE_TRACE("Setting registry key %s = %s\n", debugstr_w(registrykv->name), debugstr_w(registrykv->value));
-+
-     if (!lstrcmpW(registrykv->value_type, reg_szW))
-         return install_registry_string(assembly, key, registrykv, REG_SZ, dryrun);
-     if (!lstrcmpW(registrykv->value_type, reg_expand_szW))
-@@ -726,6 +730,8 @@ static BOOL install_registry(struct assembly_entry *assembly, BOOL dryrun)
-             break;
-         }
- 
-+        WINE_TRACE("Processing registry key %s\n", debugstr_w(registryop->key));
-+
-         if (!dryrun && RegCreateKeyExW(root, path, 0, NULL, REG_OPTION_NON_VOLATILE, sam, NULL, &subkey, NULL))
-         {
-             WINE_ERR("Failed to open registry key %s\n", debugstr_w(registryop->key));
-@@ -812,6 +818,8 @@ static BOOL install_assembly(struct list *manifest_list, struct assembly_identit
-         if (!install_assembly(manifest_list, &dependency->identity, dryrun)) return FALSE;
-     }
- 
-+    WINE_TRACE("Installing assembly %s%s\n", debugstr_w(name), dryrun ? " (dryrun)" : "");
-+
-     if (!install_files(assembly, dryrun))
-     {
-         WINE_ERR("Failed to install all files for %s\n", debugstr_w(name));
-@@ -824,6 +832,8 @@ static BOOL install_assembly(struct list *manifest_list, struct assembly_identit
-         return FALSE;
-     }
- 
-+    WINE_TRACE("Installation of %s finished\n", debugstr_w(name));
-+
-     assembly->status = ASSEMBLY_STATUS_INSTALLED;
-     return TRUE;
- }
-@@ -1067,6 +1077,7 @@ static BOOL install_msu(WCHAR *filename, struct installer_state *state)
-         goto done;
-     }
- 
-+    WINE_TRACE("Installation finished\n");
-     ret = TRUE;
- 
- done:
--- 
-2.23.0.rc1
-
diff --git a/patches/wusa-MSU_Package_Installer/0007-wusa-Print-warning-when-encountering-msdelta-compres.patch b/patches/wusa-MSU_Package_Installer/0007-wusa-Print-warning-when-encountering-msdelta-compres.patch
deleted file mode 100644
index f5a60345..00000000
--- a/patches/wusa-MSU_Package_Installer/0007-wusa-Print-warning-when-encountering-msdelta-compres.patch
+++ /dev/null
@@ -1,40 +0,0 @@
-From 63ac22a0ee910f8d56bcca6f4ef1b6618b07a888 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
-Date: Mon, 21 Dec 2015 04:09:11 +0100
-Subject: [PATCH 7/7] wusa: Print warning when encountering msdelta compressed
- files.
-
----
- programs/wusa/main.c | 9 +++++++++
- 1 file changed, 9 insertions(+)
-
-diff --git a/programs/wusa/main.c b/programs/wusa/main.c
-index 847fd8b2ce..e9c04387fa 100644
---- a/programs/wusa/main.c
-+++ b/programs/wusa/main.c
-@@ -894,6 +894,7 @@ static void installer_cleanup(struct installer_state *state)
- 
- static BOOL load_assemblies_from_cab(const WCHAR *filename, struct installer_state *state)
- {
-+    static const WCHAR manifest_cix_xmlW[] = {'_','m','a','n','i','f','e','s','t','_','.','c','i','x','.','x','m','l',0};
-     static const WCHAR manifestW[] = {'.','m','a','n','i','f','e','s','t',0};
-     static const WCHAR mumW[] = {'.','m','u','m',0};
-     static const WCHAR starW[] = {'*',0};
-@@ -912,6 +913,14 @@ static BOOL load_assemblies_from_cab(const WCHAR *filename, struct installer_sta
-         return FALSE;
-     }
- 
-+    if (!(path = path_combine(temp_path, manifest_cix_xmlW))) return FALSE;
-+    if (GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES)
-+    {
-+        WINE_FIXME("Cabinet uses proprietary msdelta file compression which is not (yet) supported.\n");
-+        WINE_FIXME("Installation of msu file will most likely fail.\n");
-+    }
-+    heap_free(path);
-+
-     if (!(path = path_combine(temp_path, starW))) return FALSE;
-     search = FindFirstFileW(path, &data);
-     heap_free(path);
--- 
-2.23.0.rc1
-
diff --git a/patches/wusa-MSU_Package_Installer/definition b/patches/wusa-MSU_Package_Installer/definition
index 9d51931b..ea400197 100644
--- a/patches/wusa-MSU_Package_Installer/definition
+++ b/patches/wusa-MSU_Package_Installer/definition
@@ -1,3 +1 @@
-Fixes: [26757] Initial implementation of wusa.exe (MSU package installer)
-Disabled: True
-
+#No idea what this workaround is for.