Implemented a large number of the 32-bit setupapi functions.
Fixed a number of setupx functions by making them call the setupapi
equivalents.

diff --git a/dlls/setupapi/Makefile.in b/dlls/setupapi/Makefile.in
index 1473193..262fd42 100644
--- a/dlls/setupapi/Makefile.in
+++ b/dlls/setupapi/Makefile.in
@@ -5,13 +5,18 @@
 VPATH     = @srcdir@
 MODULE    = setupapi.dll
 ALTNAMES  = setupx.dll
+EXTRALIBS = $(LIBUNICODE)
 
 LDDLLFLAGS = @LDDLLFLAGS@
 SYMBOLFILE = $(MODULE).tmp.o
 
 C_SRCS = \
 	devinst.c \
+	dirid.c \
 	infparse.c \
+	install.c \
+	parser.c \
+	queue.c \
 	setupx_main.c \
 	stubs.c \
 	virtcopy.c
diff --git a/dlls/setupapi/dirid.c b/dlls/setupapi/dirid.c
new file mode 100644
index 0000000..44c98bc
--- /dev/null
+++ b/dlls/setupapi/dirid.c
@@ -0,0 +1,243 @@
+/*
+ * Directory id handling
+ *
+ * Copyright 2002 Alexandre Julliard 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "ntddk.h"
+#include "winerror.h"
+#include "setupapi.h"
+#include "wine/unicode.h"
+#include "setupapi_private.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
+
+#define MAX_SYSTEM_DIRID DIRID_PRINTPROCESSOR
+
+struct user_dirid
+{
+    int    id;
+    WCHAR *str;
+};
+
+static int nb_user_dirids;     /* number of user dirids in use */
+static int alloc_user_dirids;  /* number of allocated user dirids */
+static struct user_dirid *user_dirids;
+static const WCHAR *system_dirids[MAX_SYSTEM_DIRID+1];
+
+/* retrieve the string for unknown dirids */
+static const WCHAR *get_unknown_dirid(void)
+{
+    static WCHAR *unknown_dirid;
+    static const WCHAR unknown_str[] = {'\\','u','n','k','n','o','w','n',0};
+
+    if (!unknown_dirid)
+    {
+        UINT len = GetSystemDirectoryW( NULL, 0 ) + strlenW(unknown_str);
+        if (!(unknown_dirid = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return NULL;
+        GetSystemDirectoryW( unknown_dirid, len );
+        strcatW( unknown_dirid, unknown_str );
+    }
+    return unknown_dirid;
+}
+
+/* create the string for a system dirid */
+static const WCHAR *create_system_dirid( int dirid )
+{
+    static const WCHAR Null[]    = {0};
+    static const WCHAR C_Root[]  = {'C',':','\\',0};
+    static const WCHAR Drivers[] = {'\\','d','r','i','v','e','r','s',0};
+    static const WCHAR Inf[]     = {'\\','i','n','f',0};
+    static const WCHAR Help[]    = {'\\','h','e','l','p',0};
+    static const WCHAR Fonts[]   = {'\\','f','o','n','t','s',0};
+    static const WCHAR Viewers[] = {'\\','v','i','e','w','e','r','s',0};
+    static const WCHAR System[]  = {'\\','s','y','s','t','e','m',0};
+    static const WCHAR Spool[]   = {'\\','s','p','o','o','l',0};
+
+    WCHAR buffer[MAX_PATH+16], *str;
+    int len;
+
+    switch(dirid)
+    {
+    case DIRID_NULL:
+        return Null;
+    case DIRID_WINDOWS:
+        GetWindowsDirectoryW( buffer, MAX_PATH );
+        break;
+    case DIRID_SYSTEM:
+        GetSystemDirectoryW( buffer, MAX_PATH );
+        break;
+    case DIRID_DRIVERS:
+        GetSystemDirectoryW( buffer, MAX_PATH );
+        strcatW( buffer, Drivers );
+        break;
+    case DIRID_INF:
+        GetWindowsDirectoryW( buffer, MAX_PATH );
+        strcatW( buffer, Inf );
+        break;
+    case DIRID_HELP:
+        GetWindowsDirectoryW( buffer, MAX_PATH );
+        strcatW( buffer, Help );
+        break;
+    case DIRID_FONTS:
+        GetWindowsDirectoryW( buffer, MAX_PATH );
+        strcatW( buffer, Fonts );
+        break;
+    case DIRID_VIEWERS:
+        GetSystemDirectoryW( buffer, MAX_PATH );
+        strcatW( buffer, Viewers );
+        break;
+    case DIRID_APPS:
+        return C_Root;  /* FIXME */
+    case DIRID_SHARED:
+        GetWindowsDirectoryW( buffer, MAX_PATH );
+        break;
+    case DIRID_BOOT:
+        return C_Root;  /* FIXME */
+    case DIRID_SYSTEM16:
+        GetWindowsDirectoryW( buffer, MAX_PATH );
+        strcatW( buffer, System );
+        break;
+    case DIRID_SPOOL:
+    case DIRID_SPOOLDRIVERS:  /* FIXME */
+        GetWindowsDirectoryW( buffer, MAX_PATH );
+        strcatW( buffer, Spool );
+        break;
+    case DIRID_LOADER:
+        return C_Root;  /* FIXME */
+    case DIRID_USERPROFILE:  /* FIXME */
+    case DIRID_COLOR:  /* FIXME */
+    case DIRID_PRINTPROCESSOR:  /* FIXME */
+    default:
+        FIXME( "unknwon dirid %d\n", dirid );
+        return get_unknown_dirid();
+    }
+    len = (strlenW(buffer) + 1) * sizeof(WCHAR);
+    if ((str = HeapAlloc( GetProcessHeap(), 0, len ))) memcpy( str, buffer, len );
+    return str;
+}
+
+/* retrieve the string corresponding to a dirid, or NULL if none */
+const WCHAR *DIRID_get_string( HINF hinf, int dirid )
+{
+    int i;
+
+    if (dirid == DIRID_ABSOLUTE || dirid == DIRID_ABSOLUTE_16BIT) dirid = DIRID_NULL;
+
+    if (dirid >= DIRID_USER)
+    {
+        for (i = 0; i < nb_user_dirids; i++)
+            if (user_dirids[i].id == dirid) return user_dirids[i].str;
+        ERR("user id %d not found\n", dirid );
+        return NULL;
+    }
+    else
+    {
+        if (dirid > MAX_SYSTEM_DIRID) return get_unknown_dirid();
+        if (dirid == DIRID_SRCPATH) return PARSER_get_src_root( hinf );
+        if (!system_dirids[dirid]) system_dirids[dirid] = create_system_dirid( dirid );
+        return system_dirids[dirid];
+    }
+}
+
+/* store a user dirid string */
+static BOOL store_user_dirid( HINF hinf, int id, WCHAR *str )
+{
+    int i;
+
+    for (i = 0; i < nb_user_dirids; i++) if (user_dirids[i].id == id) break;
+
+    if (i < nb_user_dirids) HeapFree( GetProcessHeap(), 0, user_dirids[i].str );
+    else
+    {
+        if (nb_user_dirids >= alloc_user_dirids)
+        {
+            int new_size = max( 32, alloc_user_dirids * 2 );
+            struct user_dirid *new = HeapReAlloc( GetProcessHeap(), 0, user_dirids,
+                                                  new_size * sizeof(*new) );
+            if (!new) return FALSE;
+            user_dirids = new;
+            alloc_user_dirids = new_size;
+        }
+        nb_user_dirids++;
+    }
+    user_dirids[i].id  = id;
+    user_dirids[i].str = str;
+    TRACE("id %d -> %s\n", id, debugstr_w(str) );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *		SetupSetDirectoryIdW    (SETUPAPI.@)
+ */
+BOOL WINAPI SetupSetDirectoryIdA( HINF hinf, DWORD id, PCSTR dir )
+{
+    UNICODE_STRING dirW;
+    int i;
+
+    if (!id)  /* clear everything */
+    {
+        for (i = 0; i < nb_user_dirids; i++) HeapFree( GetProcessHeap(), 0, user_dirids[i].str );
+        nb_user_dirids = 0;
+        return TRUE;
+    }
+    if (id < DIRID_USER)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
+
+    /* duplicate the string */
+    if (!RtlCreateUnicodeStringFromAsciiz( &dirW, dir ))
+    {
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+        return FALSE;
+    }
+    return store_user_dirid( hinf, id, dirW.Buffer );
+}
+
+
+/***********************************************************************
+ *		SetupSetDirectoryIdW    (SETUPAPI.@)
+ */
+BOOL WINAPI SetupSetDirectoryIdW( HINF hinf, DWORD id, PCWSTR dir )
+{
+    int i, len;
+    WCHAR *str;
+
+    if (!id)  /* clear everything */
+    {
+        for (i = 0; i < nb_user_dirids; i++) HeapFree( GetProcessHeap(), 0, user_dirids[i].str );
+        nb_user_dirids = 0;
+        return TRUE;
+    }
+    if (id < DIRID_USER)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
+
+    /* duplicate the string */
+    len = (strlenW(dir)+1) * sizeof(WCHAR);
+    if (!(str = HeapAlloc( GetProcessHeap(), 0, len ))) return FALSE;
+    memcpy( str, dir, len );
+    return store_user_dirid( hinf, id, str );
+}
diff --git a/dlls/setupapi/infparse.c b/dlls/setupapi/infparse.c
index 79abff4..e203265 100644
--- a/dlls/setupapi/infparse.c
+++ b/dlls/setupapi/infparse.c
@@ -26,46 +26,175 @@
  */
 
 #include <string.h>
-#include "wine/debug.h"
 #include "windef.h"
 #include "winbase.h"
+#include "ntddk.h"
 #include "wine/winbase16.h"
+#include "setupapi.h"
 #include "setupx16.h"
 #include "setupapi_private.h"
+#include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
 
+#define MAX_HANDLES 16384
+#define FIRST_HANDLE 32
+
+static HINF handles[MAX_HANDLES];
+
+
+static RETERR16 alloc_hinf16( HINF hinf, HINF16 *hinf16 )
+{
+    int i;
+    for (i = 0; i < MAX_HANDLES; i++)
+    {
+        if (!handles[i])
+        {
+            handles[i] = hinf;
+            *hinf16 = i + FIRST_HANDLE;
+            return OK;
+        }
+    }
+    return ERR_IP_OUT_OF_HANDLES;
+}
+
+static HINF get_hinf( HINF16 hinf16 )
+{
+    int idx = hinf16 - FIRST_HANDLE;
+    if (idx < 0 || idx >= MAX_HANDLES) return 0;
+    return handles[idx];
+}
+
+
+static HINF free_hinf16( HINF16 hinf16 )
+{
+    HINF ret;
+    int idx = hinf16 - FIRST_HANDLE;
+
+    if (idx < 0 || idx >= MAX_HANDLES) return 0;
+    ret = handles[idx];
+    handles[idx] = 0;
+    return ret;
+}
+
+/* convert last error code to a RETERR16 value */
+static RETERR16 get_last_error(void)
+{
+    switch(GetLastError())
+    {
+    case ERROR_EXPECTED_SECTION_NAME:
+    case ERROR_BAD_SECTION_NAME_LINE:
+    case ERROR_SECTION_NAME_TOO_LONG: return ERR_IP_INVALID_SECT_NAME;
+    case ERROR_SECTION_NOT_FOUND: return ERR_IP_SECT_NOT_FOUND;
+    case ERROR_LINE_NOT_FOUND: return ERR_IP_LINE_NOT_FOUND;
+    default: return IP_ERROR;  /* FIXME */
+    }
+}
+
+
+/***********************************************************************
+ *		IpOpen (SETUPX.2)
+ *
+ */
+RETERR16 WINAPI IpOpen16( LPCSTR filename, HINF16 *hinf16 )
+{
+    HINF hinf = SetupOpenInfFileA( filename, NULL, INF_STYLE_WIN4, NULL );
+    if (hinf == (HINF)INVALID_HANDLE_VALUE) return get_last_error();
+    return alloc_hinf16( hinf, hinf16 );
+}
+
+
+/***********************************************************************
+ *		IpClose (SETUPX.4)
+ */
+RETERR16 WINAPI IpClose16( HINF16 hinf16 )
+{
+    HINF hinf = free_hinf16( hinf16 );
+    if (!hinf) return ERR_IP_INVALID_HINF;
+    SetupCloseInfFile( hinf );
+    return OK;
+}
+
+
+/***********************************************************************
+ *		IpGetProfileString (SETUPX.210)
+ */
+RETERR16 WINAPI IpGetProfileString16( HINF16 hinf16, LPCSTR section, LPCSTR entry,
+                                      LPSTR buffer, WORD buflen )
+{
+    DWORD required_size;
+    HINF hinf = get_hinf( hinf16 );
+
+    if (!hinf) return ERR_IP_INVALID_HINF;
+    if (!SetupGetLineTextA( NULL, hinf, section, entry, buffer, buflen, &required_size ))
+        return get_last_error();
+    TRACE("%p: section %s entry %s ret %s\n",
+          hinf, debugstr_a(section), debugstr_a(entry), debugstr_a(buffer) );
+    return OK;
+}
+
+
+/***********************************************************************
+ *		GenFormStrWithoutPlaceHolders (SETUPX.103)
+ *
+ * ought to be pretty much implemented, I guess...
+ */
+void WINAPI GenFormStrWithoutPlaceHolders16( LPSTR dst, LPCSTR src, HINF16 hinf16 )
+{
+    UNICODE_STRING srcW;
+    HINF hinf = get_hinf( hinf16 );
+
+    if (!hinf) return;
+
+    if (!RtlCreateUnicodeStringFromAsciiz( &srcW, src )) return;
+    PARSER_string_substA( hinf, srcW.Buffer, dst, MAX_INF_STRING_LENGTH );
+    RtlFreeUnicodeString( &srcW );
+    TRACE( "%s -> %s\n", debugstr_a(src), debugstr_a(dst) );
+}
+
+/***********************************************************************
+ *		GenInstall (SETUPX.101)
+ *
+ * generic installer function for .INF file sections
+ *
+ * This is not perfect - patch whenever you can !
+ *
+ * wFlags == GENINSTALL_DO_xxx
+ * e.g. NetMeeting:
+ * first call GENINSTALL_DO_REGSRCPATH | GENINSTALL_DO_FILES,
+ * second call GENINSTALL_DO_LOGCONFIG | CFGAUTO | INI2REG | REG | INI
+ */
+RETERR16 WINAPI GenInstall16( HINF16 hinf16, LPCSTR section, WORD genflags )
+{
+    UINT flags = 0;
+    HINF hinf = get_hinf( hinf16 );
+    RETERR16 ret = OK;
+    void *context;
+
+    if (!hinf) return ERR_IP_INVALID_HINF;
+
+    if (genflags & GENINSTALL_DO_FILES) flags |= SPINST_FILES;
+    if (genflags & GENINSTALL_DO_INI) flags |= SPINST_INIFILES;
+    if (genflags & GENINSTALL_DO_REG) flags |= SPINST_REGISTRY;
+    if (genflags & GENINSTALL_DO_INI2REG) flags |= SPINST_INI2REG;
+    if (genflags & GENINSTALL_DO_LOGCONFIG) flags |= SPINST_LOGCONFIG;
+    if (genflags & (GENINSTALL_DO_REGSRCPATH|GENINSTALL_DO_CFGAUTO|GENINSTALL_DO_PERUSER))
+        FIXME( "unsupported flags %x\n", genflags );
+
+    context = SetupInitDefaultQueueCallback( 0 );
+    if (!SetupInstallFromInfSectionA( 0, hinf, section, flags, 0, NULL, 0,
+                                      SetupDefaultQueueCallbackA, context, 0, 0 ))
+        ret = get_last_error();
+
+    SetupTermDefaultQueueCallback( context );
+    return ret;
+}
+
+
 WORD InfNumEntries = 0;
 INF_FILE *InfList = NULL;
 HINF16 IP_curr_handle = 0;
 
-RETERR16 IP_OpenInf(LPCSTR lpInfFileName, HINF16 *lphInf)
-{
-    HFILE hFile = _lopen(lpInfFileName, OF_READ);
-
-    if (!lphInf)
-	return IP_ERROR;
-
-    /* this could be improved by checking for already freed handles */
-    if (IP_curr_handle == 0xffff) 
-	return ERR_IP_OUT_OF_HANDLES; 
-
-    if (hFile != HFILE_ERROR)
-    {
-	InfList = HeapReAlloc(GetProcessHeap(), 0, InfList, InfNumEntries+1);
-	InfList[InfNumEntries].hInf = IP_curr_handle++;
-	InfList[InfNumEntries].hInfFile = hFile;
-	InfList[InfNumEntries].lpInfFileName = HeapAlloc( GetProcessHeap(), 0,
-                                                          strlen(lpInfFileName)+1);
-        strcpy( InfList[InfNumEntries].lpInfFileName, lpInfFileName );
-        *lphInf = InfList[InfNumEntries].hInf;
-	InfNumEntries++;
-	TRACE("ret handle %d.\n", *lphInf);
-	return OK;
-    }
-    *lphInf = 0xffff;
-    return ERR_IP_INVALID_INFFILE;
-}
 
 BOOL IP_FindInf(HINF16 hInf, WORD *ret)
 {
@@ -91,49 +220,4 @@
     return NULL;
 }
 
-RETERR16 IP_CloseInf(HINF16 hInf)
-{
-    int i;
-    WORD n;
-    RETERR16 res = ERR_IP_INVALID_HINF;
 
-    if (IP_FindInf(hInf, &n))
-    {
-	_lclose(InfList[n].hInfFile);
-	HeapFree(GetProcessHeap(), 0, InfList[n].lpInfFileName);
-	for (i=n; i < InfNumEntries-1; i++)
-	    InfList[i] = InfList[i+1];
-	InfNumEntries--;
-	InfList = HeapReAlloc(GetProcessHeap(), 0, InfList, InfNumEntries);
-	res = OK;
-    }
-    return res;
-}
-
-/***********************************************************************
- *		IpOpen (SETUPX.2)
- *
- */
-RETERR16 WINAPI IpOpen16(LPCSTR lpInfFileName, HINF16 *lphInf)
-{
-    TRACE("('%s', %p)\n", lpInfFileName, lphInf);
-    return IP_OpenInf(lpInfFileName, lphInf);
-}
-
-/***********************************************************************
- *		IpClose (SETUPX.4)
- */
-RETERR16 WINAPI IpClose16(HINF16 hInf)
-{
-    return IP_CloseInf(hInf);
-}
-
-/***********************************************************************
- *		IpGetProfileString (SETUPX.210)
- */
-RETERR16 WINAPI IpGetProfileString16(HINF16 hInf, LPCSTR section, LPCSTR entry, LPSTR buffer, WORD buflen) 
-{
-    TRACE("'%s': section '%s' entry '%s'\n", IP_GetFileName(hInf), section, entry);
-    GetPrivateProfileStringA(section, entry, "", buffer, buflen, IP_GetFileName(hInf));
-    return 0;
-}
diff --git a/dlls/setupapi/install.c b/dlls/setupapi/install.c
new file mode 100644
index 0000000..9390d04
--- /dev/null
+++ b/dlls/setupapi/install.c
@@ -0,0 +1,623 @@
+/*
+ * Setupapi install routines
+ *
+ * Copyright 2002 Alexandre Julliard 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "ntddk.h"
+#include "winerror.h"
+#include "setupapi.h"
+#include "wine/unicode.h"
+#include "setupapi_private.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
+
+/* info passed to callback functions dealing with files */
+struct files_callback_info
+{
+    HSPFILEQ queue;
+    PCWSTR   src_root;
+    UINT     copy_flags;
+    HINF     layout;
+};
+
+/* info passed to callback functions dealing with the registry */
+struct registry_callback_info
+{
+    HKEY default_root;
+    BOOL delete;
+};
+
+typedef BOOL (*iterate_fields_func)( HINF hinf, PCWSTR field, void *arg );
+
+/* Unicode constants */
+static const WCHAR CopyFiles[]  = {'C','o','p','y','F','i','l','e','s',0};
+static const WCHAR DelFiles[]   = {'D','e','l','F','i','l','e','s',0};
+static const WCHAR RenFiles[]   = {'R','e','n','F','i','l','e','s',0};
+static const WCHAR Ini2Reg[]    = {'I','n','i','2','R','e','g',0};
+static const WCHAR LogConf[]    = {'L','o','g','C','o','n','f',0};
+static const WCHAR AddReg[]     = {'A','d','d','R','e','g',0};
+static const WCHAR DelReg[]     = {'D','e','l','R','e','g',0};
+static const WCHAR UpdateInis[] = {'U','p','d','a','t','e','I','n','i','s',0};
+static const WCHAR UpdateIniFields[] = {'U','p','d','a','t','e','I','n','i','F','i','e','l','d','s',0};
+
+
+/***********************************************************************
+ *            get_field_string
+ *
+ * Retrieve the contents of a field, dynamically growing the buffer if necessary.
+ */
+static WCHAR *get_field_string( INFCONTEXT *context, DWORD index, WCHAR *buffer,
+                                WCHAR *static_buffer, DWORD *size )
+{
+    DWORD required;
+
+    if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
+    if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+    {
+        /* now grow the buffer */
+        if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
+        if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required*sizeof(WCHAR) ))) return NULL;
+        *size = required;
+        if (SetupGetStringFieldW( context, index, buffer, *size, &required )) return buffer;
+    }
+    if (buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
+    return NULL;
+}
+
+
+/***********************************************************************
+ *            copy_files_callback
+ *
+ * Called once for each CopyFiles entry in a given section.
+ */
+static BOOL copy_files_callback( HINF hinf, PCWSTR field, void *arg )
+{
+    struct files_callback_info *info = arg;
+
+    if (field[0] == '@')  /* special case: copy single file */
+        SetupQueueDefaultCopyW( info->queue, info->layout, info->src_root, NULL, field, info->copy_flags );
+    else
+        SetupQueueCopySectionW( info->queue, info->src_root, info->layout, hinf, field, info->copy_flags );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            delete_files_callback
+ *
+ * Called once for each DelFiles entry in a given section.
+ */
+static BOOL delete_files_callback( HINF hinf, PCWSTR field, void *arg )
+{
+    struct files_callback_info *info = arg;
+    SetupQueueDeleteSectionW( info->queue, hinf, 0, field );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            rename_files_callback
+ *
+ * Called once for each RenFiles entry in a given section.
+ */
+static BOOL rename_files_callback( HINF hinf, PCWSTR field, void *arg )
+{
+    struct files_callback_info *info = arg;
+    SetupQueueRenameSectionW( info->queue, hinf, 0, field );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            get_root_key
+ *
+ * Retrieve the registry root key from its name.
+ */
+static HKEY get_root_key( const WCHAR *name, HKEY def_root )
+{
+    static const WCHAR HKCR[] = {'H','K','C','R',0};
+    static const WCHAR HKCU[] = {'H','K','C','U',0};
+    static const WCHAR HKLM[] = {'H','K','L','M',0};
+    static const WCHAR HKU[]  = {'H','K','U',0};
+    static const WCHAR HKR[]  = {'H','K','R',0};
+
+    if (!strcmpiW( name, HKCR )) return HKEY_CLASSES_ROOT;
+    if (!strcmpiW( name, HKCU )) return HKEY_CURRENT_USER;
+    if (!strcmpiW( name, HKLM )) return HKEY_LOCAL_MACHINE;
+    if (!strcmpiW( name, HKU )) return HKEY_USERS;
+    if (!strcmpiW( name, HKR )) return def_root;
+    return 0;
+}
+
+
+/***********************************************************************
+ *            append_multi_sz_value
+ *
+ * Append a multisz string to a multisz registry value.
+ */
+static void append_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *strings,
+                                   DWORD str_size )
+{
+    DWORD size, type, total;
+    WCHAR *buffer, *p;
+
+    if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
+    if (type != REG_MULTI_SZ) return;
+
+    if (!(buffer = HeapAlloc( GetProcessHeap(), 0, (size + str_size) * sizeof(WCHAR) ))) return;
+    if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
+
+    /* compare each string against all the existing ones */
+    total = size;
+    while (*strings)
+    {
+        int len = strlenW(strings) + 1;
+
+        for (p = buffer; *p; p += strlenW(p) + 1)
+            if (!strcmpiW( p, strings )) break;
+
+        if (!*p)  /* not found, need to append it */
+        {
+            memcpy( p, strings, len * sizeof(WCHAR) );
+            p[len] = 0;
+            total += len;
+        }
+        strings += len;
+    }
+    if (total != size)
+    {
+        TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer) );
+        RegSetValueExW( hkey, value, 0, REG_MULTI_SZ, (BYTE *)buffer, total );
+    }
+ done:
+    HeapFree( GetProcessHeap(), 0, buffer );
+}
+
+
+/***********************************************************************
+ *            delete_multi_sz_value
+ *
+ * Remove a string from a multisz registry value.
+ */
+static void delete_multi_sz_value( HKEY hkey, const WCHAR *value, const WCHAR *string )
+{
+    DWORD size, type;
+    WCHAR *buffer, *src, *dst;
+
+    if (RegQueryValueExW( hkey, value, NULL, &type, NULL, &size )) return;
+    if (type != REG_MULTI_SZ) return;
+    /* allocate double the size, one for value before and one for after */
+    if (!(buffer = HeapAlloc( GetProcessHeap(), 0, size * 2 * sizeof(WCHAR) ))) return;
+    if (RegQueryValueExW( hkey, value, NULL, NULL, (BYTE *)buffer, &size )) goto done;
+    src = buffer;
+    dst = buffer + size;
+    while (*src)
+    {
+        int len = strlenW(src) + 1;
+        if (strcmpiW( src, string ))
+        {
+            memcpy( dst, src, len * sizeof(WCHAR) );
+            dst += len;
+        }
+        src += len;
+    }
+    *dst++ = 0;
+    if (dst != buffer + 2*size)  /* did we remove something? */
+    {
+        TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(buffer + size) );
+        RegSetValueExW( hkey, value, 0, REG_MULTI_SZ,
+                        (BYTE *)(buffer + size), dst - (buffer + size) );
+    }
+ done:
+    HeapFree( GetProcessHeap(), 0, buffer );
+}
+
+
+/***********************************************************************
+ *            do_reg_operation
+ *
+ * Perform an add/delete registry operation depending on the flags.
+ */
+static BOOL do_reg_operation( HKEY hkey, const WCHAR *value, INFCONTEXT *context, INT flags )
+{
+    DWORD type, size;
+
+    if (flags & (FLG_ADDREG_DELREG_BIT | FLG_ADDREG_DELVAL))  /* deletion */
+    {
+        if (*value && !(flags & FLG_DELREG_KEYONLY_COMMON))
+        {
+            if ((flags & FLG_DELREG_MULTI_SZ_DELSTRING) == FLG_DELREG_MULTI_SZ_DELSTRING)
+            {
+                WCHAR *str;
+
+                if (!SetupGetStringFieldW( context, 5, NULL, 0, &size ) || !size) return TRUE;
+                if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
+                SetupGetStringFieldW( context, 5, str, size, NULL );
+                delete_multi_sz_value( hkey, value, str );
+                HeapFree( GetProcessHeap(), 0, str );
+            }
+            else RegDeleteValueW( hkey, value );
+        }
+        else RegDeleteKeyW( hkey, NULL );
+        return TRUE;
+    }
+
+    if (flags & (FLG_ADDREG_KEYONLY|FLG_ADDREG_KEYONLY_COMMON)) return TRUE;
+
+    if (flags & (FLG_ADDREG_NOCLOBBER|FLG_ADDREG_OVERWRITEONLY))
+    {
+        BOOL exists = !RegQueryValueExW( hkey, value, NULL, NULL, NULL, NULL );
+        if (exists && (flags & FLG_ADDREG_NOCLOBBER)) return TRUE;
+        if (!exists & (flags & FLG_ADDREG_OVERWRITEONLY)) return TRUE;
+    }
+
+    switch(flags & FLG_ADDREG_TYPE_MASK)
+    {
+    case FLG_ADDREG_TYPE_SZ:        type = REG_SZ; break;
+    case FLG_ADDREG_TYPE_MULTI_SZ:  type = REG_MULTI_SZ; break;
+    case FLG_ADDREG_TYPE_EXPAND_SZ: type = REG_EXPAND_SZ; break;
+    case FLG_ADDREG_TYPE_BINARY:    type = REG_BINARY; break;
+    case FLG_ADDREG_TYPE_DWORD:     type = REG_DWORD; break;
+    case FLG_ADDREG_TYPE_NONE:      type = REG_NONE; break;
+    default:                        type = flags >> 16; break;
+    }
+
+    if (!(flags & FLG_ADDREG_BINVALUETYPE) ||
+        (type == REG_DWORD && SetupGetFieldCount(context) == 5))
+    {
+        static const WCHAR empty;
+        WCHAR *str = NULL;
+
+        if (type == REG_MULTI_SZ)
+        {
+            if (!SetupGetMultiSzFieldW( context, 5, NULL, 0, &size )) size = 0;
+            if (size)
+            {
+                if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
+                SetupGetMultiSzFieldW( context, 5, str, size, NULL );
+            }
+            if (flags & FLG_ADDREG_APPEND)
+            {
+                if (!str) return TRUE;
+                append_multi_sz_value( hkey, value, str, size );
+                HeapFree( GetProcessHeap(), 0, str );
+                return TRUE;
+            }
+            /* else fall through to normal string handling */
+        }
+        else
+        {
+            if (!SetupGetStringFieldW( context, 5, NULL, 0, &size )) size = 0;
+            if (size)
+            {
+                if (!(str = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) ))) return FALSE;
+                SetupGetStringFieldW( context, 5, str, size, NULL );
+            }
+        }
+
+        if (type == REG_DWORD)
+        {
+            DWORD dw = str ? wcstol( str, NULL, 16 ) : 0;
+            TRACE( "setting dword %s to %lx\n", debugstr_w(value), dw );
+            RegSetValueExW( hkey, value, 0, type, (BYTE *)&dw, sizeof(dw) );
+        }
+        else
+        {
+            TRACE( "setting value %s to %s\n", debugstr_w(value), debugstr_w(str) );
+            if (str) RegSetValueExW( hkey, value, 0, type, (BYTE *)str, size * sizeof(WCHAR) );
+            else RegSetValueExW( hkey, value, 0, type, (BYTE *)&empty, sizeof(WCHAR) );
+        }
+        HeapFree( GetProcessHeap(), 0, str );
+        return TRUE;
+    }
+    else  /* get the binary data */
+    {
+        BYTE *data = NULL;
+
+        if (!SetupGetBinaryField( context, 5, NULL, 0, &size )) size = 0;
+        if (size)
+        {
+            if (!(data = HeapAlloc( GetProcessHeap(), 0, size ))) return FALSE;
+            TRACE( "setting binary data %s len %ld\n", debugstr_w(value), size );
+            SetupGetBinaryField( context, 5, data, size, NULL );
+        }
+        RegSetValueExW( hkey, value, 0, type, data, size );
+        HeapFree( GetProcessHeap(), 0, data );
+        return TRUE;
+    }
+}
+
+
+/***********************************************************************
+ *            registry_callback
+ *
+ * Called once for each AddReg and DelReg entry in a given section.
+ */
+static BOOL registry_callback( HINF hinf, PCWSTR field, void *arg )
+{
+    struct registry_callback_info *info = arg;
+    INFCONTEXT context;
+    HKEY root_key, hkey;
+
+    BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
+
+    for (; ok; ok = SetupFindNextLine( &context, &context ))
+    {
+        WCHAR buffer[MAX_INF_STRING_LENGTH];
+        INT flags;
+
+        /* get root */
+        if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
+            continue;
+        if (!(root_key = get_root_key( buffer, info->default_root )))
+            continue;
+
+        /* get key */
+        if (!SetupGetStringFieldW( &context, 2, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
+            *buffer = 0;
+
+        /* get flags */
+        if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
+
+        if (!info->delete)
+        {
+            if (flags & FLG_ADDREG_DELREG_BIT) continue;  /* ignore this entry */
+        }
+        else
+        {
+            if (!flags) flags = FLG_ADDREG_DELREG_BIT;
+            else if (!(flags & FLG_ADDREG_DELREG_BIT)) continue;  /* ignore this entry */
+        }
+
+        if (info->delete || (flags & FLG_ADDREG_OVERWRITEONLY))
+        {
+            if (RegOpenKeyW( root_key, buffer, &hkey )) continue;  /* ignore if it doesn't exist */
+        }
+        else if (RegCreateKeyW( root_key, buffer, &hkey ))
+        {
+            ERR( "could not create key %08x %s\n", root_key, debugstr_w(buffer) );
+            continue;
+        }
+        TRACE( "key %08x %s\n", root_key, debugstr_w(buffer) );
+
+        /* get value name */
+        if (!SetupGetStringFieldW( &context, 3, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
+            *buffer = 0;
+
+        /* and now do it */
+        if (!do_reg_operation( hkey, buffer, &context, flags ))
+        {
+            RegCloseKey( hkey );
+            return FALSE;
+        }
+        RegCloseKey( hkey );
+    }
+    return TRUE;
+}
+
+
+static BOOL update_ini_callback( HINF hinf, PCWSTR field, void *arg )
+{
+    FIXME( "should update ini %s\n", debugstr_w(field) );
+    return TRUE;
+}
+
+static BOOL update_ini_fields_callback( HINF hinf, PCWSTR field, void *arg )
+{
+    FIXME( "should update ini fields %s\n", debugstr_w(field) );
+    return TRUE;
+}
+
+static BOOL ini2reg_callback( HINF hinf, PCWSTR field, void *arg )
+{
+    FIXME( "should do ini2reg %s\n", debugstr_w(field) );
+    return TRUE;
+}
+
+static BOOL logconf_callback( HINF hinf, PCWSTR field, void *arg )
+{
+    FIXME( "should do logconf %s\n", debugstr_w(field) );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            iterate_section_fields
+ *
+ * Iterate over all fields of a certain key of a certain section
+ */
+static BOOL iterate_section_fields( HINF hinf, PCWSTR section, PCWSTR key,
+                                    iterate_fields_func callback, void *arg )
+{
+    WCHAR static_buffer[200];
+    WCHAR *buffer = static_buffer;
+    DWORD size = sizeof(static_buffer)/sizeof(WCHAR);
+    INFCONTEXT context;
+    BOOL ret = FALSE;
+
+    BOOL ok = SetupFindFirstLineW( hinf, section, key, &context );
+    while (ok)
+    {
+        UINT i, count = SetupGetFieldCount( &context );
+        for (i = 1; i <= count; i++)
+        {
+            if (!(buffer = get_field_string( &context, i, buffer, static_buffer, &size )))
+                goto done;
+            if (!callback( hinf, buffer, arg ))
+            {
+                ERR("callback failed for %s %s\n", debugstr_w(section), debugstr_w(buffer) );
+                goto done;
+            }
+        }
+        ok = SetupFindNextMatchLineW( &context, key, &context );
+    }
+    ret = TRUE;
+ done:
+    if (buffer && buffer != static_buffer) HeapFree( GetProcessHeap(), 0, buffer );
+    return ret;
+}
+
+
+/***********************************************************************
+ *            SetupInstallFilesFromInfSectionA   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupInstallFilesFromInfSectionA( HINF hinf, HINF hlayout, HSPFILEQ queue,
+                                              PCSTR section, PCSTR src_root, UINT flags )
+{
+    UNICODE_STRING sectionW;
+    BOOL ret = FALSE;
+
+    if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
+    {
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+        return FALSE;
+    }
+    if (!src_root)
+        ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
+                                                NULL, flags );
+    else
+    {
+        UNICODE_STRING srcW;
+        if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
+        {
+            ret = SetupInstallFilesFromInfSectionW( hinf, hlayout, queue, sectionW.Buffer,
+                                                    srcW.Buffer, flags );
+            RtlFreeUnicodeString( &srcW );
+        }
+        else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+    }
+    RtlFreeUnicodeString( &sectionW );
+    return ret;
+}
+
+
+/***********************************************************************
+ *            SetupInstallFilesFromInfSectionW   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupInstallFilesFromInfSectionW( HINF hinf, HINF hlayout, HSPFILEQ queue,
+                                              PCWSTR section, PCWSTR src_root, UINT flags )
+{
+    struct files_callback_info info;
+
+    info.queue      = queue;
+    info.src_root   = src_root;
+    info.copy_flags = flags;
+    info.layout     = hlayout;
+    return iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info );
+}
+
+
+/***********************************************************************
+ *            SetupInstallFromInfSectionA   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupInstallFromInfSectionA( HWND owner, HINF hinf, PCSTR section, UINT flags,
+                                         HKEY key_root, PCSTR src_root, UINT copy_flags,
+                                         PSP_FILE_CALLBACK_A callback, PVOID context,
+                                         HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
+{
+    UNICODE_STRING sectionW, src_rootW;
+    struct callback_WtoA_context ctx;
+    BOOL ret = FALSE;
+
+    src_rootW.Buffer = NULL;
+    if (src_root && !RtlCreateUnicodeStringFromAsciiz( &src_rootW, src_root ))
+    {
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+        return FALSE;
+    }
+
+    if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
+    {
+        ctx.orig_context = context;
+        ctx.orig_handler = callback;
+        ret = SetupInstallFromInfSectionW( owner, hinf, sectionW.Buffer, flags, key_root,
+                                           src_rootW.Buffer, copy_flags, QUEUE_callback_WtoA,
+                                           &ctx, devinfo, devinfo_data );
+        RtlFreeUnicodeString( &sectionW );
+    }
+    else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+
+    RtlFreeUnicodeString( &src_rootW );
+    return ret;
+}
+
+
+/***********************************************************************
+ *            SetupInstallFromInfSectionW   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupInstallFromInfSectionW( HWND owner, HINF hinf, PCWSTR section, UINT flags,
+                                         HKEY key_root, PCWSTR src_root, UINT copy_flags,
+                                         PSP_FILE_CALLBACK_W callback, PVOID context,
+                                         HDEVINFO devinfo, PSP_DEVINFO_DATA devinfo_data )
+{
+    if (flags & SPINST_FILES)
+    {
+        struct files_callback_info info;
+        HSPFILEQ queue;
+        BOOL ret;
+
+        if (!(queue = SetupOpenFileQueue())) return FALSE;
+        info.queue      = queue;
+        info.src_root   = src_root;
+        info.copy_flags = copy_flags;
+        info.layout     = hinf;
+        ret = (iterate_section_fields( hinf, section, CopyFiles, copy_files_callback, &info ) &&
+               iterate_section_fields( hinf, section, DelFiles, delete_files_callback, &info ) &&
+               iterate_section_fields( hinf, section, RenFiles, rename_files_callback, &info ) &&
+               SetupCommitFileQueueW( owner, queue, callback, context ));
+        SetupCloseFileQueue( queue );
+        if (!ret) return FALSE;
+    }
+    if (flags & SPINST_INIFILES)
+    {
+        if (!iterate_section_fields( hinf, section, UpdateInis, update_ini_callback, NULL ) ||
+            !iterate_section_fields( hinf, section, UpdateIniFields,
+                                     update_ini_fields_callback, NULL ))
+            return FALSE;
+    }
+    if (flags & SPINST_INI2REG)
+    {
+        if (!iterate_section_fields( hinf, section, Ini2Reg, ini2reg_callback, NULL ))
+            return FALSE;
+    }
+
+    if (flags & SPINST_LOGCONFIG)
+    {
+        if (!iterate_section_fields( hinf, section, LogConf, logconf_callback, NULL ))
+            return FALSE;
+    }
+
+    if (flags & SPINST_REGISTRY)
+    {
+        struct registry_callback_info info;
+
+        info.default_root = key_root;
+        info.delete = FALSE;
+        if (!iterate_section_fields( hinf, section, AddReg, registry_callback, &info ))
+            return FALSE;
+        info.delete = TRUE;
+        if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info ))
+            return FALSE;
+    }
+    if (flags & (SPINST_BITREG|SPINST_REGSVR|SPINST_UNREGSVR|SPINST_PROFILEITEMS|SPINST_COPYINF))
+        FIXME( "unsupported flags %x\n", flags );
+    return TRUE;
+}
diff --git a/dlls/setupapi/parser.c b/dlls/setupapi/parser.c
new file mode 100644
index 0000000..68c10f5
--- /dev/null
+++ b/dlls/setupapi/parser.c
@@ -0,0 +1,1766 @@
+/*
+ * INF file parsing
+ *
+ * Copyright 2002 Alexandre Julliard 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "windef.h"
+#include "ntddk.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/unicode.h"
+#include "setupapi.h"
+#include "setupx16.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
+
+#define CONTROL_Z  '\x1a'
+#define MAX_SECTION_NAME_LEN  255
+#define MAX_FIELD_LEN         511  /* larger fields get silently truncated */
+/* actual string limit is MAX_INF_STRING_LENGTH+1 (plus terminating null) under Windows */
+#define MAX_STRING_LEN        (MAX_INF_STRING_LENGTH+1)
+
+/* inf file structure definitions */
+
+struct field
+{
+    const WCHAR *text;         /* field text */
+};
+
+struct line
+{
+    int first_field;           /* index of first field in field array */
+    int nb_fields;             /* number of fields in line */
+    int key_field;             /* index of field for key or -1 if no key */
+};
+
+struct section
+{
+    const WCHAR *name;         /* section name */
+    unsigned int nb_lines;     /* number of used lines */
+    unsigned int alloc_lines;  /* total number of allocated lines in array below */
+    struct line  lines[16];    /* lines information (grown dynamically, 16 is initial size) */
+};
+
+struct inf_file
+{
+    struct inf_file *next;            /* next appended file */
+    WCHAR           *strings;         /* buffer for string data (section names and field values) */
+    WCHAR           *string_pos;      /* position of next available string in buffer */
+    unsigned int     nb_sections;     /* number of used sections */
+    unsigned int     alloc_sections;  /* total number of allocated section pointers */
+    struct section **sections;        /* section pointers array */
+    unsigned int     nb_fields;
+    unsigned int     alloc_fields;
+    struct field    *fields;
+    int              strings_section; /* index of [Strings] section or -1 if none */
+    WCHAR           *src_root;        /* source root directory */
+};
+
+/* parser definitions */
+
+enum parser_state
+{
+    LINE_START,      /* at beginning of a line */
+    SECTION_NAME,    /* parsing a section name */
+    KEY_NAME,        /* parsing a key name */
+    VALUE_NAME,      /* parsing a value name */
+    EOL_BACKSLASH,   /* backslash at end of line */
+    QUOTES,          /* inside quotes */
+    LEADING_SPACES,  /* leading spaces */
+    TRAILING_SPACES, /* trailing spaces */
+    COMMENT,         /* inside a comment */
+    NB_PARSER_STATES
+};
+
+struct parser
+{
+    const WCHAR      *start;        /* start position of item being parsed */
+    const WCHAR      *end;          /* end of buffer */
+    struct inf_file  *file;         /* file being built */
+    enum parser_state state;        /* current parser state */
+    enum parser_state stack[4];     /* state stack */
+    int               stack_pos;    /* current pos in stack */
+
+    int               cur_section;  /* index of section being parsed*/
+    struct line      *line;         /* current line */
+    unsigned int      line_pos;     /* current line position in file */
+    unsigned int      error;        /* error code */
+    unsigned int      token_len;    /* current token len */
+    WCHAR token[MAX_FIELD_LEN+1];   /* current token */
+};
+
+typedef const WCHAR * (*parser_state_func)( struct parser *parser, const WCHAR *pos );
+
+/* parser state machine functions */
+static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos );
+static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos );
+static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos );
+static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos );
+static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos );
+static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos );
+static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos );
+static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos );
+static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos );
+
+static const parser_state_func parser_funcs[NB_PARSER_STATES] =
+{
+    line_start_state,      /* LINE_START */
+    section_name_state,    /* SECTION_NAME */
+    key_name_state,        /* KEY_NAME */
+    value_name_state,      /* VALUE_NAME */
+    eol_backslash_state,   /* EOL_BACKSLASH */
+    quotes_state,          /* QUOTES */
+    leading_spaces_state,  /* LEADING_SPACES */
+    trailing_spaces_state, /* TRAILING_SPACES */
+    comment_state          /* COMMENT */
+};
+
+
+/* Unicode string constants */
+static const WCHAR Version[]    = {'V','e','r','s','i','o','n',0};
+static const WCHAR Signature[]  = {'S','i','g','n','a','t','u','r','e',0};
+static const WCHAR Chicago[]    = {'$','C','h','i','c','a','g','o','$',0};
+static const WCHAR WindowsNT[]  = {'$','W','i','n','d','o','w','s',' ','N','T','$',0};
+static const WCHAR Windows95[]  = {'$','W','i','n','d','o','w','s',' ','9','5','$',0};
+static const WCHAR LayoutFile[] = {'L','a','y','o','u','t','F','i','l','e',0};
+
+/* extend an array, allocating more memory if necessary */
+static void *grow_array( void *array, unsigned int *count, size_t elem )
+{
+    void *new_array;
+    unsigned int new_count = *count + *count / 2;
+    if (new_count < 32) new_count = 32;
+    if ((new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, array, new_count * elem )))
+        *count = new_count;
+    else
+        HeapFree( GetProcessHeap(), 0, array );
+    return new_array;
+}
+
+
+/* find a section by name */
+static int find_section( struct inf_file *file, const WCHAR *name )
+{
+    int i;
+
+    for (i = 0; i < file->nb_sections; i++)
+        if (!strcmpiW( name, file->sections[i]->name )) return i;
+    return -1;
+}
+
+
+/* find a line by name */
+static struct line *find_line( struct inf_file *file, int section_index, const WCHAR *name )
+{
+    struct section *section;
+    struct line *line;
+    int i;
+
+    if (section_index < 0 || section_index >= file->nb_sections) return NULL;
+    section = file->sections[section_index];
+    for (i = 0, line = section->lines; i < section->nb_lines; i++, line++)
+    {
+        if (line->key_field == -1) continue;
+        if (!strcmpiW( name, file->fields[line->key_field].text )) return line;
+    }
+    return NULL;
+}
+
+
+/* add a section to the file and return the section index */
+static int add_section( struct inf_file *file, const WCHAR *name )
+{
+    struct section *section;
+
+    if (file->nb_sections >= file->alloc_sections)
+    {
+        if (!(file->sections = grow_array( file->sections, &file->alloc_sections,
+                                           sizeof(file->sections[0]) ))) return -1;
+    }
+    if (!(section = HeapAlloc( GetProcessHeap(), 0, sizeof(*section) ))) return -1;
+    section->name        = name;
+    section->nb_lines    = 0;
+    section->alloc_lines = sizeof(section->lines)/sizeof(section->lines[0]);
+    file->sections[file->nb_sections] = section;
+    return file->nb_sections++;
+}
+
+
+/* add a line to a given section */
+static struct line *add_line( struct inf_file *file, int section_index )
+{
+    struct section *section;
+    struct line *line;
+
+    assert( section_index >= 0 && section_index < file->nb_sections );
+
+    section = file->sections[section_index];
+    if (section->nb_lines == section->alloc_lines)  /* need to grow the section */
+    {
+        int size = sizeof(*section) - sizeof(section->lines) + 2*section->alloc_lines*sizeof(*line);
+        if (!(section = HeapReAlloc( GetProcessHeap(), 0, section, size ))) return NULL;
+        section->alloc_lines *= 2;
+        file->sections[section_index] = section;
+    }
+    line = &section->lines[section->nb_lines++];
+    line->first_field = file->nb_fields;
+    line->nb_fields   = 0;
+    line->key_field   = -1;
+    return line;
+}
+
+
+/* retrieve a given line from section/line index */
+inline static struct line *get_line( struct inf_file *file, unsigned int section_index,
+                                     unsigned int line_index )
+{
+    struct section *section;
+
+    if (section_index >= file->nb_sections) return NULL;
+    section = file->sections[section_index];
+    if (line_index >= section->nb_lines) return NULL;
+    return &section->lines[line_index];
+}
+
+
+/* retrieve a given field from section/line/field index */
+static struct field *get_field( struct inf_file *file, int section_index, int line_index,
+                                int field_index )
+{
+    struct line *line = get_line( file, section_index, line_index );
+
+    if (!line) return NULL;
+    if (!field_index)  /* get the key */
+    {
+        if (line->key_field == -1) return NULL;
+        return &file->fields[line->key_field];
+    }
+    field_index--;
+    if (field_index >= line->nb_fields) return NULL;
+    return &file->fields[line->first_field + field_index];
+}
+
+
+/* allocate a new field, growing the array if necessary */
+static struct field *add_field( struct inf_file *file, const WCHAR *text )
+{
+    struct field *field;
+
+    if (file->nb_fields >= file->alloc_fields)
+    {
+        if (!(file->fields = grow_array( file->fields, &file->alloc_fields,
+                                         sizeof(file->fields[0]) ))) return NULL;
+    }
+    field = &file->fields[file->nb_fields++];
+    field->text = text;
+    return field;
+}
+
+
+/* retrieve the string substitution for a directory id */
+static const WCHAR *get_dirid_subst( int dirid, unsigned int *len )
+{
+    extern const WCHAR *DIRID_get_string( HINF hinf, int dirid );
+    const WCHAR *ret = DIRID_get_string( 0, dirid );
+    if (ret) *len = strlenW(ret);
+    return ret;
+}
+
+
+/* retrieve the string substitution for a given string, or NULL if not found */
+/* if found, len is set to the substitution length */
+static const WCHAR *get_string_subst( struct inf_file *file, const WCHAR *str, unsigned int *len )
+{
+    static const WCHAR percent = '%';
+
+    struct section *strings_section;
+    struct line *line;
+    struct field *field;
+    int i, dirid;
+    WCHAR *dirid_str, *end;
+    const WCHAR *ret = NULL;
+
+    if (!*len)  /* empty string (%%) is replaced by single percent */
+    {
+        *len = 1;
+        return &percent;
+    }
+    if (file->strings_section == -1) goto not_found;
+    strings_section = file->sections[file->strings_section];
+    for (i = 0, line = strings_section->lines; i < strings_section->nb_lines; i++, line++)
+    {
+        if (line->key_field == -1) continue;
+        if (strncmpiW( str, file->fields[line->key_field].text, *len )) continue;
+        if (!file->fields[line->key_field].text[*len]) break;
+    }
+    if (i == strings_section->nb_lines || !line->nb_fields) goto not_found;
+    field = &file->fields[line->first_field];
+    *len = strlenW( field->text );
+    return field->text;
+
+ not_found:  /* check for integer id */
+    if ((dirid_str = HeapAlloc( GetProcessHeap(), 0, (*len+1) * sizeof(WCHAR) )))
+    {
+        memcpy( dirid_str, str, *len * sizeof(WCHAR) );
+        dirid_str[*len] = 0;
+        dirid = wcstol( dirid_str, &end, 10 );
+        if (!*end) ret = get_dirid_subst( dirid, len );
+        HeapFree( GetProcessHeap(), 0, dirid_str );
+        return ret;
+    }
+    return NULL;
+}
+
+
+/* do string substitutions on the specified text */
+/* the buffer is assumed to be large enough */
+/* returns necessary length not including terminating null */
+unsigned int PARSER_string_substW( struct inf_file *file, const WCHAR *text, WCHAR *buffer,
+                                   unsigned int size )
+{
+    const WCHAR *start, *subst, *p;
+    unsigned int len, total = 0;
+    int inside = 0;
+
+    if (!buffer) size = MAX_STRING_LEN + 1;
+    for (p = start = text; *p; p++)
+    {
+        if (*p != '%') continue;
+        inside = !inside;
+        if (inside)  /* start of a %xx% string */
+        {
+            len = p - start;
+            if (len > size - 1) len = size - 1;
+            if (buffer) memcpy( buffer + total, start, len * sizeof(WCHAR) );
+            total += len;
+            size -= len;
+            start = p;
+        }
+        else /* end of the %xx% string, find substitution */
+        {
+            len = p - start - 1;
+            subst = get_string_subst( file, start + 1, &len );
+            if (!subst)
+            {
+                subst = start;
+                len = p - start + 1;
+            }
+            if (len > size - 1) len = size - 1;
+            if (buffer) memcpy( buffer + total, subst, len * sizeof(WCHAR) );
+            total += len;
+            size -= len;
+            start = p + 1;
+        }
+    }
+
+    if (start != p) /* unfinished string, copy it */
+    {
+        len = p - start;
+        if (len > size - 1) len = size - 1;
+        if (buffer) memcpy( buffer + total, start, len * sizeof(WCHAR) );
+        total += len;
+    }
+    if (buffer && size) buffer[total] = 0;
+    return total;
+}
+
+
+/* do string substitutions on the specified text */
+/* the buffer is assumed to be large enough */
+/* returns necessary length not including terminating null */
+unsigned int PARSER_string_substA( struct inf_file *file, const WCHAR *text, char *buffer,
+                                   unsigned int size )
+{
+    WCHAR buffW[MAX_STRING_LEN+1];
+    DWORD ret;
+
+    unsigned int len = PARSER_string_substW( file, text, buffW, sizeof(buffW)/sizeof(WCHAR) );
+    if (!buffer) RtlUnicodeToMultiByteSize( &ret, buffW, len * sizeof(WCHAR) );
+    else
+    {
+        RtlUnicodeToMultiByteN( buffer, size-1, &ret, buffW, len * sizeof(WCHAR) );
+        buffer[ret] = 0;
+    }
+    return ret;
+}
+
+
+/* push some string data into the strings buffer */
+static WCHAR *push_string( struct inf_file *file, const WCHAR *string )
+{
+    WCHAR *ret = file->string_pos;
+    strcpyW( ret, string );
+    file->string_pos += strlenW( ret ) + 1;
+    return ret;
+}
+
+
+/* push the current state on the parser stack */
+inline static void push_state( struct parser *parser, enum parser_state state )
+{
+    assert( parser->stack_pos < sizeof(parser->stack)/sizeof(parser->stack[0]) );
+    parser->stack[parser->stack_pos++] = state;
+}
+
+
+/* pop the current state */
+inline static void pop_state( struct parser *parser )
+{
+    assert( parser->stack_pos );
+    parser->state = parser->stack[--parser->stack_pos];
+}
+
+
+/* set the parser state and return the previous one */
+inline static enum parser_state set_state( struct parser *parser, enum parser_state state )
+{
+    enum parser_state ret = parser->state;
+    parser->state = state;
+    return ret;
+}
+
+
+/* check if the pointer points to an end of file */
+inline static int is_eof( struct parser *parser, const WCHAR *ptr )
+{
+    return (ptr >= parser->end || *ptr == CONTROL_Z);
+}
+
+
+/* check if the pointer points to an end of line */
+inline static int is_eol( struct parser *parser, const WCHAR *ptr )
+{
+    return (ptr >= parser->end || *ptr == CONTROL_Z || *ptr == '\n');
+}
+
+
+/* push data from current token start up to pos into the current token */
+static int push_token( struct parser *parser, const WCHAR *pos )
+{
+    int len = pos - parser->start;
+    const WCHAR *src = parser->start;
+    WCHAR *dst = parser->token + parser->token_len;
+
+    if (len > MAX_FIELD_LEN - parser->token_len) len = MAX_FIELD_LEN - parser->token_len;
+
+    parser->token_len += len;
+    for ( ; len > 0; len--, dst++, src++) *dst = *src ? *src : ' ';
+    *dst = 0;
+    parser->start = pos;
+    return 0;
+}
+
+
+/* add a section with the current token as name */
+static int add_section_from_token( struct parser *parser )
+{
+    int section_index;
+
+    if (parser->token_len > MAX_SECTION_NAME_LEN)
+    {
+        parser->error = ERROR_SECTION_NAME_TOO_LONG;
+        return -1;
+    }
+    if ((section_index = find_section( parser->file, parser->token )) == -1)
+    {
+        /* need to create a new one */
+        const WCHAR *name = push_string( parser->file, parser->token );
+        if ((section_index = add_section( parser->file, name )) == -1)
+        {
+            parser->error = ERROR_NOT_ENOUGH_MEMORY;
+            return -1;
+        }
+    }
+    parser->token_len = 0;
+    parser->cur_section = section_index;
+    return section_index;
+}
+
+
+/* add a field containing the current token to the current line */
+static struct field *add_field_from_token( struct parser *parser, int is_key )
+{
+    struct field *field;
+    WCHAR *text;
+
+    if (!parser->line)  /* need to start a new line */
+    {
+        if (parser->cur_section == -1)  /* got a line before the first section */
+        {
+            parser->error = ERROR_WRONG_INF_STYLE;
+            return NULL;
+        }
+        if (!(parser->line = add_line( parser->file, parser->cur_section ))) goto error;
+    }
+    else assert(!is_key);
+
+    text = push_string( parser->file, parser->token );
+    if ((field = add_field( parser->file, text )))
+    {
+        if (!is_key) parser->line->nb_fields++;
+        else
+        {
+            /* replace first field by key field */
+            parser->line->key_field = parser->line->first_field;
+            parser->line->first_field++;
+        }
+        parser->token_len = 0;
+        return field;
+    }
+ error:
+    parser->error = ERROR_NOT_ENOUGH_MEMORY;
+    return NULL;
+}
+
+
+/* handler for parser LINE_START state */
+static const WCHAR *line_start_state( struct parser *parser, const WCHAR *pos )
+{
+    const WCHAR *p;
+
+    for (p = pos; !is_eof( parser, p ); p++)
+    {
+        switch(*p)
+        {
+        case '\n':
+            parser->line_pos++;
+            parser->line = NULL;  /* start a new line */
+            break;
+        case ';':
+            push_state( parser, LINE_START );
+            set_state( parser, COMMENT );
+            return p + 1;
+        case '[':
+            parser->start = p + 1;
+            set_state( parser, SECTION_NAME );
+            return p + 1;
+        default:
+            if (!isspaceW(*p))
+            {
+                parser->start = p;
+                set_state( parser, KEY_NAME );
+                return p;
+            }
+            break;
+        }
+    }
+    return NULL;
+}
+
+
+/* handler for parser SECTION_NAME state */
+static const WCHAR *section_name_state( struct parser *parser, const WCHAR *pos )
+{
+    const WCHAR *p;
+
+    for (p = pos; !is_eol( parser, p ); p++)
+    {
+        if (*p == ']')
+        {
+            push_token( parser, p );
+            if (add_section_from_token( parser ) == -1) return NULL;
+            push_state( parser, LINE_START );
+            set_state( parser, COMMENT );  /* ignore everything else on the line */
+            return p + 1;
+        }
+    }
+    parser->error = ERROR_BAD_SECTION_NAME_LINE; /* unfinished section name */
+    return NULL;
+}
+
+
+/* handler for parser KEY_NAME state */
+static const WCHAR *key_name_state( struct parser *parser, const WCHAR *pos )
+{
+    const WCHAR *p, *token_end = parser->start;
+
+    for (p = pos; !is_eol( parser, p ); p++)
+    {
+        if (*p == ',') break;
+        switch(*p)
+        {
+
+         case '=':
+            push_token( parser, token_end );
+            if (!add_field_from_token( parser, 1 )) return NULL;
+            parser->start = p + 1;
+            push_state( parser, VALUE_NAME );
+            set_state( parser, LEADING_SPACES );
+            return p + 1;
+        case ';':
+            push_token( parser, token_end );
+            if (!add_field_from_token( parser, 0 )) return NULL;
+            push_state( parser, LINE_START );
+            set_state( parser, COMMENT );
+            return p + 1;
+        case '"':
+            push_token( parser, token_end );
+            parser->start = p + 1;
+            push_state( parser, KEY_NAME );
+            set_state( parser, QUOTES );
+            return p + 1;
+        case '\\':
+            push_token( parser, token_end );
+            parser->start = p;
+            push_state( parser, KEY_NAME );
+            set_state( parser, EOL_BACKSLASH );
+            return p;
+        default:
+            if (!isspaceW(*p)) token_end = p + 1;
+            else
+            {
+                push_token( parser, p );
+                push_state( parser, KEY_NAME );
+                set_state( parser, TRAILING_SPACES );
+                return p;
+            }
+            break;
+        }
+    }
+    push_token( parser, token_end );
+    set_state( parser, VALUE_NAME );
+    return p;
+}
+
+
+/* handler for parser VALUE_NAME state */
+static const WCHAR *value_name_state( struct parser *parser, const WCHAR *pos )
+{
+    const WCHAR *p, *token_end = parser->start;
+
+    for (p = pos; !is_eol( parser, p ); p++)
+    {
+        switch(*p)
+        {
+        case ';':
+            push_token( parser, token_end );
+            if (!add_field_from_token( parser, 0 )) return NULL;
+            push_state( parser, LINE_START );
+            set_state( parser, COMMENT );
+            return p + 1;
+        case ',':
+            push_token( parser, token_end );
+            if (!add_field_from_token( parser, 0 )) return NULL;
+            parser->start = p + 1;
+            push_state( parser, VALUE_NAME );
+            set_state( parser, LEADING_SPACES );
+            return p + 1;
+        case '"':
+            push_token( parser, token_end );
+            parser->start = p + 1;
+            push_state( parser, VALUE_NAME );
+            set_state( parser, QUOTES );
+            return p + 1;
+        case '\\':
+            push_token( parser, token_end );
+            parser->start = p;
+            push_state( parser, VALUE_NAME );
+            set_state( parser, EOL_BACKSLASH );
+            return p;
+        default:
+            if (!isspaceW(*p)) token_end = p + 1;
+            else
+            {
+                push_token( parser, p );
+                push_state( parser, VALUE_NAME );
+                set_state( parser, TRAILING_SPACES );
+                return p;
+            }
+            break;
+        }
+    }
+    push_token( parser, token_end );
+    if (!add_field_from_token( parser, 0 )) return NULL;
+    set_state( parser, LINE_START );
+    return p;
+}
+
+
+/* handler for parser EOL_BACKSLASH state */
+static const WCHAR *eol_backslash_state( struct parser *parser, const WCHAR *pos )
+{
+    const WCHAR *p;
+
+    for (p = pos; !is_eof( parser, p ); p++)
+    {
+        switch(*p)
+        {
+        case '\n':
+            parser->line_pos++;
+            parser->start = p + 1;
+            set_state( parser, LEADING_SPACES );
+            return p + 1;
+        case '\\':
+            continue;
+        case ';':
+            push_state( parser, EOL_BACKSLASH );
+            set_state( parser, COMMENT );
+            return p + 1;
+        default:
+            if (isspaceW(*p)) continue;
+            push_token( parser, p );
+            pop_state( parser );
+            return p;
+        }
+    }
+    parser->start = p;
+    pop_state( parser );
+    return p;
+}
+
+
+/* handler for parser QUOTES state */
+static const WCHAR *quotes_state( struct parser *parser, const WCHAR *pos )
+{
+    const WCHAR *p, *token_end = parser->start;
+
+    for (p = pos; !is_eol( parser, p ); p++)
+    {
+        if (*p == '"')
+        {
+            if (p+1 < parser->end && p[1] == '"')  /* double quotes */
+            {
+                push_token( parser, p + 1 );
+                parser->start = token_end = p + 2;
+                p++;
+            }
+            else  /* end of quotes */
+            {
+                push_token( parser, p );
+                parser->start = p + 1;
+                pop_state( parser );
+                return p + 1;
+            }
+        }
+    }
+    push_token( parser, p );
+    pop_state( parser );
+    return p;
+}
+
+
+/* handler for parser LEADING_SPACES state */
+static const WCHAR *leading_spaces_state( struct parser *parser, const WCHAR *pos )
+{
+    const WCHAR *p;
+
+    for (p = pos; !is_eol( parser, p ); p++)
+    {
+        if (*p == '\\')
+        {
+            parser->start = p;
+            set_state( parser, EOL_BACKSLASH );
+            return p;
+        }
+        if (!isspaceW(*p)) break;
+    }
+    parser->start = p;
+    pop_state( parser );
+    return p;
+}
+
+
+/* handler for parser TRAILING_SPACES state */
+static const WCHAR *trailing_spaces_state( struct parser *parser, const WCHAR *pos )
+{
+    const WCHAR *p;
+
+    for (p = pos; !is_eol( parser, p ); p++)
+    {
+        if (*p == '\\')
+        {
+            set_state( parser, EOL_BACKSLASH );
+            return p;
+        }
+        if (!isspaceW(*p)) break;
+    }
+    pop_state( parser );
+    return p;
+}
+
+
+/* handler for parser COMMENT state */
+static const WCHAR *comment_state( struct parser *parser, const WCHAR *pos )
+{
+    const WCHAR *p = pos;
+
+    while (!is_eol( parser, p )) p++;
+    pop_state( parser );
+    return p;
+}
+
+
+/* parse a complete buffer */
+static DWORD parse_buffer( struct inf_file *file, const WCHAR *buffer, const WCHAR *end,
+                           UINT *error_line )
+{
+    static const WCHAR Strings[] = {'S','t','r','i','n','g','s',0};
+
+    struct parser parser;
+    const WCHAR *pos = buffer;
+
+    parser.start       = buffer;
+    parser.end         = end;
+    parser.file        = file;
+    parser.line        = NULL;
+    parser.state       = LINE_START;
+    parser.stack_pos   = 0;
+    parser.cur_section = -1;
+    parser.line_pos    = 1;
+    parser.error       = 0;
+    parser.token_len   = 0;
+
+    /* parser main loop */
+    while (pos) pos = (parser_funcs[parser.state])( &parser, pos );
+
+    /* trim excess buffer space */
+    if (file->alloc_sections > file->nb_sections)
+    {
+        file->sections = HeapReAlloc( GetProcessHeap(), 0, file->sections,
+                                      file->nb_sections * sizeof(file->sections[0]) );
+        file->alloc_sections = file->nb_sections;
+    }
+    if (file->alloc_fields > file->nb_fields)
+    {
+        file->fields = HeapReAlloc( GetProcessHeap(), 0, file->fields,
+                                    file->nb_fields * sizeof(file->fields[0]) );
+        file->alloc_fields = file->nb_fields;
+    }
+    file->strings = HeapReAlloc( GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, file->strings,
+                                 (file->string_pos - file->strings) * sizeof(WCHAR) );
+
+    if (parser.error)
+    {
+        if (error_line) *error_line = parser.line_pos;
+        return parser.error;
+    }
+
+    /* find the [strings] section */
+    file->strings_section = find_section( file, Strings );
+    return 0;
+}
+
+
+/* append a child INF file to its parent list, in a thread-safe manner */
+static void append_inf_file( struct inf_file *parent, struct inf_file *child )
+{
+    struct inf_file **ppnext = &parent->next;
+    child->next = NULL;
+
+    for (;;)
+    {
+        struct inf_file *next = InterlockedCompareExchangePointer( (void **)ppnext, child, NULL );
+        if (!next) return;
+        ppnext = &next->next;
+    }
+}
+
+
+/***********************************************************************
+ *            parse_file
+ *
+ * parse an INF file.
+ */
+static struct inf_file *parse_file( HANDLE handle, const WCHAR *class, UINT *error_line )
+{
+    void *buffer;
+    DWORD err = 0;
+    struct inf_file *file;
+
+    DWORD size = GetFileSize( handle, NULL );
+    HANDLE mapping = CreateFileMappingW( handle, NULL, PAGE_READONLY, 0, size, NULL );
+    if (!mapping) return NULL;
+    buffer = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, size );
+    NtClose( mapping );
+    if (!buffer) return NULL;
+
+    if (class) FIXME( "class %s not supported yet\n", debugstr_w(class) );
+
+    if (!(file = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*file) )))
+    {
+        err = ERROR_NOT_ENOUGH_MEMORY;
+        goto done;
+    }
+
+    /* we won't need more strings space than the size of the file,
+     * so we can preallocate it here
+     */
+    if (!(file->strings = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) )))
+    {
+        err = ERROR_NOT_ENOUGH_MEMORY;
+        goto done;
+    }
+    file->string_pos = file->strings;
+    file->strings_section = -1;
+
+    if (!RtlIsTextUnicode( buffer, size, NULL ))
+    {
+        WCHAR *new_buff = HeapAlloc( GetProcessHeap(), 0, size * sizeof(WCHAR) );
+        if (new_buff)
+        {
+            DWORD len = MultiByteToWideChar( CP_ACP, 0, buffer, size, new_buff,
+                                             size * sizeof(WCHAR) );
+            err = parse_buffer( file, new_buff, new_buff + len, error_line );
+            HeapFree( GetProcessHeap(), 0, new_buff );
+        }
+    }
+    else err = parse_buffer( file, buffer, (WCHAR *)((char *)buffer + size), error_line );
+
+    if (!err)  /* now check signature */
+    {
+        int version_index = find_section( file, Version );
+        if (version_index != -1)
+        {
+            struct line *line = find_line( file, version_index, Signature );
+            if (line && line->nb_fields > 0)
+            {
+                struct field *field = file->fields + line->first_field;
+                if (!strcmpiW( field->text, Chicago )) goto done;
+                if (!strcmpiW( field->text, WindowsNT )) goto done;
+                if (!strcmpiW( field->text, Windows95 )) goto done;
+            }
+        }
+        err = ERROR_WRONG_INF_STYLE;
+    }
+
+ done:
+    UnmapViewOfFile( buffer );
+    if (err)
+    {
+        HeapFree( GetProcessHeap(), 0, file );
+        SetLastError( err );
+        file = NULL;
+    }
+    return file;
+}
+
+
+/***********************************************************************
+ *            PARSER_get_src_root
+ *
+ * Retrieve the source directory of an inf file.
+ */
+const WCHAR *PARSER_get_src_root( HINF hinf )
+{
+    struct inf_file *file = hinf;
+    return file->src_root;
+}
+
+
+/***********************************************************************
+ *            SetupOpenInfFileA   (SETUPAPI.@)
+ */
+HINF WINAPI SetupOpenInfFileA( PCSTR name, PCSTR class, DWORD style, UINT *error )
+{
+    UNICODE_STRING nameW, classW;
+    HINF ret = (HINF)INVALID_HANDLE_VALUE;
+
+    classW.Buffer = NULL;
+    if (class && !RtlCreateUnicodeStringFromAsciiz( &classW, class ))
+    {
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+        return ret;
+    }
+    if (RtlCreateUnicodeStringFromAsciiz( &nameW, name ))
+    {
+        ret = SetupOpenInfFileW( nameW.Buffer, classW.Buffer, style, error );
+        RtlFreeUnicodeString( &nameW );
+    }
+    RtlFreeUnicodeString( &classW );
+    return ret;
+}
+
+
+/***********************************************************************
+ *            SetupOpenInfFileW   (SETUPAPI.@)
+ */
+HINF WINAPI SetupOpenInfFileW( PCWSTR name, PCWSTR class, DWORD style, UINT *error )
+{
+    struct inf_file *file = NULL;
+    HANDLE handle;
+    WCHAR *path, *p;
+    UINT len;
+
+    if (strchrW( name, '\\' ) || strchrW( name, '/' ))
+    {
+        if (!(len = GetFullPathNameW( name, 0, NULL, NULL ))) return (HINF)INVALID_HANDLE_VALUE;
+        if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
+        {
+            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+            return (HINF)INVALID_HANDLE_VALUE;
+        }
+        GetFullPathNameW( name, len, path, NULL );
+        handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
+    }
+    else  /* try Windows directory */
+    {
+        static const WCHAR Inf[]      = {'\\','i','n','f','\\',0};
+        static const WCHAR System32[] = {'\\','s','y','s','t','e','m','3','2','\\',0};
+
+        len = GetWindowsDirectoryW( NULL, 0 ) + strlenW(name) + 12;
+        if (!(path = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
+        {
+            SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+            return (HINF)INVALID_HANDLE_VALUE;
+        }
+        GetWindowsDirectoryW( path, len );
+        p = path + strlenW(path);
+        strcpyW( p, Inf );
+        strcatW( p, name );
+        handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
+        if (handle == INVALID_HANDLE_VALUE)
+        {
+            strcpyW( p, System32 );
+            strcatW( p, name );
+            handle = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
+        }
+    }
+
+    if (handle != INVALID_HANDLE_VALUE)
+    {
+        file = parse_file( handle, class, error );
+        CloseHandle( handle );
+    }
+    if (!file)
+    {
+        HeapFree( GetProcessHeap(), 0, path );
+        return (HINF)INVALID_HANDLE_VALUE;
+    }
+    TRACE( "%s -> %p\n", debugstr_w(path), file );
+    file->src_root = path;
+    if ((p = strrchrW( path, '\\' ))) p[1] = 0;  /* remove file name */
+    SetLastError( 0 );
+    return (HINF)file;
+}
+
+
+/***********************************************************************
+ *            SetupOpenAppendInfFileA    (SETUPAPI.@)
+ */
+BOOL WINAPI SetupOpenAppendInfFileA( PCSTR name, HINF parent_hinf, UINT *error )
+{
+    HINF child_hinf;
+
+    if (!name) return SetupOpenAppendInfFileW( NULL, parent_hinf, error );
+    child_hinf = SetupOpenInfFileA( name, NULL, INF_STYLE_WIN4, error );
+    if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE;
+    append_inf_file( parent_hinf, child_hinf );
+    TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_a(name), child_hinf );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            SetupOpenAppendInfFileW    (SETUPAPI.@)
+ */
+BOOL WINAPI SetupOpenAppendInfFileW( PCWSTR name, HINF parent_hinf, UINT *error )
+{
+    HINF child_hinf;
+
+    if (!name)
+    {
+        INFCONTEXT context;
+        WCHAR filename[MAX_PATH];
+        int idx = 1;
+
+        if (!SetupFindFirstLineW( parent_hinf, Version, LayoutFile, &context )) return FALSE;
+        while (SetupGetStringFieldW( &context, idx++, filename,
+                                     sizeof(filename)/sizeof(WCHAR), NULL ))
+        {
+            child_hinf = SetupOpenInfFileW( filename, NULL, INF_STYLE_WIN4, error );
+            if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE;
+            append_inf_file( parent_hinf, child_hinf );
+            TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_w(filename), child_hinf );
+        }
+        return TRUE;
+    }
+    child_hinf = SetupOpenInfFileW( name, NULL, INF_STYLE_WIN4, error );
+    if (child_hinf == (HINF)INVALID_HANDLE_VALUE) return FALSE;
+    append_inf_file( parent_hinf, child_hinf );
+    TRACE( "%p: appended %s (%p)\n", parent_hinf, debugstr_w(name), child_hinf );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            SetupCloseInfFile   (SETUPAPI.@)
+ */
+void WINAPI SetupCloseInfFile( HINF hinf )
+{
+    struct inf_file *file = hinf;
+    int i;
+
+    for (i = 0; i < file->nb_sections; i++) HeapFree( GetProcessHeap(), 0, file->sections[i] );
+    HeapFree( GetProcessHeap(), 0, file->src_root );
+    HeapFree( GetProcessHeap(), 0, file->sections );
+    HeapFree( GetProcessHeap(), 0, file->fields );
+    HeapFree( GetProcessHeap(), 0, file->strings );
+    HeapFree( GetProcessHeap(), 0, file );
+}
+
+
+/***********************************************************************
+ *            SetupGetLineCountA   (SETUPAPI.@)
+ */
+LONG WINAPI SetupGetLineCountA( HINF hinf, PCSTR name )
+{
+    UNICODE_STRING sectionW;
+    LONG ret = -1;
+
+    if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, name ))
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+    else
+    {
+        ret = SetupGetLineCountW( hinf, sectionW.Buffer );
+        RtlFreeUnicodeString( &sectionW );
+    }
+    return ret;
+}
+
+
+/***********************************************************************
+ *            SetupGetLineCountW   (SETUPAPI.@)
+ */
+LONG WINAPI SetupGetLineCountW( HINF hinf, PCWSTR section )
+{
+    struct inf_file *file = hinf;
+    int section_index;
+    LONG ret = -1;
+
+    for (file = hinf; file; file = file->next)
+    {
+        if ((section_index = find_section( file, section )) == -1) continue;
+        if (ret == -1) ret = 0;
+        ret += file->sections[section_index]->nb_lines;
+    }
+    TRACE( "(%p,%s) returning %ld\n", hinf, debugstr_w(section), ret );
+    SetLastError( (ret == -1) ? ERROR_SECTION_NOT_FOUND : 0 );
+    return ret;
+}
+
+
+/***********************************************************************
+ *            SetupGetLineByIndexA   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupGetLineByIndexA( HINF hinf, PCSTR section, DWORD index, INFCONTEXT *context )
+{
+    UNICODE_STRING sectionW;
+    BOOL ret = FALSE;
+
+    if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+    else
+    {
+        ret = SetupGetLineByIndexW( hinf, sectionW.Buffer, index, context );
+        RtlFreeUnicodeString( &sectionW );
+    }
+    return ret;
+}
+
+
+/***********************************************************************
+ *            SetupGetLineByIndexW   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupGetLineByIndexW( HINF hinf, PCWSTR section, DWORD index, INFCONTEXT *context )
+{
+    struct inf_file *file = hinf;
+    int section_index;
+
+    SetLastError( ERROR_SECTION_NOT_FOUND );
+    for (file = hinf; file; file = file->next)
+    {
+        if ((section_index = find_section( file, section )) == -1) continue;
+        SetLastError( ERROR_LINE_NOT_FOUND );
+        if (index < file->sections[section_index]->nb_lines)
+        {
+            context->Inf        = hinf;
+            context->CurrentInf = file;
+            context->Section    = section_index;
+            context->Line       = index;
+            SetLastError( 0 );
+            TRACE( "(%p,%s): returning %d/%ld\n",
+                   hinf, debugstr_w(section), section_index, index );
+            return TRUE;
+        }
+        index -= file->sections[section_index]->nb_lines;
+    }
+    TRACE( "(%p,%s) not found\n", hinf, debugstr_w(section) );
+    return FALSE;
+}
+
+
+/***********************************************************************
+ *            SetupFindFirstLineA   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupFindFirstLineA( HINF hinf, PCSTR section, PCSTR key, INFCONTEXT *context )
+{
+    UNICODE_STRING sectionW, keyW;
+    BOOL ret = FALSE;
+
+    if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
+    {
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+        return FALSE;
+    }
+
+    if (!key) ret = SetupFindFirstLineW( hinf, sectionW.Buffer, NULL, context );
+    else
+    {
+        if (RtlCreateUnicodeStringFromAsciiz( &keyW, key ))
+        {
+            ret = SetupFindFirstLineW( hinf, sectionW.Buffer, keyW.Buffer, context );
+            RtlFreeUnicodeString( &keyW );
+        }
+        else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+    }
+    RtlFreeUnicodeString( &sectionW );
+    return ret;
+}
+
+
+/***********************************************************************
+ *            SetupFindFirstLineW   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupFindFirstLineW( HINF hinf, PCWSTR section, PCWSTR key, INFCONTEXT *context )
+{
+    struct inf_file *file;
+    int section_index;
+
+    SetLastError( ERROR_SECTION_NOT_FOUND );
+    for (file = hinf; file; file = file->next)
+    {
+        if ((section_index = find_section( file, section )) == -1) continue;
+        if (key)
+        {
+            INFCONTEXT ctx;
+            ctx.Inf        = hinf;
+            ctx.CurrentInf = file;
+            ctx.Section    = section_index;
+            ctx.Line       = -1;
+            return SetupFindNextMatchLineW( &ctx, key, context );
+        }
+        SetLastError( ERROR_LINE_NOT_FOUND );  /* found at least one section */
+        if (file->sections[section_index]->nb_lines)
+        {
+            context->Inf        = hinf;
+            context->CurrentInf = file;
+            context->Section    = section_index;
+            context->Line       = 0;
+            SetLastError( 0 );
+            TRACE( "(%p,%s,%s): returning %d/0\n",
+                   hinf, debugstr_w(section), debugstr_w(key), section_index );
+            return TRUE;
+        }
+    }
+    TRACE( "(%p,%s,%s): not found\n", hinf, debugstr_w(section), debugstr_w(key) );
+    return FALSE;
+}
+
+
+/***********************************************************************
+ *            SetupFindNextLine   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupFindNextLine( const INFCONTEXT *context_in, INFCONTEXT *context_out )
+{
+    struct inf_file *file = context_in->CurrentInf;
+    struct section *section;
+
+    if (context_in->Section >= file->nb_sections) goto error;
+
+    section = file->sections[context_in->Section];
+    if (context_in->Line+1 < section->nb_lines)
+    {
+        if (context_out != context_in) *context_out = *context_in;
+        context_out->Line++;
+        SetLastError( 0 );
+        return TRUE;
+    }
+
+    /* now search the appended files */
+
+    for (file = file->next; file; file = file->next)
+    {
+        int section_index = find_section( file, section->name );
+        if (section_index == -1) continue;
+        if (file->sections[section_index]->nb_lines)
+        {
+            context_out->Inf        = context_in->Inf;
+            context_out->CurrentInf = file;
+            context_out->Section    = section_index;
+            context_out->Line       = 0;
+            SetLastError( 0 );
+            return TRUE;
+        }
+    }
+ error:
+    SetLastError( ERROR_LINE_NOT_FOUND );
+    return FALSE;
+}
+
+
+/***********************************************************************
+ *            SetupFindNextMatchLineA   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupFindNextMatchLineA( const INFCONTEXT *context_in, PCSTR key,
+                                     INFCONTEXT *context_out )
+{
+    UNICODE_STRING keyW;
+    BOOL ret = FALSE;
+
+    if (!key) return SetupFindNextLine( context_in, context_out );
+
+    if (!RtlCreateUnicodeStringFromAsciiz( &keyW, key ))
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+    else
+    {
+        ret = SetupFindNextMatchLineW( context_in, keyW.Buffer, context_out );
+        RtlFreeUnicodeString( &keyW );
+    }
+    return ret;
+}
+
+
+/***********************************************************************
+ *            SetupFindNextMatchLineW   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupFindNextMatchLineW( const INFCONTEXT *context_in, PCWSTR key,
+                                     INFCONTEXT *context_out )
+{
+    struct inf_file *file = context_in->CurrentInf;
+    struct section *section;
+    struct line *line;
+    unsigned int i;
+
+    if (!key) return SetupFindNextLine( context_in, context_out );
+
+    if (context_in->Section >= file->nb_sections) goto error;
+
+    section = file->sections[context_in->Section];
+
+    for (i = context_in->Line+1, line = &section->lines[i]; i < section->nb_lines; i++, line++)
+    {
+        if (line->key_field == -1) continue;
+        if (!strcmpiW( key, file->fields[line->key_field].text ))
+        {
+            if (context_out != context_in) *context_out = *context_in;
+            context_out->Line = i;
+            SetLastError( 0 );
+            TRACE( "(%p,%s,%s): returning %d\n",
+                   file, debugstr_w(section->name), debugstr_w(key), i );
+            return TRUE;
+        }
+    }
+
+    /* now search the appended files */
+
+    for (file = file->next; file; file = file->next)
+    {
+        int section_index = find_section( file, section->name );
+        if (section_index == -1) continue;
+        section = file->sections[section_index];
+        for (i = 0, line = section->lines; i < section->nb_lines; i++, line++)
+        {
+            if (line->key_field == -1) continue;
+            if (!strcmpiW( key, file->fields[line->key_field].text ))
+            {
+                context_out->Inf        = context_in->Inf;
+                context_out->CurrentInf = file;
+                context_out->Section    = section_index;
+                context_out->Line       = i;
+                SetLastError( 0 );
+                TRACE( "(%p,%s,%s): returning %d/%d\n",
+                       file, debugstr_w(section->name), debugstr_w(key), section_index, i );
+                return TRUE;
+            }
+        }
+    }
+    TRACE( "(%p,%s,%s): not found\n",
+           context_in->CurrentInf, debugstr_w(section->name), debugstr_w(key) );
+ error:
+    SetLastError( ERROR_LINE_NOT_FOUND );
+    return FALSE;
+}
+
+
+/***********************************************************************
+ *		SetupGetLineTextW    (SETUPAPI.@)
+ */
+BOOL WINAPI SetupGetLineTextW( const INFCONTEXT *context, HINF hinf, PCWSTR section_name,
+                               PCWSTR key_name, PWSTR buffer, DWORD size, DWORD *required )
+{
+    struct inf_file *file;
+    struct line *line;
+    struct field *field;
+    int i;
+    DWORD total = 0;
+
+    if (!context)
+    {
+        INFCONTEXT new_context;
+        if (!SetupFindFirstLineW( hinf, section_name, key_name, &new_context )) return FALSE;
+        file = new_context.CurrentInf;
+        line = get_line( file, new_context.Section, new_context.Line );
+    }
+    else
+    {
+        file = context->CurrentInf;
+        if (!(line = get_line( file, context->Section, context->Line )))
+        {
+            SetLastError( ERROR_LINE_NOT_FOUND );
+            return FALSE;
+        }
+    }
+
+    for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
+        total += PARSER_string_substW( file, field->text, NULL, 0 ) + 1;
+
+    if (required) *required = total;
+    if (buffer)
+    {
+        if (total > size)
+        {
+            SetLastError( ERROR_INSUFFICIENT_BUFFER );
+            return FALSE;
+        }
+        for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
+        {
+            unsigned int len = PARSER_string_substW( file, field->text, buffer, size );
+            if (i+1 < line->nb_fields) buffer[len] = ',';
+            buffer += len + 1;
+        }
+    }
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *		SetupGetLineTextA    (SETUPAPI.@)
+ */
+BOOL WINAPI SetupGetLineTextA( const INFCONTEXT *context, HINF hinf, PCSTR section_name,
+                               PCSTR key_name, PSTR buffer, DWORD size, DWORD *required )
+{
+    struct inf_file *file;
+    struct line *line;
+    struct field *field;
+    int i;
+    DWORD total = 0;
+
+    if (!context)
+    {
+        INFCONTEXT new_context;
+        if (!SetupFindFirstLineA( hinf, section_name, key_name, &new_context )) return FALSE;
+        file = new_context.CurrentInf;
+        line = get_line( file, new_context.Section, new_context.Line );
+    }
+    else
+    {
+        file = context->CurrentInf;
+        if (!(line = get_line( file, context->Section, context->Line )))
+        {
+            SetLastError( ERROR_LINE_NOT_FOUND );
+            return FALSE;
+        }
+    }
+
+    for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
+        total += PARSER_string_substA( file, field->text, NULL, 0 ) + 1;
+
+    if (required) *required = total;
+    if (buffer)
+    {
+        if (total > size)
+        {
+            SetLastError( ERROR_INSUFFICIENT_BUFFER );
+            return FALSE;
+        }
+        for (i = 0, field = &file->fields[line->first_field]; i < line->nb_fields; i++, field++)
+        {
+            unsigned int len = PARSER_string_substA( file, field->text, buffer, size );
+            if (i+1 < line->nb_fields) buffer[len] = ',';
+            buffer += len + 1;
+        }
+    }
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *		SetupGetFieldCount    (SETUPAPI.@)
+ */
+DWORD WINAPI SetupGetFieldCount( const INFCONTEXT *context )
+{
+    struct inf_file *file = context->CurrentInf;
+    struct line *line = get_line( file, context->Section, context->Line );
+
+    if (!line) return 0;
+    return line->nb_fields;
+}
+
+
+/***********************************************************************
+ *		SetupGetStringFieldA    (SETUPAPI.@)
+ */
+BOOL WINAPI SetupGetStringFieldA( const INFCONTEXT *context, DWORD index, PSTR buffer,
+                                  DWORD size, DWORD *required )
+{
+    struct inf_file *file = context->CurrentInf;
+    struct field *field = get_field( file, context->Section, context->Line, index );
+    unsigned int len;
+
+    SetLastError(0);
+    if (!field) return FALSE;
+    len = PARSER_string_substA( file, field->text, NULL, 0 );
+    if (required) *required = len + 1;
+    if (buffer)
+    {
+        if (size <= len)
+        {
+            SetLastError( ERROR_INSUFFICIENT_BUFFER );
+            return FALSE;
+        }
+        PARSER_string_substA( file, field->text, buffer, size );
+
+        TRACE( "context %p/%p/%d/%d index %ld returning %s\n",
+               context->Inf, context->CurrentInf, context->Section, context->Line,
+               index, debugstr_a(buffer) );
+    }
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *		SetupGetStringFieldW    (SETUPAPI.@)
+ */
+BOOL WINAPI SetupGetStringFieldW( const INFCONTEXT *context, DWORD index, PWSTR buffer,
+                                  DWORD size, DWORD *required )
+{
+    struct inf_file *file = context->CurrentInf;
+    struct field *field = get_field( file, context->Section, context->Line, index );
+    unsigned int len;
+
+    SetLastError(0);
+    if (!field) return FALSE;
+    len = PARSER_string_substW( file, field->text, NULL, 0 );
+    if (required) *required = len + 1;
+    if (buffer)
+    {
+        if (size <= len)
+        {
+            SetLastError( ERROR_INSUFFICIENT_BUFFER );
+            return FALSE;
+        }
+        PARSER_string_substW( file, field->text, buffer, size );
+
+        TRACE( "context %p/%p/%d/%d index %ld returning %s\n",
+               context->Inf, context->CurrentInf, context->Section, context->Line,
+               index, debugstr_w(buffer) );
+    }
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *		SetupGetIntField    (SETUPAPI.@)
+ */
+BOOL WINAPI SetupGetIntField( const INFCONTEXT *context, DWORD index, INT *result )
+{
+    char localbuff[20];
+    char *end, *buffer = localbuff;
+    DWORD required;
+    INT res;
+    BOOL ret = FALSE;
+
+    if (!SetupGetStringFieldA( context, index, localbuff, sizeof(localbuff), &required ))
+    {
+        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return FALSE;
+        if (!(buffer = HeapAlloc( GetProcessHeap(), 0, required ))) return FALSE;
+        if (!SetupGetStringFieldA( context, index, buffer, required, NULL )) goto done;
+    }
+    res = strtol( buffer, &end, 0 );
+    if (end != buffer && !*end)
+    {
+        *result = res;
+        ret = TRUE;
+    }
+    else SetLastError( ERROR_INVALID_DATA );
+
+ done:
+    if (buffer != localbuff) HeapFree( GetProcessHeap(), 0, buffer );
+    return ret;
+}
+
+
+/***********************************************************************
+ *		SetupGetBinaryField    (SETUPAPI.@)
+ */
+BOOL WINAPI SetupGetBinaryField( const INFCONTEXT *context, DWORD index, BYTE *buffer,
+                                 DWORD size, DWORD *required )
+{
+    struct inf_file *file = context->CurrentInf;
+    struct line *line = get_line( file, context->Section, context->Line );
+    struct field *field;
+    int i;
+
+    if (!line)
+    {
+        SetLastError( ERROR_LINE_NOT_FOUND );
+        return FALSE;
+    }
+    if (!index || index >= line->nb_fields)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
+    index--;  /* fields start at 0 */
+    if (required) *required = line->nb_fields - index;
+    if (!buffer) return TRUE;
+    if (size < line->nb_fields - index)
+    {
+        SetLastError( ERROR_INSUFFICIENT_BUFFER );
+        return FALSE;
+    }
+    field = &file->fields[line->first_field + index];
+    for (i = index; i < line->nb_fields; i++, field++)
+    {
+        const WCHAR *p;
+        DWORD value = 0;
+        for (p = field->text; *p && isxdigitW(*p); p++)
+        {
+            if ((value <<= 8) > 255)
+            {
+                SetLastError( ERROR_INVALID_DATA );
+                return FALSE;
+            }
+            if (*p <= '9') value |= (*p - '0');
+            else value |= (tolowerW(*p) - 'a' + 10);
+        }
+        buffer[i - index] = value;
+    }
+    if (TRACE_ON(setupapi))
+    {
+        TRACE( "%p/%p/%d/%d index %ld returning",
+               context->Inf, context->CurrentInf, context->Section, context->Line, index );
+        for (i = index; i < line->nb_fields; i++) DPRINTF( " %02x", buffer[i - index] );
+        DPRINTF( "\n" );
+    }
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *		SetupGetMultiSzFieldA    (SETUPAPI.@)
+ */
+BOOL WINAPI SetupGetMultiSzFieldA( const INFCONTEXT *context, DWORD index, PSTR buffer,
+                                   DWORD size, DWORD *required )
+{
+    struct inf_file *file = context->CurrentInf;
+    struct line *line = get_line( file, context->Section, context->Line );
+    struct field *field;
+    unsigned int len;
+    int i;
+    DWORD total = 1;
+
+    if (!line)
+    {
+        SetLastError( ERROR_LINE_NOT_FOUND );
+        return FALSE;
+    }
+    if (!index || index >= line->nb_fields)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
+    index--;  /* fields start at 0 */
+    field = &file->fields[line->first_field + index];
+    for (i = index; i < line->nb_fields; i++, field++)
+    {
+        if (!(len = PARSER_string_substA( file, field->text, NULL, 0 ))) break;
+        total += len + 1;
+    }
+
+    if (required) *required = total;
+    if (!buffer) return TRUE;
+    if (total > size)
+    {
+        SetLastError( ERROR_INSUFFICIENT_BUFFER );
+        return FALSE;
+    }
+    field = &file->fields[line->first_field + index];
+    for (i = index; i < line->nb_fields; i++, field++)
+    {
+        if (!(len = PARSER_string_substA( file, field->text, buffer, size ))) break;
+        buffer += len + 1;
+    }
+    *buffer = 0;  /* add final null */
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *		SetupGetMultiSzFieldW    (SETUPAPI.@)
+ */
+BOOL WINAPI SetupGetMultiSzFieldW( const INFCONTEXT *context, DWORD index, PWSTR buffer,
+                                   DWORD size, DWORD *required )
+{
+    struct inf_file *file = context->CurrentInf;
+    struct line *line = get_line( file, context->Section, context->Line );
+    struct field *field;
+    unsigned int len;
+    int i;
+    DWORD total = 1;
+
+    if (!line)
+    {
+        SetLastError( ERROR_LINE_NOT_FOUND );
+        return FALSE;
+    }
+    if (!index || index >= line->nb_fields)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
+    index--;  /* fields start at 0 */
+    field = &file->fields[line->first_field + index];
+    for (i = index; i < line->nb_fields; i++, field++)
+    {
+        if (!(len = PARSER_string_substW( file, field->text, NULL, 0 ))) break;
+        total += len + 1;
+    }
+
+    if (required) *required = total;
+    if (!buffer) return TRUE;
+    if (total > size)
+    {
+        SetLastError( ERROR_INSUFFICIENT_BUFFER );
+        return FALSE;
+    }
+    field = &file->fields[line->first_field + index];
+    for (i = index; i < line->nb_fields; i++, field++)
+    {
+        if (!(len = PARSER_string_substW( file, field->text, buffer, size ))) break;
+        buffer += len + 1;
+    }
+    *buffer = 0;  /* add final null */
+    return TRUE;
+}
diff --git a/dlls/setupapi/queue.c b/dlls/setupapi/queue.c
new file mode 100644
index 0000000..59e0617
--- /dev/null
+++ b/dlls/setupapi/queue.c
@@ -0,0 +1,1237 @@
+/*
+ * Setupapi file queue routines
+ *
+ * Copyright 2002 Alexandre Julliard 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "ntddk.h"
+#include "winerror.h"
+#include "setupapi.h"
+#include "wine/unicode.h"
+#include "setupapi_private.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
+
+/* context structure for the default queue callback */
+struct default_callback_context
+{
+    HWND owner;
+    HWND progress;
+    UINT message;
+};
+
+struct file_op
+{
+    struct file_op *next;
+    UINT            style;
+    WCHAR          *src_root;
+    WCHAR          *src_path;
+    WCHAR          *src_file;
+    WCHAR          *src_descr;
+    WCHAR          *src_tag;
+    WCHAR          *dst_path;
+    WCHAR          *dst_file;
+};
+
+struct file_op_queue
+{
+    struct file_op *head;
+    struct file_op *tail;
+    unsigned int count;
+};
+
+struct file_queue
+{
+    struct file_op_queue copy_queue;
+    struct file_op_queue delete_queue;
+    struct file_op_queue rename_queue;
+    DWORD                flags;
+};
+
+
+inline static WCHAR *strdupW( const WCHAR *str )
+{
+    WCHAR *ret = NULL;
+    if (str)
+    {
+        int len = (strlenW(str) + 1) * sizeof(WCHAR);
+        if ((ret = HeapAlloc( GetProcessHeap(), 0, len ))) memcpy( ret, str, len );
+    }
+    return ret;
+}
+
+
+inline static WCHAR *strdupAtoW( const char *str )
+{
+    WCHAR *ret = NULL;
+    if (str)
+    {
+        DWORD len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
+        if ((ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
+            MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
+    }
+    return ret;
+}
+
+inline static char *strdupWtoA( const WCHAR *str )
+{
+    char *ret = NULL;
+    if (str)
+    {
+        DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
+        if ((ret = HeapAlloc( GetProcessHeap(), 0, len )))
+            WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
+    }
+    return ret;
+}
+
+/* append a file operation to a queue */
+inline static void queue_file_op( struct file_op_queue *queue, struct file_op *op )
+{
+    op->next = NULL;
+    if (queue->tail) queue->tail->next = op;
+    else queue->head = op;
+    queue->tail = op;
+    queue->count++;
+}
+
+/* free all the file operations on a given queue */
+static void free_file_op_queue( struct file_op_queue *queue )
+{
+    struct file_op *op;
+
+    for (op = queue->head; op; op = op->next)
+    {
+        HeapFree( GetProcessHeap(), 0, op->src_root );
+        HeapFree( GetProcessHeap(), 0, op->src_path );
+        HeapFree( GetProcessHeap(), 0, op->src_file );
+        HeapFree( GetProcessHeap(), 0, op->src_descr );
+        HeapFree( GetProcessHeap(), 0, op->src_tag );
+        HeapFree( GetProcessHeap(), 0, op->dst_path );
+        if (op->dst_file != op->src_file) HeapFree( GetProcessHeap(), 0, op->dst_file );
+    }
+}
+
+/* concat 3 strings to make a path, handling separators correctly */
+static void concat_W( WCHAR *buffer, const WCHAR *src1, const WCHAR *src2, const WCHAR *src3 )
+{
+    *buffer = 0;
+    if (src1 && *src1)
+    {
+        strcpyW( buffer, src1 );
+        buffer += strlenW(buffer );
+        if (buffer[-1] != '\\') *buffer++ = '\\';
+        if (src2) while (*src2 == '\\') src2++;
+    }
+
+    if (src2)
+    {
+        strcpyW( buffer, src2 );
+        buffer += strlenW(buffer );
+        if (buffer[-1] != '\\') *buffer++ = '\\';
+        if (src3) while (*src3 == '\\') src3++;
+    }
+    if (src3)
+    {
+        strcpyW( buffer, src3 );
+        buffer += strlenW(buffer );
+    }
+}
+
+
+/***********************************************************************
+ *            build_filepathsW
+ *
+ * Build a FILEPATHS_W structure for a given file operation.
+ */
+static BOOL build_filepathsW( const struct file_op *op, FILEPATHS_W *paths )
+{
+    int src_len = 1, dst_len = 1;
+    WCHAR *source = (PWSTR)paths->Source, *target = (PWSTR)paths->Target;
+
+    if (op->src_root) src_len += strlenW(op->src_root) + 1;
+    if (op->src_path) src_len += strlenW(op->src_path) + 1;
+    if (op->src_file) src_len += strlenW(op->src_file) + 1;
+    if (op->dst_path) dst_len += strlenW(op->dst_path) + 1;
+    if (op->dst_file) dst_len += strlenW(op->dst_file) + 1;
+    src_len *= sizeof(WCHAR);
+    dst_len *= sizeof(WCHAR);
+
+    if (!source || HeapSize( GetProcessHeap(), 0, source ) < src_len )
+    {
+        HeapFree( GetProcessHeap(), 0, source );
+        paths->Source = source = HeapAlloc( GetProcessHeap(), 0, src_len );
+    }
+    if (!target || HeapSize( GetProcessHeap(), 0, target ) < dst_len )
+    {
+        HeapFree( GetProcessHeap(), 0, target );
+        paths->Target = target = HeapAlloc( GetProcessHeap(), 0, dst_len );
+    }
+    if (!source || !target) return FALSE;
+    concat_W( source, op->src_root, op->src_path, op->src_file );
+    concat_W( target, NULL, op->dst_path, op->dst_file );
+    paths->Win32Error = 0;
+    paths->Flags      = 0;
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            QUEUE_callback_WtoA
+ *
+ * Map a file callback parameters from W to A and call the A callback.
+ */
+UINT CALLBACK QUEUE_callback_WtoA( void *context, UINT notification,
+                                   UINT_PTR param1, UINT_PTR param2 )
+{
+    struct callback_WtoA_context *callback_ctx = context;
+    char buffer[MAX_PATH];
+    UINT ret;
+    UINT_PTR old_param2 = param2;
+
+    switch(notification)
+    {
+    case SPFILENOTIFY_COPYERROR:
+        param2 = (UINT_PTR)&buffer;
+        /* fall through */
+    case SPFILENOTIFY_STARTDELETE:
+    case SPFILENOTIFY_ENDDELETE:
+    case SPFILENOTIFY_DELETEERROR:
+    case SPFILENOTIFY_STARTRENAME:
+    case SPFILENOTIFY_ENDRENAME:
+    case SPFILENOTIFY_RENAMEERROR:
+    case SPFILENOTIFY_STARTCOPY:
+    case SPFILENOTIFY_ENDCOPY:
+        {
+            FILEPATHS_W *pathsW = (FILEPATHS_W *)param1;
+            FILEPATHS_A pathsA;
+
+            pathsA.Source     = strdupWtoA( pathsW->Source );
+            pathsA.Target     = strdupWtoA( pathsW->Target );
+            pathsA.Win32Error = pathsW->Win32Error;
+            pathsA.Flags      = pathsW->Flags;
+            ret = callback_ctx->orig_handler( callback_ctx->orig_context, notification,
+                                              (UINT_PTR)&pathsA, param2 );
+            HeapFree( GetProcessHeap(), 0, (void *)pathsA.Source );
+            HeapFree( GetProcessHeap(), 0, (void *)pathsA.Target );
+        }
+        if (notification == SPFILENOTIFY_COPYERROR)
+            MultiByteToWideChar( CP_ACP, 0, buffer, -1, (WCHAR *)old_param2, MAX_PATH );
+        break;
+
+    case SPFILENOTIFY_NEEDMEDIA:
+    case SPFILENOTIFY_QUEUESCAN:
+        FIXME("mapping for %d not implemented\n",notification);
+    case SPFILENOTIFY_STARTQUEUE:
+    case SPFILENOTIFY_ENDQUEUE:
+    case SPFILENOTIFY_STARTSUBQUEUE:
+    case SPFILENOTIFY_ENDSUBQUEUE:
+    default:
+        ret = callback_ctx->orig_handler( callback_ctx->orig_context, notification, param1, param2 );
+        break;
+    }
+        return ret;
+}
+
+
+/***********************************************************************
+ *            get_src_file_info
+ *
+ * Retrieve the source file information for a given file.
+ */
+static void get_src_file_info( HINF hinf, struct file_op *op )
+{
+    static const WCHAR SourceDisksNames[] =
+        {'S','o','u','r','c','e','D','i','s','k','s','N','a','m','e','s',0};
+    static const WCHAR SourceDisksFiles[] =
+        {'S','o','u','r','c','e','D','i','s','k','s','F','i','l','e','s',0};
+
+    INFCONTEXT file_ctx, disk_ctx;
+    INT id, diskid;
+    DWORD len, len2;
+
+    /* find the SourceDisksFiles entry */
+    if (!SetupFindFirstLineW( hinf, SourceDisksFiles, op->src_file, &file_ctx ))
+    {
+        const WCHAR *dir;
+
+        if ((op->style & (SP_COPY_SOURCE_ABSOLUTE|SP_COPY_SOURCEPATH_ABSOLUTE))) return;
+        /* no specific info, use .inf file source directory */
+        if (!op->src_root && (dir = DIRID_get_string( hinf, DIRID_SRCPATH )))
+            op->src_root = strdupW( dir );
+        return;
+    }
+    if (!SetupGetIntField( &file_ctx, 1, &diskid )) return;
+
+    /* now find the diskid in the SourceDisksNames section */
+    if (!SetupFindFirstLineW( hinf, SourceDisksNames, NULL, &disk_ctx )) return;
+    for (;;)
+    {
+        if (SetupGetIntField( &disk_ctx, 0, &id ) && (id == diskid)) break;
+        if (!SetupFindNextLine( &disk_ctx, &disk_ctx )) return;
+    }
+
+    /* and fill in the missing info */
+
+    if (!op->src_descr)
+    {
+        if (SetupGetStringFieldW( &disk_ctx, 1, NULL, 0, &len ) &&
+            (op->src_descr = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) )))
+            SetupGetStringFieldW( &disk_ctx, 1, op->src_descr, len, NULL );
+    }
+    if (!op->src_tag)
+    {
+        if (SetupGetStringFieldW( &disk_ctx, 2, NULL, 0, &len ) &&
+            (op->src_tag = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) )))
+            SetupGetStringFieldW( &disk_ctx, 2, op->src_tag, len, NULL );
+    }
+    if (!op->src_path && !(op->style & SP_COPY_SOURCE_ABSOLUTE))
+    {
+        if (!(op->style & SP_COPY_SOURCEPATH_ABSOLUTE))
+        {
+            /* retrieve relative path for this disk */
+            if (!SetupGetStringFieldW( &disk_ctx, 4, NULL, 0, &len )) len = 0;
+        }
+        /* retrieve relative path for this file */
+        if (!SetupGetStringFieldW( &file_ctx, 2, NULL, 0, &len2 )) len2 = 0;
+
+        if ((len || len2) &&
+            (op->src_path = HeapAlloc( GetProcessHeap(), 0, (len+len2)*sizeof(WCHAR) )))
+        {
+            WCHAR *ptr = op->src_path;
+            if (len)
+            {
+                SetupGetStringFieldW( &disk_ctx, 4, op->src_path, len, NULL );
+                ptr = op->src_path + strlenW(op->src_path);
+                if (len2 && ptr > op->src_path && ptr[-1] != '\\') *ptr++ = '\\';
+            }
+            if (!SetupGetStringFieldW( &disk_ctx, 4, ptr, len2, NULL )) *ptr = 0;
+        }
+    }
+    if (!op->src_root) op->src_root = strdupW( PARSER_get_src_root(hinf) );
+}
+
+
+/***********************************************************************
+ *            get_destination_dir
+ *
+ * Retrieve the destination dir for a given section.
+ */
+static WCHAR *get_destination_dir( HINF hinf, const WCHAR *section )
+{
+    static const WCHAR Dest[] = {'D','e','s','t','i','n','a','t','i','o','n','D','i','r','s',0};
+    static const WCHAR Def[]  = {'D','e','f','a','u','l','t','D','e','s','t','D','i','r',0};
+
+    const WCHAR *dir;
+    WCHAR *ptr, *ret;
+    INFCONTEXT context;
+    INT dirid;
+    DWORD len1, len2;
+
+    if (!SetupFindFirstLineW( hinf, Dest, section, &context ) &&
+        !SetupFindFirstLineW( hinf, Dest, Def, &context )) return NULL;
+    if (!SetupGetIntField( &context, 1, &dirid )) return NULL;
+    if (!(dir = DIRID_get_string( hinf, dirid ))) return NULL;
+    len1 = strlenW(dir) + 1;
+    if (!SetupGetStringFieldW( &context, 2, NULL, 0, &len2 )) len2 = 0;
+    if (!(ret = HeapAlloc( GetProcessHeap(), 0, (len1+len2) * sizeof(WCHAR) ))) return NULL;
+    strcpyW( ret, dir );
+    ptr = ret + strlenW(ret);
+    if (len2 && ptr > ret && ptr[-1] != '\\') *ptr++ = '\\';
+    if (!SetupGetStringFieldW( &context, 2, ptr, len2, NULL )) *ptr = 0;
+    return ret;
+}
+
+
+static void (WINAPI *pExtractFiles)( LPSTR, LPSTR, DWORD, DWORD, DWORD, DWORD );
+
+/***********************************************************************
+ *            extract_cabinet_file
+ *
+ * Extract a file from a .cab file.
+ */
+static BOOL extract_cabinet_file( const WCHAR *cabinet, const WCHAR *root,
+                                  const WCHAR *src, const WCHAR *dst )
+{
+    static const WCHAR extW[] = {'.','c','a','b',0};
+    static HMODULE advpack;
+
+    char *cab_path, *cab_file;
+    int len = strlenW( cabinet );
+
+    /* make sure the cabinet file has a .cab extension */
+    if (len <= 4 || strcmpiW( cabinet + len - 4, extW )) return FALSE;
+    if (!pExtractFiles)
+    {
+        if (!advpack && !(advpack = LoadLibraryA( "advpack.dll" )))
+        {
+            ERR( "could not load advpack.dll\n" );
+            return FALSE;
+        }
+        if (!(pExtractFiles = (void *)GetProcAddress( advpack, "ExtractFiles" )))
+        {
+            ERR( "could not find ExtractFiles in advpack.dll\n" );
+            return FALSE;
+        }
+    }
+
+    if (!(cab_path = strdupWtoA( root ))) return FALSE;
+    len = WideCharToMultiByte( CP_ACP, 0, cabinet, -1, NULL, 0, NULL, NULL );
+    if (!(cab_file = HeapAlloc( GetProcessHeap(), 0, strlen(cab_path) + len + 1 )))
+    {
+        HeapFree( GetProcessHeap(), 0, cab_path );
+        return FALSE;
+    }
+    strcpy( cab_file, cab_path );
+    if (cab_file[0] && cab_file[strlen(cab_file)-1] != '\\') strcat( cab_file, "\\" );
+    WideCharToMultiByte( CP_ACP, 0, cabinet, -1, cab_file + strlen(cab_file), len, NULL, NULL );
+    FIXME( "awful hack: extracting cabinet %s\n", debugstr_a(cab_file) );
+    pExtractFiles( cab_file, cab_path, 0, 0, 0, 0 );
+    HeapFree( GetProcessHeap(), 0, cab_file );
+    HeapFree( GetProcessHeap(), 0, cab_path );
+    return CopyFileW( src, dst, FALSE /*FIXME*/ );
+}
+
+
+/***********************************************************************
+ *            SetupOpenFileQueue   (SETUPAPI.@)
+ */
+HSPFILEQ WINAPI SetupOpenFileQueue(void)
+{
+    struct file_queue *queue;
+
+    if (!(queue = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*queue))))
+        return (HSPFILEQ)INVALID_HANDLE_VALUE;
+    return queue;
+}
+
+
+/***********************************************************************
+ *            SetupCloseFileQueue   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupCloseFileQueue( HSPFILEQ handle )
+{
+    struct file_queue *queue = handle;
+
+    free_file_op_queue( &queue->copy_queue );
+    free_file_op_queue( &queue->rename_queue );
+    free_file_op_queue( &queue->delete_queue );
+    HeapFree( GetProcessHeap(), 0, queue );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            SetupQueueCopyIndirectA   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueCopyIndirectA( PSP_FILE_COPY_PARAMS_A params )
+{
+    struct file_queue *queue = params->QueueHandle;
+    struct file_op *op;
+
+    if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
+    op->style      = params->CopyStyle;
+    op->src_root   = strdupAtoW( params->SourceRootPath );
+    op->src_path   = strdupAtoW( params->SourcePath );
+    op->src_file   = strdupAtoW( params->SourceFilename );
+    op->src_descr  = strdupAtoW( params->SourceDescription );
+    op->src_tag    = strdupAtoW( params->SourceTagfile );
+    op->dst_path   = strdupAtoW( params->TargetDirectory );
+    op->dst_file   = strdupAtoW( params->TargetFilename );
+
+    /* some defaults */
+    if (!op->src_file) op->src_file = op->dst_file;
+    if (params->LayoutInf)
+    {
+        get_src_file_info( params->LayoutInf, op );
+        if (!op->dst_path) op->dst_path = get_destination_dir( params->LayoutInf, op->dst_file );
+    }
+
+    TRACE( "root=%s path=%s file=%s -> dir=%s file=%s  descr=%s tag=%s\n",
+           debugstr_w(op->src_root), debugstr_w(op->src_path), debugstr_w(op->src_file),
+           debugstr_w(op->dst_path), debugstr_w(op->dst_file),
+           debugstr_w(op->src_descr), debugstr_w(op->src_tag) );
+
+    queue_file_op( &queue->copy_queue, op );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            SetupQueueCopyIndirectW   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueCopyIndirectW( PSP_FILE_COPY_PARAMS_W params )
+{
+    struct file_queue *queue = params->QueueHandle;
+    struct file_op *op;
+
+    if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
+    op->style      = params->CopyStyle;
+    op->src_root   = strdupW( params->SourceRootPath );
+    op->src_path   = strdupW( params->SourcePath );
+    op->src_file   = strdupW( params->SourceFilename );
+    op->src_descr  = strdupW( params->SourceDescription );
+    op->src_tag    = strdupW( params->SourceTagfile );
+    op->dst_path   = strdupW( params->TargetDirectory );
+    op->dst_file   = strdupW( params->TargetFilename );
+
+    /* some defaults */
+    if (!op->src_file) op->src_file = op->dst_file;
+    if (params->LayoutInf)
+    {
+        get_src_file_info( params->LayoutInf, op );
+        if (!op->dst_path) op->dst_path = get_destination_dir( params->LayoutInf, op->dst_file );
+    }
+
+    TRACE( "root=%s path=%s file=%s -> dir=%s file=%s  descr=%s tag=%s\n",
+           debugstr_w(op->src_root), debugstr_w(op->src_path), debugstr_w(op->src_file),
+           debugstr_w(op->dst_path), debugstr_w(op->dst_file),
+           debugstr_w(op->src_descr), debugstr_w(op->src_tag) );
+
+    queue_file_op( &queue->copy_queue, op );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            SetupQueueCopyA   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueCopyA( HSPFILEQ queue, PCSTR src_root, PCSTR src_path, PCSTR src_file,
+                             PCSTR src_descr, PCSTR src_tag, PCSTR dst_dir, PCSTR dst_file,
+                             DWORD style )
+{
+    SP_FILE_COPY_PARAMS_A params;
+
+    params.cbSize             = sizeof(params);
+    params.QueueHandle        = queue;
+    params.SourceRootPath     = src_root;
+    params.SourcePath         = src_path;
+    params.SourceFilename     = src_file;
+    params.SourceDescription  = src_descr;
+    params.SourceTagfile      = src_tag;
+    params.TargetDirectory    = dst_dir;
+    params.TargetFilename     = dst_file;
+    params.CopyStyle          = style;
+    params.LayoutInf          = 0;
+    params.SecurityDescriptor = NULL;
+    return SetupQueueCopyIndirectA( &params );
+}
+
+
+/***********************************************************************
+ *            SetupQueueCopyW   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueCopyW( HSPFILEQ queue, PCWSTR src_root, PCWSTR src_path, PCWSTR src_file,
+                             PCWSTR src_descr, PCWSTR src_tag, PCWSTR dst_dir, PCWSTR dst_file,
+                             DWORD style )
+{
+    SP_FILE_COPY_PARAMS_W params;
+
+    params.cbSize             = sizeof(params);
+    params.QueueHandle        = queue;
+    params.SourceRootPath     = src_root;
+    params.SourcePath         = src_path;
+    params.SourceFilename     = src_file;
+    params.SourceDescription  = src_descr;
+    params.SourceTagfile      = src_tag;
+    params.TargetDirectory    = dst_dir;
+    params.TargetFilename     = dst_file;
+    params.CopyStyle          = style;
+    params.LayoutInf          = 0;
+    params.SecurityDescriptor = NULL;
+    return SetupQueueCopyIndirectW( &params );
+}
+
+
+/***********************************************************************
+ *            SetupQueueDefaultCopyA   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueDefaultCopyA( HSPFILEQ queue, HINF hinf, PCSTR src_root, PCSTR src_file,
+                                    PCSTR dst_file, DWORD style )
+{
+    SP_FILE_COPY_PARAMS_A params;
+
+    params.cbSize             = sizeof(params);
+    params.QueueHandle        = queue;
+    params.SourceRootPath     = src_root;
+    params.SourcePath         = NULL;
+    params.SourceFilename     = src_file;
+    params.SourceDescription  = NULL;
+    params.SourceTagfile      = NULL;
+    params.TargetDirectory    = NULL;
+    params.TargetFilename     = dst_file;
+    params.CopyStyle          = style;
+    params.LayoutInf          = hinf;
+    params.SecurityDescriptor = NULL;
+    return SetupQueueCopyIndirectA( &params );
+}
+
+
+/***********************************************************************
+ *            SetupQueueDefaultCopyW   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueDefaultCopyW( HSPFILEQ queue, HINF hinf, PCWSTR src_root, PCWSTR src_file,
+                                    PCWSTR dst_file, DWORD style )
+{
+    SP_FILE_COPY_PARAMS_W params;
+
+    params.cbSize             = sizeof(params);
+    params.QueueHandle        = queue;
+    params.SourceRootPath     = src_root;
+    params.SourcePath         = NULL;
+    params.SourceFilename     = src_file;
+    params.SourceDescription  = NULL;
+    params.SourceTagfile      = NULL;
+    params.TargetDirectory    = NULL;
+    params.TargetFilename     = dst_file;
+    params.CopyStyle          = style;
+    params.LayoutInf          = hinf;
+    params.SecurityDescriptor = NULL;
+    return SetupQueueCopyIndirectW( &params );
+}
+
+
+/***********************************************************************
+ *            SetupQueueDeleteA   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueDeleteA( HSPFILEQ handle, PCSTR part1, PCSTR part2 )
+{
+    struct file_queue *queue = handle;
+    struct file_op *op;
+
+    if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
+    op->style      = 0;
+    op->src_root   = NULL;
+    op->src_path   = NULL;
+    op->src_file   = NULL;
+    op->src_descr  = NULL;
+    op->src_tag    = NULL;
+    op->dst_path   = strdupAtoW( part1 );
+    op->dst_file   = strdupAtoW( part2 );
+    queue_file_op( &queue->delete_queue, op );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            SetupQueueDeleteW   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueDeleteW( HSPFILEQ handle, PCWSTR part1, PCWSTR part2 )
+{
+    struct file_queue *queue = handle;
+    struct file_op *op;
+
+    if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
+    op->style      = 0;
+    op->src_root   = NULL;
+    op->src_path   = NULL;
+    op->src_file   = NULL;
+    op->src_descr  = NULL;
+    op->src_tag    = NULL;
+    op->dst_path   = strdupW( part1 );
+    op->dst_file   = strdupW( part2 );
+    queue_file_op( &queue->delete_queue, op );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            SetupQueueRenameA   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueRenameA( HSPFILEQ handle, PCSTR SourcePath, PCSTR SourceFilename,
+                               PCSTR TargetPath, PCSTR TargetFilename )
+{
+    struct file_queue *queue = handle;
+    struct file_op *op;
+
+    if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
+    op->style      = 0;
+    op->src_root   = NULL;
+    op->src_path   = strdupAtoW( SourcePath );
+    op->src_file   = strdupAtoW( SourceFilename );
+    op->src_descr  = NULL;
+    op->src_tag    = NULL;
+    op->dst_path   = strdupAtoW( TargetPath );
+    op->dst_file   = strdupAtoW( TargetFilename );
+    queue_file_op( &queue->rename_queue, op );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            SetupQueueRenameW   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueRenameW( HSPFILEQ handle, PCWSTR SourcePath, PCWSTR SourceFilename,
+                               PCWSTR TargetPath, PCWSTR TargetFilename )
+{
+    struct file_queue *queue = handle;
+    struct file_op *op;
+
+    if (!(op = HeapAlloc( GetProcessHeap(), 0, sizeof(*op) ))) return FALSE;
+    op->style      = 0;
+    op->src_root   = NULL;
+    op->src_path   = strdupW( SourcePath );
+    op->src_file   = strdupW( SourceFilename );
+    op->src_descr  = NULL;
+    op->src_tag    = NULL;
+    op->dst_path   = strdupW( TargetPath );
+    op->dst_file   = strdupW( TargetFilename );
+    queue_file_op( &queue->rename_queue, op );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            SetupQueueCopySectionA   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueCopySectionA( HSPFILEQ queue, PCSTR src_root, HINF hinf, HINF hlist,
+                                    PCSTR section, DWORD style )
+{
+    UNICODE_STRING sectionW;
+    BOOL ret = FALSE;
+
+    if (!RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
+    {
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+        return FALSE;
+    }
+    if (!src_root)
+        ret = SetupQueueCopySectionW( queue, NULL, hinf, hlist, sectionW.Buffer, style );
+    else
+    {
+        UNICODE_STRING srcW;
+        if (RtlCreateUnicodeStringFromAsciiz( &srcW, src_root ))
+        {
+            ret = SetupQueueCopySectionW( queue, srcW.Buffer, hinf, hlist, sectionW.Buffer, style );
+            RtlFreeUnicodeString( &srcW );
+        }
+        else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+    }
+    RtlFreeUnicodeString( &sectionW );
+    return ret;
+}
+
+
+/***********************************************************************
+ *            SetupQueueCopySectionW   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueCopySectionW( HSPFILEQ queue, PCWSTR src_root, HINF hinf, HINF hlist,
+                                    PCWSTR section, DWORD style )
+{
+    SP_FILE_COPY_PARAMS_W params;
+    INFCONTEXT context;
+    WCHAR dest[MAX_PATH], src[MAX_PATH];
+    INT flags;
+
+    TRACE( "hinf=%p/%p section=%s root=%s\n",
+           hinf, hlist, debugstr_w(section), debugstr_w(src_root) );
+
+    params.cbSize             = sizeof(params);
+    params.QueueHandle        = queue;
+    params.SourceRootPath     = src_root;
+    params.SourcePath         = NULL;
+    params.SourceDescription  = NULL;
+    params.SourceTagfile      = NULL;
+    params.TargetFilename     = dest;
+    params.CopyStyle          = style;
+    params.LayoutInf          = hinf;
+    params.SecurityDescriptor = NULL;
+
+    if (!hlist) hlist = hinf;
+    if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE;
+    if (!(params.TargetDirectory = get_destination_dir( hinf, section ))) return FALSE;
+    do
+    {
+        if (!SetupGetStringFieldW( &context, 1, dest, sizeof(dest)/sizeof(WCHAR), NULL ))
+            return FALSE;
+        if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL )) *src = 0;
+        if (!SetupGetIntField( &context, 4, &flags )) flags = 0;  /* FIXME */
+
+        params.SourceFilename = *src ? src : NULL;
+        if (!SetupQueueCopyIndirectW( &params )) return FALSE;
+    } while (SetupFindNextLine( &context, &context ));
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            SetupQueueDeleteSectionA   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueDeleteSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section )
+{
+    UNICODE_STRING sectionW;
+    BOOL ret = FALSE;
+
+    if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
+    {
+        ret = SetupQueueDeleteSectionW( queue, hinf, hlist, sectionW.Buffer );
+        RtlFreeUnicodeString( &sectionW );
+    }
+    else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+    return ret;
+}
+
+
+/***********************************************************************
+ *            SetupQueueDeleteSectionW   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueDeleteSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section )
+{
+    INFCONTEXT context;
+    WCHAR *dest_dir;
+    WCHAR buffer[MAX_PATH];
+    BOOL ret = FALSE;
+    INT flags;
+
+    TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) );
+
+    if (!hlist) hlist = hinf;
+    if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE;
+    if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE;
+    do
+    {
+        if (!SetupGetStringFieldW( &context, 1, buffer, sizeof(buffer)/sizeof(WCHAR), NULL ))
+            goto done;
+        if (!SetupGetIntField( &context, 4, &flags )) flags = 0;
+        if (!SetupQueueDeleteW( queue, dest_dir, buffer )) goto done;
+    } while (SetupFindNextLine( &context, &context ));
+
+    ret = TRUE;
+ done:
+    HeapFree( GetProcessHeap(), 0, dest_dir );
+    return ret;
+}
+
+
+/***********************************************************************
+ *            SetupQueueRenameSectionA   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueRenameSectionA( HSPFILEQ queue, HINF hinf, HINF hlist, PCSTR section )
+{
+    UNICODE_STRING sectionW;
+    BOOL ret = FALSE;
+
+    if (RtlCreateUnicodeStringFromAsciiz( &sectionW, section ))
+    {
+        ret = SetupQueueRenameSectionW( queue, hinf, hlist, sectionW.Buffer );
+        RtlFreeUnicodeString( &sectionW );
+    }
+    else SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+    return ret;
+}
+
+
+/***********************************************************************
+ *            SetupQueueRenameSectionW   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupQueueRenameSectionW( HSPFILEQ queue, HINF hinf, HINF hlist, PCWSTR section )
+{
+    INFCONTEXT context;
+    WCHAR *dest_dir;
+    WCHAR src[MAX_PATH], dst[MAX_PATH];
+    BOOL ret = FALSE;
+
+    TRACE( "hinf=%p/%p section=%s\n", hinf, hlist, debugstr_w(section) );
+
+    if (!hlist) hlist = hinf;
+    if (!SetupFindFirstLineW( hlist, section, NULL, &context )) return FALSE;
+    if (!(dest_dir = get_destination_dir( hinf, section ))) return FALSE;
+    do
+    {
+        if (!SetupGetStringFieldW( &context, 1, dst, sizeof(dst)/sizeof(WCHAR), NULL ))
+            goto done;
+        if (!SetupGetStringFieldW( &context, 2, src, sizeof(src)/sizeof(WCHAR), NULL ))
+            goto done;
+        if (!SetupQueueRenameW( queue, dest_dir, src, NULL, dst )) goto done;
+    } while (SetupFindNextLine( &context, &context ));
+
+    ret = TRUE;
+ done:
+    HeapFree( GetProcessHeap(), 0, dest_dir );
+    return ret;
+}
+
+
+/***********************************************************************
+ *            SetupCommitFileQueueA   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupCommitFileQueueA( HWND owner, HSPFILEQ queue, PSP_FILE_CALLBACK_A handler,
+                                   PVOID context )
+{
+    struct callback_WtoA_context ctx;
+
+    ctx.orig_context = context;
+    ctx.orig_handler = handler;
+    return SetupCommitFileQueueW( owner, queue, QUEUE_callback_WtoA, &ctx );
+}
+
+
+/***********************************************************************
+ *            create_full_pathW
+ *
+ * Recursively create all directories in the path.
+ */
+static BOOL create_full_pathW(const WCHAR *path)
+{
+    BOOL ret = TRUE;
+    int len;
+    WCHAR *new_path;
+
+    new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) * sizeof(WCHAR));
+    strcpyW(new_path, path);
+
+    while((len = strlenW(new_path)) && new_path[len - 1] == '\\')
+	new_path[len - 1] = 0;
+
+    while(!CreateDirectoryW(new_path, NULL))
+    {
+	WCHAR *slash;
+	DWORD last_error = GetLastError();
+
+	if(last_error == ERROR_ALREADY_EXISTS)
+	    break;
+
+	if(last_error != ERROR_PATH_NOT_FOUND)
+	{
+	    ret = FALSE;
+	    break;
+	}
+
+	if(!(slash = strrchrW(new_path, '\\')))
+	{
+	    ret = FALSE;
+	    break;
+	}
+
+	len = slash - new_path;
+	new_path[len] = 0;
+	if(!create_full_pathW(new_path))
+	{
+	    ret = FALSE;
+	    break;
+	}
+	new_path[len] = '\\';
+    }
+
+    HeapFree(GetProcessHeap(), 0, new_path);
+    return ret;
+}
+
+
+/***********************************************************************
+ *            SetupCommitFileQueueW   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupCommitFileQueueW( HWND owner, HSPFILEQ handle, PSP_FILE_CALLBACK_W handler,
+                                   PVOID context )
+{
+    struct file_queue *queue = handle;
+    struct file_op *op;
+    BOOL result = FALSE;
+    FILEPATHS_W paths;
+    UINT op_result;
+
+    paths.Source = paths.Target = NULL;
+
+    if (!queue->copy_queue.count && !queue->delete_queue.count && !queue->rename_queue.count)
+        return TRUE;  /* nothing to do */
+
+    if (!handler( context, SPFILENOTIFY_STARTQUEUE, owner, 0 )) return FALSE;
+
+    /* perform deletes */
+
+    if (queue->delete_queue.count)
+    {
+        if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_DELETE,
+                       queue->delete_queue.count ))) goto done;
+        for (op = queue->delete_queue.head; op; op = op->next)
+        {
+            build_filepathsW( op, &paths );
+            op_result = handler( context, SPFILENOTIFY_STARTDELETE, (UINT_PTR)&paths, FILEOP_DELETE);
+            if (op_result == FILEOP_ABORT) goto done;
+            while (op_result == FILEOP_DOIT)
+            {
+                TRACE( "deleting file %s\n", debugstr_w(paths.Target) );
+                if (DeleteFileW( paths.Target )) break;  /* success */
+                paths.Win32Error = GetLastError();
+                op_result = handler( context, SPFILENOTIFY_DELETEERROR, (UINT_PTR)&paths, 0 );
+                if (op_result == FILEOP_ABORT) goto done;
+            }
+            handler( context, SPFILENOTIFY_ENDDELETE, (UINT_PTR)&paths, 0 );
+        }
+        handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_DELETE, 0 );
+    }
+
+    /* perform renames */
+
+    if (queue->rename_queue.count)
+    {
+        if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_RENAME,
+                       queue->rename_queue.count ))) goto done;
+        for (op = queue->rename_queue.head; op; op = op->next)
+        {
+            build_filepathsW( op, &paths );
+            op_result = handler( context, SPFILENOTIFY_STARTRENAME, (UINT_PTR)&paths, FILEOP_RENAME);
+            if (op_result == FILEOP_ABORT) goto done;
+            while (op_result == FILEOP_DOIT)
+            {
+                TRACE( "renaming file %s -> %s\n",
+                       debugstr_w(paths.Source), debugstr_w(paths.Target) );
+                if (MoveFileW( paths.Source, paths.Target )) break;  /* success */
+                paths.Win32Error = GetLastError();
+                op_result = handler( context, SPFILENOTIFY_RENAMEERROR, (UINT_PTR)&paths, 0 );
+                if (op_result == FILEOP_ABORT) goto done;
+            }
+            handler( context, SPFILENOTIFY_ENDRENAME, (UINT_PTR)&paths, 0 );
+        }
+        handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_RENAME, 0 );
+    }
+
+    /* perform copies */
+
+    if (queue->copy_queue.count)
+    {
+        if (!(handler( context, SPFILENOTIFY_STARTSUBQUEUE, FILEOP_COPY,
+                       queue->copy_queue.count ))) goto done;
+        for (op = queue->copy_queue.head; op; op = op->next)
+        {
+            WCHAR newpath[MAX_PATH];
+
+            build_filepathsW( op, &paths );
+            op_result = handler( context, SPFILENOTIFY_STARTCOPY, (UINT_PTR)&paths, FILEOP_COPY );
+            if (op_result == FILEOP_ABORT) goto done;
+            if (op_result == FILEOP_NEWPATH) op_result = FILEOP_DOIT;
+            while (op_result == FILEOP_DOIT || op_result == FILEOP_NEWPATH)
+            {
+                TRACE( "copying file %s -> %s\n",
+                       debugstr_w( op_result == FILEOP_NEWPATH ? newpath : paths.Source ),
+                       debugstr_w(paths.Target) );
+                if (op->dst_path)
+		{
+		    if (!create_full_pathW( op->dst_path ))
+		    {
+			paths.Win32Error = GetLastError();
+			op_result = handler( context, SPFILENOTIFY_COPYERROR,
+					     (UINT_PTR)&paths, (UINT_PTR)newpath );
+			if (op_result == FILEOP_ABORT) goto done;
+		    }
+		}
+                if (CopyFileW( op_result == FILEOP_NEWPATH ? newpath : paths.Source,
+                               paths.Target, FALSE /*FIXME*/ )) break;  /* success */
+                /* try to extract it from the cabinet file */
+                if (op->src_tag)
+                {
+                    if (extract_cabinet_file( op->src_tag, op->src_root,
+                                              paths.Source, paths.Target )) break;
+                }
+                paths.Win32Error = GetLastError();
+                op_result = handler( context, SPFILENOTIFY_COPYERROR,
+                                     (UINT_PTR)&paths, (UINT_PTR)newpath );
+                if (op_result == FILEOP_ABORT) goto done;
+            }
+            handler( context, SPFILENOTIFY_ENDCOPY, (UINT_PTR)&paths, 0 );
+        }
+        handler( context, SPFILENOTIFY_ENDSUBQUEUE, FILEOP_COPY, 0 );
+    }
+
+
+    result = TRUE;
+
+ done:
+    handler( context, SPFILENOTIFY_ENDQUEUE, result, 0 );
+    HeapFree( GetProcessHeap(), 0, (void *)paths.Source );
+    HeapFree( GetProcessHeap(), 0, (void *)paths.Target );
+    return result;
+}
+
+
+/***********************************************************************
+ *            SetupScanFileQueueA   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupScanFileQueueA( HSPFILEQ queue, DWORD flags, HWND window,
+                                 PSP_FILE_CALLBACK_A callback, PVOID context, PDWORD result )
+{
+    FIXME("stub\n");
+    return FALSE;
+}
+
+
+/***********************************************************************
+ *            SetupScanFileQueueW   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupScanFileQueueW( HSPFILEQ queue, DWORD flags, HWND window,
+                                 PSP_FILE_CALLBACK_W callback, PVOID context, PDWORD result )
+{
+    FIXME("stub\n");
+    return FALSE;
+}
+
+
+/***********************************************************************
+ *            SetupGetFileQueueCount   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupGetFileQueueCount( HSPFILEQ handle, UINT op, PUINT result )
+{
+    struct file_queue *queue = handle;
+
+    switch(op)
+    {
+    case FILEOP_COPY:
+        *result = queue->copy_queue.count;
+        return TRUE;
+    case FILEOP_RENAME:
+        *result = queue->rename_queue.count;
+        return TRUE;
+    case FILEOP_DELETE:
+        *result = queue->delete_queue.count;
+        return TRUE;
+    }
+    return FALSE;
+}
+
+
+/***********************************************************************
+ *            SetupGetFileQueueFlags   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupGetFileQueueFlags( HSPFILEQ handle, PDWORD flags )
+{
+    struct file_queue *queue = handle;
+    *flags = queue->flags;
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            SetupSetFileQueueFlags   (SETUPAPI.@)
+ */
+BOOL WINAPI SetupSetFileQueueFlags( HSPFILEQ handle, DWORD mask, DWORD flags )
+{
+    struct file_queue *queue = handle;
+    queue->flags = (queue->flags & ~mask) | flags;
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *            SetupInitDefaultQueueCallback   (SETUPAPI.@)
+ */
+PVOID WINAPI SetupInitDefaultQueueCallback( HWND owner )
+{
+    return SetupInitDefaultQueueCallbackEx( owner, 0, 0, 0, NULL );
+}
+
+
+/***********************************************************************
+ *            SetupInitDefaultQueueCallbackEx   (SETUPAPI.@)
+ */
+PVOID WINAPI SetupInitDefaultQueueCallbackEx( HWND owner, HWND progress, UINT msg,
+                                              DWORD reserved1, PVOID reserved2 )
+{
+    struct default_callback_context *context;
+
+    if ((context = HeapAlloc( GetProcessHeap(), 0, sizeof(*context) )))
+    {
+        context->owner    = owner;
+        context->progress = progress;
+        context->message  = msg;
+    }
+    return context;
+}
+
+
+/***********************************************************************
+ *            SetupTermDefaultQueueCallback   (SETUPAPI.@)
+ */
+void WINAPI SetupTermDefaultQueueCallback( PVOID context )
+{
+    HeapFree( GetProcessHeap(), 0, context );
+}
+
+
+/***********************************************************************
+ *            SetupDefaultQueueCallbackA   (SETUPAPI.@)
+ */
+UINT WINAPI SetupDefaultQueueCallbackA( PVOID context, UINT notification,
+                                        UINT_PTR param1, UINT_PTR param2 )
+{
+    FILEPATHS_A *paths = (FILEPATHS_A *)param1;
+
+    switch(notification)
+    {
+    case SPFILENOTIFY_STARTQUEUE:
+        TRACE( "start queue\n" );
+        return TRUE;
+    case SPFILENOTIFY_ENDQUEUE:
+        TRACE( "end queue\n" );
+        return 0;
+    case SPFILENOTIFY_STARTSUBQUEUE:
+        TRACE( "start subqueue %d count %d\n", param1, param2 );
+        return TRUE;
+    case SPFILENOTIFY_ENDSUBQUEUE:
+        TRACE( "end subqueue %d\n", param1 );
+        return 0;
+    case SPFILENOTIFY_STARTDELETE:
+        TRACE( "start delete %s\n", debugstr_a(paths->Target) );
+        return FILEOP_DOIT;
+    case SPFILENOTIFY_ENDDELETE:
+        TRACE( "end delete %s\n", debugstr_a(paths->Target) );
+        return 0;
+    case SPFILENOTIFY_DELETEERROR:
+        ERR( "delete error %d %s\n", paths->Win32Error, debugstr_a(paths->Target) );
+        return FILEOP_SKIP;
+    case SPFILENOTIFY_STARTRENAME:
+        TRACE( "start rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
+        return FILEOP_DOIT;
+    case SPFILENOTIFY_ENDRENAME:
+        TRACE( "end rename %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
+        return 0;
+    case SPFILENOTIFY_RENAMEERROR:
+        ERR( "rename error %d %s -> %s\n", paths->Win32Error,
+             debugstr_a(paths->Source), debugstr_a(paths->Target) );
+        return FILEOP_SKIP;
+    case SPFILENOTIFY_STARTCOPY:
+        TRACE( "start copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
+        return FILEOP_DOIT;
+    case SPFILENOTIFY_ENDCOPY:
+        TRACE( "end copy %s -> %s\n", debugstr_a(paths->Source), debugstr_a(paths->Target) );
+        return 0;
+    case SPFILENOTIFY_COPYERROR:
+        ERR( "copy error %d %s -> %s\n", paths->Win32Error,
+             debugstr_a(paths->Source), debugstr_a(paths->Target) );
+        return FILEOP_SKIP;
+    case SPFILENOTIFY_NEEDMEDIA:
+        TRACE( "need media\n" );
+        return FILEOP_SKIP;
+    default:
+        FIXME( "notification %d params %x,%x\n", notification, param1, param2 );
+        break;
+    }
+    return 0;
+}
+
+
+/***********************************************************************
+ *            SetupDefaultQueueCallbackW   (SETUPAPI.@)
+ */
+UINT WINAPI SetupDefaultQueueCallbackW( PVOID context, UINT notification,
+                                        UINT_PTR param1, UINT_PTR param2 )
+{
+    FIXME( "notification %d params %x,%x\n", notification, param1, param2 );
+    return FILEOP_SKIP;
+}
diff --git a/dlls/setupapi/setupapi.spec b/dlls/setupapi/setupapi.spec
index 27af8b9..b12e23c 100644
--- a/dlls/setupapi/setupapi.spec
+++ b/dlls/setupapi/setupapi.spec
@@ -9,229 +9,302 @@
 
 debug_channels (setupapi)
 
-# almost all functions are commented out for now. Ordinals are from setupapi.dll 4.0
-
-# 45 stdcall SetupAddInstallSectionToDiskSpaceListA()         SetupAddInstallSectionToDiskSpaceListA
-# 46 stdcall SetupAddInstallSectionToDiskSpaceListW()         SetupAddInstallSectionToDiskSpaceListW
-# 47 stdcall SetupAddSectionToDiskSpaceListA()                SetupAddSectionToDiskSpaceListA
-# 48 stdcall SetupAddSectionToDiskSpaceListW()                SetupAddSectionToDiskSpaceListW
-# 49 stdcall SetupAddToDiskSpaceListA()                       SetupAddToDiskSpaceListA
-# 50 stdcall SetupAddToDiskSpaceListW()                       SetupAddToDiskSpaceListW
-# 51 stdcall SetupAddToSourceListA()                          SetupAddToSourceListA
-# 52 stdcall SetupAddToSourceListW()                          SetupAddToSourceListW
-# 53 stdcall SetupAdjustDiskSpaceListA()                      SetupAdjustDiskSpaceListA
-# 54 stdcall SetupAdjustDiskSpaceListW()                      SetupAdjustDiskSpaceListW
-# 55 stdcall SetupCancelTemporarySourceList()                 SetupCancelTemporarySourceList
-56 stdcall SetupCloseFileQueue(ptr)                           SetupCloseFileQueue
-57 stdcall SetupCloseInfFile(long)                            SetupCloseInfFile
-# 58 stdcall SetupCommitFileQueue()                           SetupCommitFileQueue
-59 stdcall SetupCommitFileQueueA(ptr ptr ptr ptr)             SetupCommitFileQueueA
-# 60 stdcall SetupCommitFileQueueW()                          SetupCommitFileQueueW
-# 61 stdcall SetupCopyErrorA()                                SetupCopyErrorA
-# 62 stdcall SetupCopyErrorW()                                SetupCopyErrorW
-# 63 stdcall SetupCreateDiskSpaceListA()                      SetupCreateDiskSpaceListA
-# 64 stdcall SetupCreateDiskSpaceListW()                      SetupCreateDiskSpaceListW
-# 65 stdcall SetupDecompressOrCopyFileA()                     SetupDecompressOrCopyFileA
-# 66 stdcall SetupDecompressOrCopyFileW()                     SetupDecompressOrCopyFileW
-# 67 stdcall SetupDefaultQueueCallback()                      SetupDefaultQueueCallback
-68 stdcall SetupDefaultQueueCallbackA(ptr long long long)     SetupDefaultQueueCallbackA
-# 69 stdcall SetupDefaultQueueCallbackW()                     SetupDefaultQueueCallbackW
-# 70 stdcall SetupDeleteErrorA()                              SetupDeleteErrorA
-# 71 stdcall SetupDeleteErrorW()                              SetupDeleteErrorW
-# 72 stdcall SetupDestroyDiskSpaceList()                      SetupDestroyDiskSpaceList
-# 73 stdcall SetupDiAskForOEMDisk()                           SetupDiAskForOEMDisk
-# 74 stdcall SetupDiBuildClassInfoList()                      SetupDiBuildClassInfoList
-# 75 stdcall SetupDiBuildDriverInfoList()                     SetupDiBuildDriverInfoList
-# 76 stdcall SetupDiCallClassInstaller()                      SetupDiCallClassInstaller
-# 77 stdcall SetupDiCancelDriverInfoSearch()                  SetupDiCancelDriverInfoSearch
-# 78 stdcall SetupDiChangeState()                             SetupDiChangeState
-# 79 stdcall SetupDiClassGuidsFromNameA()                     SetupDiClassGuidsFromNameA
-# 80 stdcall SetupDiClassGuidsFromNameW()                     SetupDiClassGuidsFromNameW
-# 81 stdcall SetupDiClassNameFromGuidA()                      SetupDiClassNameFromGuidA
-# 82 stdcall SetupDiClassNameFromGuidW()                      SetupDiClassNameFromGuidW
-# 83 stdcall SetupDiCreateDevRegKeyA()                        SetupDiCreateDevRegKeyA
-# 84 stdcall SetupDiCreateDevRegKeyW()                        SetupDiCreateDevRegKeyW
-# 85 stdcall SetupDiCreateDeviceInfoA()                       SetupDiCreateDeviceInfoA
-# 86 stdcall SetupDiCreateDeviceInfoList()                    SetupDiCreateDeviceInfoList
-# 87 stdcall SetupDiCreateDeviceInfoW()                       SetupDiCreateDeviceInfoW
-# 88 stdcall SetupDiDeleteDevRegKey()                         SetupDiDeleteDevRegKey
-# 89 stdcall SetupDiDeleteDeviceInfo()                        SetupDiDeleteDeviceInfo
-# 90 stdcall SetupDiDestroyClassImageList()                   SetupDiDestroyClassImageList
-# 91 stdcall SetupDiDestroyDeviceInfoList()                   SetupDiDestroyDeviceInfoList
-# 92 stdcall SetupDiDestroyDriverInfoList()                   SetupDiDestroyDriverInfoList
-# 93 stdcall SetupDiDrawMiniIcon()                            SetupDiDrawMiniIcon
-# 94 stdcall SetupDiEnumDeviceInfo()                          SetupDiEnumDeviceInfo
-# 95 stdcall SetupDiEnumDriverInfoA()                         SetupDiEnumDriverInfoA
-# 96 stdcall SetupDiEnumDriverInfoW()                         SetupDiEnumDriverInfoW
-# 97 stdcall SetupDiGetActualSectionToInstallA()              SetupDiGetActualSectionToInstallA
-# 98 stdcall SetupDiGetActualSectionToInstallW()              SetupDiGetActualSectionToInstallW
-# 99 stdcall SetupDiGetClassBitmapIndex()                     SetupDiGetClassBitmapIndex
-#100 stdcall SetupDiGetClassDescriptionA()                    SetupDiGetClassDescriptionA
-#101 stdcall SetupDiGetClassDescriptionW()                    SetupDiGetClassDescriptionW
-#102 stdcall SetupDiGetClassDevPropertySheetsA()              SetupDiGetClassDevPropertySheetsA
-#103 stdcall SetupDiGetClassDevPropertySheetsW()              SetupDiGetClassDevPropertySheetsW
-#104 stdcall SetupDiGetClassDevsA()                           SetupDiGetClassDevsA
-#105 stdcall SetupDiGetClassDevsW()                           SetupDiGetClassDevsW
-#106 stdcall SetupDiGetClassImageIndex()                      SetupDiGetClassImageIndex
-#107 stdcall SetupDiGetClassImageList()                       SetupDiGetClassImageList
-#108 stdcall SetupDiGetClassInstallParamsA()                  SetupDiGetClassInstallParamsA
-#109 stdcall SetupDiGetClassInstallParamsW()                  SetupDiGetClassInstallParamsW
-#110 stdcall SetupDiGetDeviceInfoListClass()                  SetupDiGetDeviceInfoListClass
-#111 stdcall SetupDiGetDeviceInstallParamsA()                 SetupDiGetDeviceInstallParamsA
-#112 stdcall SetupDiGetDeviceInstallParamsW()                 SetupDiGetDeviceInstallParamsW
-#113 stdcall SetupDiGetDeviceInstanceIdA()                    SetupDiGetDeviceInstanceIdA
-#114 stdcall SetupDiGetDeviceInstanceIdW()                    SetupDiGetDeviceInstanceIdW
-#115 stdcall SetupDiGetDeviceRegistryPropertyA()              SetupDiGetDeviceRegistryPropertyA
-#116 stdcall SetupDiGetDeviceRegistryPropertyW()              SetupDiGetDeviceRegistryPropertyW
-#117 stdcall SetupDiGetDriverInfoDetailA()                    SetupDiGetDriverInfoDetailA
-#118 stdcall SetupDiGetDriverInfoDetailW()                    SetupDiGetDriverInfoDetailW
-#119 stdcall SetupDiGetDriverInstallParamsA()                 SetupDiGetDriverInstallParamsA
-#120 stdcall SetupDiGetDriverInstallParamsW()                 SetupDiGetDriverInstallParamsW
-#121 stdcall SetupDiGetHwProfileFriendlyNameA()               SetupDiGetHwProfileFriendlyNameA
-#122 stdcall SetupDiGetHwProfileFriendlyNameW()               SetupDiGetHwProfileFriendlyNameW
-#123 stdcall SetupDiGetHwProfileList()                        SetupDiGetHwProfileList
-#124 stdcall SetupDiGetINFClassA()                            SetupDiGetINFClassA
-#125 stdcall SetupDiGetINFClassW()                            SetupDiGetINFClassW
-#126 stdcall SetupDiGetSelectedDevice()                       SetupDiGetSelectedDevice
-#127 stdcall SetupDiGetSelectedDriverA()                      SetupDiGetSelectedDriverA
-#128 stdcall SetupDiGetSelectedDriverW()                      SetupDiGetSelectedDriverW
-#129 stdcall SetupDiGetWizardPage()                           SetupDiGetWizardPage
-#130 stdcall SetupDiInstallClassA()                           SetupDiInstallClassA
-#131 stdcall SetupDiInstallClassW()                           SetupDiInstallClassW
-#132 stdcall SetupDiInstallDevice()                           SetupDiInstallDevice
-#133 stdcall SetupDiInstallDriverFiles()                      SetupDiInstallDriverFiles
-#134 stdcall SetupDiLoadClassIcon()                           SetupDiLoadClassIcon
-#135 stdcall SetupDiMoveDuplicateDevice()                     SetupDiMoveDuplicateDevice
-#136 stdcall SetupDiOpenClassRegKey()                         SetupDiOpenClassRegKey
-#137 stdcall SetupDiOpenDevRegKey()                           SetupDiOpenDevRegKey
-#138 stdcall SetupDiOpenDeviceInfoA()                         SetupDiOpenDeviceInfoA
-#139 stdcall SetupDiOpenDeviceInfoW()                         SetupDiOpenDeviceInfoW
-#140 stdcall SetupDiRegisterDeviceInfo()                      SetupDiRegisterDeviceInfo
-#141 stdcall SetupDiRemoveDevice()                            SetupDiRemoveDevice
-#142 stdcall SetupDiSelectDevice()                            SetupDiSelectDevice
-#143 stdcall SetupDiSelectOEMDrv()                            SetupDiSelectOEMDrv
-#144 stdcall SetupDiSetClassInstallParamsA()                  SetupDiSetClassInstallParamsA
-#145 stdcall SetupDiSetClassInstallParamsW()                  SetupDiSetClassInstallParamsW
-#146 stdcall SetupDiSetDeviceInstallParamsA()                 SetupDiSetDeviceInstallParamsA
-#147 stdcall SetupDiSetDeviceInstallParamsW()                 SetupDiSetDeviceInstallParamsW
-#148 stdcall SetupDiSetDeviceRegistryPropertyA()              SetupDiSetDeviceRegistryPropertyA
-#149 stdcall SetupDiSetDeviceRegistryPropertyW()              SetupDiSetDeviceRegistryPropertyW
-#150 stdcall SetupDiSetDriverInstallParamsA()                 SetupDiSetDriverInstallParamsA
-#151 stdcall SetupDiSetDriverInstallParamsW()                 SetupDiSetDriverInstallParamsW
-#152 stdcall SetupDiSetSelectedDevice()                       SetupDiSetSelectedDevice
-#153 stdcall SetupDiSetSelectedDriverA()                      SetupDiSetSelectedDriverA
-#154 stdcall SetupDiSetSelectedDriverW()                      SetupDiSetSelectedDriverW
-#155 stdcall SetupDuplicateDiskSpaceListA()                   SetupDuplicateDiskSpaceListA
-#156 stdcall SetupDuplicateDiskSpaceListW()                   SetupDuplicateDiskSpaceListW
-157 stdcall SetupFindFirstLineA(long str str ptr)             SetupFindFirstLineA
-#158 stdcall SetupFindFirstLineW()                            SetupFindFirstLineW
-159 stdcall SetupFindNextLine(ptr ptr)                        SetupFindNextLine
-#160 stdcall SetupFindNextMatchLineA()                        SetupFindNextMatchLineA
-#161 stdcall SetupFindNextMatchLineW()                        SetupFindNextMatchLineW
-#162 stdcall SetupFreeSourceListA()                           SetupFreeSourceListA
-#163 stdcall SetupFreeSourceListW()                           SetupFreeSourceListW
-#164 stdcall SetupGetBinaryField()                            SetupGetBinaryField
-#165 stdcall SetupGetFieldCount()                             SetupGetFieldCount
-#166 stdcall SetupGetFileCompressionInfoA()                   SetupGetFileCompressionInfoA
-#167 stdcall SetupGetFileCompressionInfoW()                   SetupGetFileCompressionInfoW
-#168 stdcall SetupGetInfFileListA()                           SetupGetInfFileListA
-#169 stdcall SetupGetInfFileListW()                           SetupGetInfFileListW
-#170 stdcall SetupGetInfInformationA()                        SetupGetInfInformationA
-#171 stdcall SetupGetInfInformationW()                        SetupGetInfInformationW
-#172 stdcall SetupGetIntField()                               SetupGetIntField
-173 stdcall SetupGetLineByIndexA(ptr str long ptr)            SetupGetLineByIndexA
-#174 stdcall SetupGetLineByIndexW()                           SetupGetLineByIndexW
-#175 stdcall SetupGetLineCountA()                             SetupGetLineCountA
-#176 stdcall SetupGetLineCountW()                             SetupGetLineCountW
-177 stdcall SetupGetLineTextA(ptr long str str ptr long ptr)  SetupGetLineTextA
-#178 stdcall SetupGetLineTextW()                              SetupGetLineTextW
-#179 stdcall SetupGetMultiSzFieldA()                          SetupGetMultiSzFieldA
-#180 stdcall SetupGetMultiSzFieldW()                          SetupGetMultiSzFieldW
-#181 stdcall SetupGetSourceFileLocationA()                    SetupGetSourceFileLocationA
-#182 stdcall SetupGetSourceFileLocationW()                    SetupGetSourceFileLocationW
-#183 stdcall SetupGetSourceFileSizeA()                        SetupGetSourceFileSizeA
-#184 stdcall SetupGetSourceFileSizeW()                        SetupGetSourceFileSizeW
-#185 stdcall SetupGetSourceInfoA()                            SetupGetSourceInfoA
-#186 stdcall SetupGetSourceInfoW()                            SetupGetSourceInfoW
-187 stdcall SetupGetStringFieldA(ptr long str long ptr)       SetupGetStringFieldA
-#188 stdcall SetupGetStringFieldW()                           SetupGetStringFieldW
-#189 stdcall SetupGetTargetPathA()                            SetupGetTargetPathA
-#190 stdcall SetupGetTargetPathW()                            SetupGetTargetPathW
-191 stdcall SetupInitDefaultQueueCallback(long)               SetupInitDefaultQueueCallback
-192 stdcall SetupInitDefaultQueueCallbackEx(long long long long ptr) SetupInitDefaultQueueCallbackEx
-#193 stdcall SetupInitializeFileLogA()                        SetupInitializeFileLogA
-#194 stdcall SetupInitializeFileLogW()                        SetupInitializeFileLogW
-#195 stdcall SetupInstallFileA()                              SetupInstallFileA
-#196 stdcall SetupInstallFileExA()                            SetupInstallFileExA
-#197 stdcall SetupInstallFileExW()                            SetupInstallFileExW
-#198 stdcall SetupInstallFileW()                              SetupInstallFileW
-#199 stdcall SetupInstallFilesFromInfSectionA()               SetupInstallFilesFromInfSectionA
-#200 stdcall SetupInstallFilesFromInfSectionW()               SetupInstallFilesFromInfSectionW
-201 stdcall SetupInstallFromInfSectionA(long long str long long str long ptr ptr long ptr)    SetupInstallFromInfSectionA
-#202 stdcall SetupInstallFromInfSectionW()                    SetupInstallFromInfSectionW
-#203 stdcall SetupInstallServicesFromInfSectionA()            SetupInstallServicesFromInfSectionA
-#204 stdcall SetupInstallServicesFromInfSectionW()            SetupInstallServicesFromInfSectionW
-205 stdcall SetupIterateCabinetA(str long ptr ptr) SetupIterateCabinetA
-206 stdcall SetupIterateCabinetW(wstr long ptr ptr) SetupIterateCabinetW
-#207 stdcall SetupLogFileA()                                  SetupLogFileA
-#208 stdcall SetupLogFileW()                                  SetupLogFileW
-209 stdcall SetupOpenAppendInfFileA(str ptr ptr)              SetupOpenAppendInfFileA
-#210 stdcall SetupOpenAppendInfFileW()                        SetupOpenAppendInfFileW
-211 stdcall SetupOpenFileQueue()                              SetupOpenFileQueue
-212 stdcall SetupOpenInfFileA(str str long ptr)               SetupOpenInfFileA
-#213 stdcall SetupOpenInfFileW()                              SetupOpenInfFileW
-#214 stdcall SetupOpenMasterInf()                             SetupOpenMasterInf
-#215 stdcall SetupPromptForDiskA()                            SetupPromptForDiskA
-#216 stdcall SetupPromptForDiskW()                            SetupPromptForDiskW
-#217 stdcall SetupPromptReboot()                              SetupPromptReboot
-#218 stdcall SetupQueryDrivesInDiskSpaceListA()               SetupQueryDrivesInDiskSpaceListA
-#219 stdcall SetupQueryDrivesInDiskSpaceListW()               SetupQueryDrivesInDiskSpaceListW
-#220 stdcall SetupQueryFileLogA()                             SetupQueryFileLogA
-#221 stdcall SetupQueryFileLogW()                             SetupQueryFileLogW
-#222 stdcall SetupQueryInfFileInformationA()                  SetupQueryInfFileInformationA
-#223 stdcall SetupQueryInfFileInformationW()                  SetupQueryInfFileInformationW
-#224 stdcall SetupQueryInfVersionInformationA()               SetupQueryInfVersionInformationA
-#225 stdcall SetupQueryInfVersionInformationW()               SetupQueryInfVersionInformationW
-#226 stdcall SetupQuerySourceListA()                          SetupQuerySourceListA
-#227 stdcall SetupQuerySourceListW()                          SetupQuerySourceListW
-#228 stdcall SetupQuerySpaceRequiredOnDriveA()                SetupQuerySpaceRequiredOnDriveA
-#229 stdcall SetupQuerySpaceRequiredOnDriveW()                SetupQuerySpaceRequiredOnDriveW
-230 stdcall SetupQueueCopyA(ptr str str str str str str str long) SetupQueueCopyA
-#231 stdcall SetupQueueCopySectionA()                         SetupQueueCopySectionA
-#232 stdcall SetupQueueCopySectionW()                         SetupQueueCopySectionW
-#233 stdcall SetupQueueCopyW()                                SetupQueueCopyW
-#234 stdcall SetupQueueDefaultCopyA()                         SetupQueueDefaultCopyA
-#235 stdcall SetupQueueDefaultCopyW()                         SetupQueueDefaultCopyW
-#236 stdcall SetupQueueDeleteA()                              SetupQueueDeleteA
-#237 stdcall SetupQueueDeleteSectionA()                       SetupQueueDeleteSectionA
-#238 stdcall SetupQueueDeleteSectionW()                       SetupQueueDeleteSectionW
-#239 stdcall SetupQueueDeleteW()                              SetupQueueDeleteW
-#240 stdcall SetupQueueRenameA()                              SetupQueueRenameA
-#241 stdcall SetupQueueRenameSectionA()                       SetupQueueRenameSectionA
-#242 stdcall SetupQueueRenameSectionW()                       SetupQueueRenameSectionW
-#243 stdcall SetupQueueRenameW()                              SetupQueueRenameW
-#244 stdcall SetupRemoveFileLogEntryA()                       SetupRemoveFileLogEntryA
-#245 stdcall SetupRemoveFileLogEntryW()                       SetupRemoveFileLogEntryW
-#246 stdcall SetupRemoveFromDiskSpaceListA()                  SetupRemoveFromDiskSpaceListA
-#247 stdcall SetupRemoveFromDiskSpaceListW()                  SetupRemoveFromDiskSpaceListW
-#248 stdcall SetupRemoveFromSourceListA()                     SetupRemoveFromSourceListA
-#249 stdcall SetupRemoveFromSourceListW()                     SetupRemoveFromSourceListW
-#250 stdcall SetupRemoveInstallSectionFromDiskSpaceListA()    SetupRemoveInstallSectionFromDiskSpaceListA
-#251 stdcall SetupRemoveInstallSectionFromDiskSpaceListW()    SetupRemoveInstallSectionFromDiskSpaceListW
-#252 stdcall SetupRemoveSectionFromDiskSpaceListA()           SetupRemoveSectionFromDiskSpaceListA
-#253 stdcall SetupRemoveSectionFromDiskSpaceListW()           SetupRemoveSectionFromDiskSpaceListW
-#254 stdcall SetupRenameErrorA()                              SetupRenameErrorA
-#255 stdcall SetupRenameErrorW()                              SetupRenameErrorW
-#256 stdcall SetupScanFileQueue()                             SetupScanFileQueue
-#257 stdcall SetupScanFileQueueA()                            SetupScanFileQueueA
-#258 stdcall SetupScanFileQueueW()                            SetupScanFileQueueW
-259 stdcall SetupSetDirectoryIdA(long long str)               SetupSetDirectoryIdA
-#260 stdcall SetupSetDirectoryIdExA(long long str long long ptr)  SetupSetDirectoryIdExA
-#261 stdcall SetupSetDirectoryIdExW(long long wstr long long ptr) SetupSetDirectoryIdExW
-#262 stdcall SetupSetDirectoryIdW(long long wstr)             SetupSetDirectoryIdW
-#263 stdcall SetupSetPlatformPathOverrideA(str)               SetupSetPlatformPathOverrideA
-#264 stdcall SetupSetPlatformPathOverrideW(wstr)              SetupSetPlatformPathOverrideW
-#265 stdcall SetupSetSourceListA(long str long)               SetupSetSourceListA
-#266 stdcall SetupSetSourceListW(long wstr long)              SetupSetSourceListW
-267 stdcall SetupTermDefaultQueueCallback(ptr)               SetupTermDefaultQueueCallback
-#268 stdcall SetupTerminateFileLog(ptr)                       SetupTerminateFileLog
+@ stub AddMiniIconToList
+@ stub AddTagToGroupOrderListEntry
+@ stub AppendStringToMultiSz
+@ stub AssertFail
+@ stub CaptureAndConvertAnsiArg
+@ stub CaptureStringArg
+@ stub CenterWindowRelativeToParent
+@ stub ConcatenatePaths
+@ stub DelayedMove
+@ stub DelimStringToMultiSz
+@ stub DestroyTextFileReadBuffer
+@ stub DoesUserHavePrivilege
+@ stub DuplicateString
+@ stub EnablePrivilege
+@ stub ExtensionPropSheetPageProc
+@ stub FileExists
+@ stub FreeStringArray
+@ stub GetNewInfName
+@ stub GetSetFileTimestamp
+@ stub GetVersionInfoFromImage
+@ stub InfIsFromOemLocation
+@ stub InstallHinfSection
+@ stub InstallHinfSectionA
+@ stub InstallHinfSectionW
+@ stub InstallStop
+@ stub IsUserAdmin
+@ stub LookUpStringInTable
+@ stub MemoryInitialize
+@ stub MultiByteToUnicode
+@ stub MultiSzFromSearchControl
+@ stub MyFree
+@ stub MyGetFileTitle
+@ stub MyMalloc
+@ stub MyRealloc
+@ stub OpenAndMapFileForRead
+@ stub OutOfMemory
+@ stub QueryMultiSzValueToArray
+@ stub QueryRegistryValue
+@ stub ReadAsciiOrUnicodeTextFile
+@ stub RegistryDelnode
+@ stub RetreiveFileSecurity
+@ stub RetrieveServiceConfig
+@ stub SearchForInfFile
+@ stub SetArrayToMultiSzValue
+@ stub SetupAddInstallSectionToDiskSpaceListA
+@ stub SetupAddInstallSectionToDiskSpaceListW
+@ stub SetupAddSectionToDiskSpaceListA
+@ stub SetupAddSectionToDiskSpaceListW
+@ stub SetupAddToDiskSpaceListA
+@ stub SetupAddToDiskSpaceListW
+@ stub SetupAddToSourceListA
+@ stub SetupAddToSourceListW
+@ stub SetupAdjustDiskSpaceListA
+@ stub SetupAdjustDiskSpaceListW
+@ stub SetupCancelTemporarySourceList
+@ stdcall SetupCloseFileQueue(ptr) SetupCloseFileQueue
+@ stdcall SetupCloseInfFile(long) SetupCloseInfFile
+@ stub SetupCommitFileQueue
+@ stdcall SetupCommitFileQueueA(long long ptr ptr) SetupCommitFileQueueA
+@ stdcall SetupCommitFileQueueW(long long ptr ptr) SetupCommitFileQueueW
+@ stub SetupCopyErrorA
+@ stub SetupCopyErrorW
+@ stub SetupCreateDiskSpaceListA
+@ stub SetupCreateDiskSpaceListW
+@ stub SetupDecompressOrCopyFileA
+@ stub SetupDecompressOrCopyFileW
+@ stub SetupDefaultQueueCallback
+@ stdcall SetupDefaultQueueCallbackA(ptr long long long) SetupDefaultQueueCallbackA
+@ stdcall SetupDefaultQueueCallbackW(ptr long long long) SetupDefaultQueueCallbackW
+@ stub SetupDeleteErrorA
+@ stub SetupDeleteErrorW
+@ stub SetupDestroyDiskSpaceList
+@ stub SetupDiAskForOEMDisk
+@ stub SetupDiBuildClassInfoList
+@ stub SetupDiBuildDriverInfoList
+@ stub SetupDiCallClassInstaller
+@ stub SetupDiCancelDriverInfoSearch
+@ stub SetupDiChangeState
+@ stub SetupDiClassGuidsFromNameA
+@ stub SetupDiClassGuidsFromNameW
+@ stub SetupDiClassNameFromGuidA
+@ stub SetupDiClassNameFromGuidW
+@ stub SetupDiCreateDevRegKeyA
+@ stub SetupDiCreateDevRegKeyW
+@ stub SetupDiCreateDeviceInfoA
+@ stub SetupDiCreateDeviceInfoList
+@ stub SetupDiCreateDeviceInfoW
+@ stub SetupDiDeleteDevRegKey
+@ stub SetupDiDeleteDeviceInfo
+@ stub SetupDiDestroyClassImageList
+@ stub SetupDiDestroyDeviceInfoList
+@ stub SetupDiDestroyDriverInfoList
+@ stub SetupDiDrawMiniIcon
+@ stub SetupDiEnumDeviceInfo
+@ stub SetupDiEnumDriverInfoA
+@ stub SetupDiEnumDriverInfoW
+@ stub SetupDiGetActualSectionToInstallA
+@ stub SetupDiGetActualSectionToInstallW
+@ stub SetupDiGetClassBitmapIndex
+@ stub SetupDiGetClassDescriptionA
+@ stub SetupDiGetClassDescriptionW
+@ stub SetupDiGetClassDevPropertySheetsA
+@ stub SetupDiGetClassDevPropertySheetsW
+@ stub SetupDiGetClassDevsA
+@ stub SetupDiGetClassDevsW
+@ stub SetupDiGetClassImageIndex
+@ stub SetupDiGetClassImageList
+@ stub SetupDiGetClassInstallParamsA
+@ stub SetupDiGetClassInstallParamsW
+@ stub SetupDiGetDeviceInfoListClass
+@ stub SetupDiGetDeviceInstallParamsA
+@ stub SetupDiGetDeviceInstallParamsW
+@ stub SetupDiGetDeviceInstanceIdA
+@ stub SetupDiGetDeviceInstanceIdW
+@ stub SetupDiGetDeviceRegistryPropertyA
+@ stub SetupDiGetDeviceRegistryPropertyW
+@ stub SetupDiGetDriverInfoDetailA
+@ stub SetupDiGetDriverInfoDetailW
+@ stub SetupDiGetDriverInstallParamsA
+@ stub SetupDiGetDriverInstallParamsW
+@ stub SetupDiGetHwProfileFriendlyNameA
+@ stub SetupDiGetHwProfileFriendlyNameW
+@ stub SetupDiGetHwProfileList
+@ stub SetupDiGetINFClassA
+@ stub SetupDiGetINFClassW
+@ stub SetupDiGetSelectedDevice
+@ stub SetupDiGetSelectedDriverA
+@ stub SetupDiGetSelectedDriverW
+@ stub SetupDiGetWizardPage
+@ stub SetupDiInstallClassA
+@ stub SetupDiInstallClassW
+@ stub SetupDiInstallDevice
+@ stub SetupDiInstallDriverFiles
+@ stub SetupDiLoadClassIcon
+@ stub SetupDiMoveDuplicateDevice
+@ stub SetupDiOpenClassRegKey
+@ stub SetupDiOpenDevRegKey
+@ stub SetupDiOpenDeviceInfoA
+@ stub SetupDiOpenDeviceInfoW
+@ stub SetupDiRegisterDeviceInfo
+@ stub SetupDiRemoveDevice
+@ stub SetupDiSelectDevice
+@ stub SetupDiSelectOEMDrv
+@ stub SetupDiSetClassInstallParamsA
+@ stub SetupDiSetClassInstallParamsW
+@ stub SetupDiSetDeviceInstallParamsA
+@ stub SetupDiSetDeviceInstallParamsW
+@ stub SetupDiSetDeviceRegistryPropertyA
+@ stub SetupDiSetDeviceRegistryPropertyW
+@ stub SetupDiSetDriverInstallParamsA
+@ stub SetupDiSetDriverInstallParamsW
+@ stub SetupDiSetSelectedDevice
+@ stub SetupDiSetSelectedDriverA
+@ stub SetupDiSetSelectedDriverW
+@ stub SetupDuplicateDiskSpaceListA
+@ stub SetupDuplicateDiskSpaceListW
+@ stdcall SetupFindFirstLineA(long str str ptr) SetupFindFirstLineA
+@ stdcall SetupFindFirstLineW(long wstr wstr ptr) SetupFindFirstLineW
+@ stdcall SetupFindNextLine(ptr ptr) SetupFindNextLine
+@ stdcall SetupFindNextMatchLineA(ptr str ptr) SetupFindNextMatchLineA
+@ stdcall SetupFindNextMatchLineW(ptr wstr ptr) SetupFindNextMatchLineW
+@ stub SetupFreeSourceListA
+@ stub SetupFreeSourceListW
+@ stdcall SetupGetBinaryField(ptr long ptr long ptr) SetupGetBinaryField
+@ stdcall SetupGetFieldCount(ptr) SetupGetFieldCount
+@ stub SetupGetFileCompressionInfoA
+@ stub SetupGetFileCompressionInfoW
+@ stdcall SetupGetFileQueueCount(long long ptr) SetupGetFileQueueCount
+@ stdcall SetupGetFileQueueFlags(long ptr) SetupGetFileQueueFlags
+@ stub SetupGetInfFileListA
+@ stub SetupGetInfFileListW
+@ stub SetupGetInfInformationA
+@ stub SetupGetInfInformationW
+@ stdcall SetupGetIntField(ptr long ptr) SetupGetIntField
+@ stdcall SetupGetLineByIndexA(long str long ptr) SetupGetLineByIndexA
+@ stdcall SetupGetLineByIndexW(long wstr long ptr) SetupGetLineByIndexW
+@ stdcall SetupGetLineCountA(long str) SetupGetLineCountA
+@ stdcall SetupGetLineCountW(long wstr) SetupGetLineCountW
+@ stdcall SetupGetLineTextA(ptr long str str ptr long ptr) SetupGetLineTextA
+@ stdcall SetupGetLineTextW(ptr long wstr wstr ptr long ptr) SetupGetLineTextW
+@ stdcall SetupGetMultiSzFieldA(ptr long ptr long ptr) SetupGetMultiSzFieldA
+@ stdcall SetupGetMultiSzFieldW(ptr long ptr long ptr) SetupGetMultiSzFieldW
+@ stub SetupGetSourceFileLocationA
+@ stub SetupGetSourceFileLocationW
+@ stub SetupGetSourceFileSizeA
+@ stub SetupGetSourceFileSizeW
+@ stub SetupGetSourceInfoA
+@ stub SetupGetSourceInfoW
+@ stdcall SetupGetStringFieldA(ptr long ptr long ptr) SetupGetStringFieldA
+@ stdcall SetupGetStringFieldW(ptr long ptr long ptr) SetupGetStringFieldW
+@ stub SetupGetTargetPathA
+@ stub SetupGetTargetPathW
+@ stdcall SetupInitDefaultQueueCallback(long) SetupInitDefaultQueueCallback
+@ stdcall SetupInitDefaultQueueCallbackEx(long long long long ptr) SetupInitDefaultQueueCallbackEx
+@ stub SetupInitializeFileLogA
+@ stub SetupInitializeFileLogW
+@ stub SetupInstallFileA
+@ stub SetupInstallFileExA
+@ stub SetupInstallFileExW
+@ stub SetupInstallFileW
+@ stdcall SetupInstallFilesFromInfSectionA(long long long str str long) SetupInstallFilesFromInfSectionA
+@ stdcall SetupInstallFilesFromInfSectionW(long long long wstr wstr long) SetupInstallFilesFromInfSectionW
+@ stdcall SetupInstallFromInfSectionA(long long str long long str long ptr ptr long ptr) SetupInstallFromInfSectionA
+@ stdcall SetupInstallFromInfSectionW(long long wstr long long wstr long ptr ptr long ptr) SetupInstallFromInfSectionW
+@ stub SetupInstallServicesFromInfSectionA
+@ stub SetupInstallServicesFromInfSectionW
+@ stdcall SetupIterateCabinetA(str long ptr ptr) SetupIterateCabinetA
+@ stdcall SetupIterateCabinetW(wstr long ptr ptr) SetupIterateCabinetW
+@ stub SetupLogFileA
+@ stub SetupLogFileW
+@ stdcall SetupOpenAppendInfFileA(str long ptr) SetupOpenAppendInfFileA
+@ stdcall SetupOpenAppendInfFileW(wstr long ptr) SetupOpenAppendInfFileW
+@ stdcall SetupOpenFileQueue() SetupOpenFileQueue
+@ stdcall SetupOpenInfFileA(str str long ptr) SetupOpenInfFileA
+@ stdcall SetupOpenInfFileW(wstr wstr long ptr) SetupOpenInfFileW
+@ stub SetupOpenMasterInf
+@ stub SetupPromptForDiskA
+@ stub SetupPromptForDiskW
+@ stub SetupPromptReboot
+@ stub SetupQueryDrivesInDiskSpaceListA
+@ stub SetupQueryDrivesInDiskSpaceListW
+@ stub SetupQueryFileLogA
+@ stub SetupQueryFileLogW
+@ stub SetupQueryInfFileInformationA
+@ stub SetupQueryInfFileInformationW
+@ stub SetupQueryInfVersionInformationA
+@ stub SetupQueryInfVersionInformationW
+@ stub SetupQuerySourceListA
+@ stub SetupQuerySourceListW
+@ stub SetupQuerySpaceRequiredOnDriveA
+@ stub SetupQuerySpaceRequiredOnDriveW
+@ stdcall SetupQueueCopyA(long str str str str str str str long) SetupQueueCopyA
+@ stdcall SetupQueueCopyIndirectA(ptr) SetupQueueCopyIndirectA
+@ stdcall SetupQueueCopyIndirectW(ptr) SetupQueueCopyIndirectW
+@ stdcall SetupQueueCopySectionA(long str long long str long) SetupQueueCopySectionA
+@ stdcall SetupQueueCopySectionW(long wstr long long wstr long) SetupQueueCopySectionW
+@ stdcall SetupQueueCopyW(long wstr wstr wstr wstr wstr wstr wstr long) SetupQueueCopyW
+@ stdcall SetupQueueDefaultCopyA(long long str str long) SetupQueueDefaultCopyA
+@ stdcall SetupQueueDefaultCopyW(long long wstr wstr long) SetupQueueDefaultCopyW
+@ stdcall SetupQueueDeleteA(long str str) SetupQueueDeleteA
+@ stdcall SetupQueueDeleteSectionA(long long long str) SetupQueueDeleteSectionA
+@ stdcall SetupQueueDeleteSectionW(long long long wstr) SetupQueueDeleteSectionW
+@ stdcall SetupQueueDeleteW(long wstr wstr) SetupQueueDeleteW
+@ stdcall SetupQueueRenameA(long str str str str) SetupQueueRenameA
+@ stdcall SetupQueueRenameSectionA(long long long str) SetupQueueRenameSectionA
+@ stdcall SetupQueueRenameSectionW(long long long wstr) SetupQueueRenameSectionW
+@ stdcall SetupQueueRenameW(long wstr wstr wstr wstr) SetupQueueRenameW
+@ stub SetupRemoveFileLogEntryA
+@ stub SetupRemoveFileLogEntryW
+@ stub SetupRemoveFromDiskSpaceListA
+@ stub SetupRemoveFromDiskSpaceListW
+@ stub SetupRemoveFromSourceListA
+@ stub SetupRemoveFromSourceListW
+@ stub SetupRemoveInstallSectionFromDiskSpaceListA
+@ stub SetupRemoveInstallSectionFromDiskSpaceListW
+@ stub SetupRemoveSectionFromDiskSpaceListA
+@ stub SetupRemoveSectionFromDiskSpaceListW
+@ stub SetupRenameErrorA
+@ stub SetupRenameErrorW
+@ stub SetupScanFileQueue
+@ stdcall SetupScanFileQueueA(long long long ptr ptr ptr) SetupScanFileQueueA
+@ stdcall SetupScanFileQueueW(long long long ptr ptr ptr) SetupScanFileQueueW
+@ stdcall SetupSetDirectoryIdA(long long str) SetupSetDirectoryIdA
+@ stub SetupSetDirectoryIdExA
+@ stub SetupSetDirectoryIdExW
+@ stdcall SetupSetDirectoryIdW(long long wstr) SetupSetDirectoryIdW
+@ stdcall SetupSetFileQueueFlags(long long long) SetupSetFileQueueFlags
+@ stub SetupSetPlatformPathOverrideA
+@ stub SetupSetPlatformPathOverrideW
+@ stub SetupSetSourceListA
+@ stub SetupSetSourceListW
+@ stdcall SetupTermDefaultQueueCallback(ptr) SetupTermDefaultQueueCallback
+@ stub SetupTerminateFileLog
+@ stub ShouldDeviceBeExcluded
+@ stub StampFileSecurity
+@ stub StringTableAddString
+@ stub StringTableAddStringEx
+@ stub StringTableDestroy
+@ stub StringTableDuplicate
+@ stub StringTableEnum
+@ stub StringTableGetExtraData
+@ stub StringTableInitialize
+@ stub StringTableInitializeEx
+@ stub StringTableLookUpString
+@ stub StringTableLookUpStringEx
+@ stub StringTableSetExtraData
+@ stub StringTableStringFromId
+@ stub StringTableTrim
+@ stub TakeOwnershipOfFile
+@ stub UnicodeToMultiByte
+@ stub UnmapAndCloseFile
+@ stub pSetupDirectoryIdToPath
+@ stub pSetupGetField
+@ stub pSetupGetOsLoaderDriveAndPath
+@ stub pSetupGetVersionDatum
+@ stub pSetupGuidFromString
+@ stub pSetupIsGuidNull
+@ stub pSetupMakeSurePathExists
+@ stub pSetupStringFromGuid
diff --git a/dlls/setupapi/setupapi_private.h b/dlls/setupapi/setupapi_private.h
index 8a9b709..73018aa 100644
--- a/dlls/setupapi/setupapi_private.h
+++ b/dlls/setupapi/setupapi_private.h
@@ -20,6 +20,7 @@
 #define __SETUPAPI_PRIVATE_H
 
 #include "wine/windef16.h"
+#include "setupx16.h"
 
 #define COPYFILEDLGORD	1000
 #define SOURCESTRORD	500
@@ -54,4 +55,24 @@
 
 extern LPCSTR IP_GetFileName(HINF16 hInf);
 
+/* string substitutions */
+
+struct inf_file;
+extern const WCHAR *DIRID_get_string( HINF hinf, int dirid );
+extern unsigned int PARSER_string_substA( struct inf_file *file, const WCHAR *text,
+                                          char *buffer, unsigned int size );
+extern unsigned int PARSER_string_substW( struct inf_file *file, const WCHAR *text,
+                                          WCHAR *buffer, unsigned int size );
+extern const WCHAR *PARSER_get_src_root( HINF hinf );
+
+/* support for Ascii queue callback functions */
+
+struct callback_WtoA_context
+{
+    void               *orig_context;
+    PSP_FILE_CALLBACK_A orig_handler;
+};
+
+UINT CALLBACK QUEUE_callback_WtoA( void *context, UINT notification, UINT_PTR, UINT_PTR );
+
 #endif /* __SETUPAPI_PRIVATE_H */
diff --git a/dlls/setupapi/setupx.spec b/dlls/setupapi/setupx.spec
index 05d9536..19206b1 100644
--- a/dlls/setupapi/setupx.spec
+++ b/dlls/setupapi/setupx.spec
@@ -59,8 +59,8 @@
 55   stub     SURegLoadKey #(word str str)
 56   stub     SURegUnLoadKey #(word str)
 60   stub     DiskInfoFromLdid #(word ptr)
-61   stub     suErrorToIds #(word word)
-62   stub     TPWriteProfileString #(str str str)
+61   pascal   suErrorToIds(word word) suErrorToIds16
+62   pascal16 TPWriteProfileString(str str str) TPWriteProfileString16
 63   stub     SURPLSETUP
 # does SUSTORELDIDPATH set the path of an LDID in the registry ?
 64   stub     SUSTORELDIDPATH
diff --git a/dlls/setupapi/setupx_main.c b/dlls/setupapi/setupx_main.c
index 0964aec..59b27d3 100644
--- a/dlls/setupapi/setupx_main.c
+++ b/dlls/setupapi/setupx_main.c
@@ -59,7 +59,9 @@
 #include <stdio.h>
 #include <string.h>
 #include "winreg.h"
+#include "winerror.h"
 #include "wine/winuser16.h"
+#include "setupapi.h"
 #include "setupx16.h"
 #include "setupapi_private.h"
 #include "winerror.h"
@@ -154,139 +156,6 @@
     HeapFree(GetProcessHeap(), 0, substr);
 }
 
-static void SETUPX_IsolateSubString(LPSTR *begin, LPSTR *end)
-{
-    LPSTR p, q;
-
-    p = *begin;
-    q = *end;
-
-    while ((p < q) && ((*p == ' ') || (*p == '\t'))) p++;
-    while ((p < q) && (*p == '"')) p++;
-
-    while ((q-1 >= p) && ((*(q-1) == ' ') || (*(q-1) == '\t'))) q--;
-    while ((q-1 >= p) && (*(q-1) == '"')) q--;
-
-    *begin = p;
-    *end = q;
-}
-
-/*
- * Example: HKLM,"Software\Microsoft\Windows\CurrentVersion","ProgramFilesDir",,"C:\"
- * FIXME: use SETUPX_GetSubStrings() instead.
- *        Hmm, but on the other hand SETUPX_GetSubStrings() will probably
- *        soon be replaced by InitSubstrData() etc. anyway.
- *
- */
-static BOOL SETUPX_LookupRegistryString(LPSTR regstr, LPSTR buffer, DWORD buflen)
-{
-    HANDLE heap = GetProcessHeap();
-    LPSTR items[5];
-    LPSTR p, q, next;
-    int len, n;
-    HKEY hkey, hsubkey;
-    DWORD dwType;
-
-    TRACE("retrieving '%s'\n", regstr);
-
-    p = regstr;
-
-    /* isolate root key, subkey, value, flag, defval */
-    for (n=0; n < 5; n++)
-    {
-	q = strchr(p, ',');
-	if (!q)
-	{
-	    if (n == 4)
-		q = p+strlen(p);
-	    else
-		return FALSE;
-	}
-	next = q+1;
-	if (q < regstr)
-            return FALSE;
-        SETUPX_IsolateSubString(&p, &q);
-        len = (int)q - (int)p;
-        items[n] = HeapAlloc(heap, 0, len+1);
-        strncpy(items[n], p, len);
-        items[n][len] = '\0';
-	p = next;
-    }
-    TRACE("got '%s','%s','%s','%s','%s'\n",
-			items[0], items[1], items[2], items[3], items[4]);
-
-    /* check root key */
-    if (!strcasecmp(items[0], "HKCR"))
-	hkey = HKEY_CLASSES_ROOT;
-    else
-    if (!strcasecmp(items[0], "HKCU"))
-	hkey = HKEY_CURRENT_USER;
-    else
-    if (!strcasecmp(items[0], "HKLM"))
-	hkey = HKEY_LOCAL_MACHINE;
-    else
-    if (!strcasecmp(items[0], "HKU"))
-	hkey = HKEY_USERS;
-    else
-    { /* HKR ? -> relative to key passed to GenInstallEx */
-	FIXME("unsupported regkey '%s'\n", items[0]);
-        goto regfailed;
-    }
-
-    if (RegOpenKeyA(hkey, items[1], &hsubkey) != ERROR_SUCCESS)
-        goto regfailed;
-
-    if (RegQueryValueExA(hsubkey, items[2], NULL, &dwType, buffer, &buflen)
-		!= ERROR_SUCCESS)
-        goto regfailed;
-    goto done;
-
-regfailed:
-    if (buffer) strcpy(buffer, items[4]); /* I don't care about buflen */
-done:
-    for (n=0; n < 5; n++)
-        HeapFree(heap, 0, items[n]);
-    if (buffer)
-	TRACE("return '%s'\n", buffer);
-    return TRUE;
-}
-
-static LPSTR SETUPX_GetSections(LPCSTR filename)
-{
-    LPSTR buf = NULL;
-    DWORD len = 1024, res;
-
-    do {
-	buf = HeapReAlloc(GetProcessHeap(), 0, buf, len);
-	res = GetPrivateProfileStringA(NULL, NULL, NULL, buf, len, filename);
-	len *= 2;
-    } while ((!res) && (len < 1048576));
-    if (!res)
-    {
-	HeapFree(GetProcessHeap(), 0, buf);
-	return NULL;
-    }
-    return buf;
-}
-
-static LPSTR SETUPX_GetSectionEntries(LPCSTR filename, LPCSTR section)
-{
-    LPSTR buf = NULL;
-    DWORD len = 1024, res;
-
-    do {
-	buf = HeapReAlloc(GetProcessHeap(), 0, buf, len);
-	res = GetPrivateProfileSectionA(section, buf, len, filename);
-	len *= 2;
-    } while ((!res) && (len < 1048576));
-    if (!res)
-    {
-	HeapFree(GetProcessHeap(), 0, buf);
-	return NULL;
-    }
-    return buf;
-}
-
 
 /***********************************************************************
  *		InstallHinfSection (SETUPX.527)
@@ -707,7 +576,7 @@
 	 pPrev = pCurr;
 	 pCurr = pCurr->next;
     }
-    if (pCurr == NULL) /* hit end of list */
+    if (!pCurr || pldd->ldid != pCurr->pldd->ldid)
     {
 	is_new = TRUE;
         pCurr = HeapAlloc(heap, 0, sizeof(LDD_LIST));
@@ -850,400 +719,8 @@
     LOGDISKDESC_S ldd;
     TRACE("(%d, '%s');\n", ldid, szPath);
 
+    SetupSetDirectoryIdA( 0, ldid, szPath );
     INIT_LDD(ldd, ldid);
     ldd.pszPath = szPath;
     return CtlSetLdd16(&ldd);
 }
-
-/*
- * Find the value of a custom LDID in a .inf file
- * e.g. for 49301:
- * 49300,49301=ProgramFilesDir,5
- * -- profile section lookup -->
- * [ProgramFilesDir]
- * HKLM,"Software\Microsoft\Windows\CurrentVersion","ProgramFilesDir",,"%24%"
- * -- GenFormStrWithoutPlaceHolders16 -->
- * HKLM,"Software\Microsoft\Windows\CurrentVersion","ProgramFilesDir",,"C:\"
- * -- registry lookup -->
- * C:\Program Files (or C:\ if not found in registry)
- *
- * FIXME:
- * - maybe we ought to add a caching array for speed ? - I don't care :)
- * - not sure whether the processing is correct - sometimes there are equal
- *   LDIDs for both install and removal sections.
- * - probably the whole function can be removed as installers add that on their
- *   own
- */
-static BOOL SETUPX_AddCustomLDID(int ldid, INT16 hInf)
-{
-    char ldidstr[6];
-    LPSTR sectionbuf = NULL, entrybuf = NULL, regsectionbuf = NULL;
-    LPCSTR filename;
-    LPSTR pSec, pEnt, pEqual, p, *pSub = NULL;
-    BOOL ret = FALSE;
-    char buffer[MAX_PATH];
-    LOGDISKDESC_S ldd;
-
-    sprintf(ldidstr, "%d", ldid);
-    filename = IP_GetFileName(hInf);
-    if (!(sectionbuf = SETUPX_GetSections(filename)))
-    {
-	ERR("couldn't get sections !\n");
-	return FALSE;
-    }
-    for (pSec=sectionbuf; *pSec; pSec += strlen(pSec)+1)
-    {
-	if (!(entrybuf = SETUPX_GetSectionEntries(filename, pSec)))
-	{
-	    ERR("couldn't get section entries !\n");
-	    goto end;
-	}
-	for (pEnt=entrybuf; *pEnt; pEnt += strlen(pEnt)+1)
-	{
-	    if (strstr(pEnt, ldidstr))
-	    {
-		pEqual = strchr(pEnt, '=');
-		if (!pEqual) /* crippled entry ?? */
-		    continue;
-
-		/* make sure we found the LDID on left side of the equation */
-		if (pEnt+strlen(ldidstr) <= pEqual)
-		{ /* found */
-
-		    /* but we don't want entries in the strings section */
-		    if (!strcasecmp(pSec, "Strings")) continue;
-		    p = pEqual+1;
-		    goto found;
-		}
-	    }
-	}
-    }
-    goto end;
-found:
-    TRACE("found entry '%s'\n", p);
-    pSub = SETUPX_GetSubStrings(p, ',');
-    if (*(DWORD *)pSub > 2)
-    {
-	ERR("malformed entry '%s' ?\n", p);
-	goto end;
-    }
-    TRACE("found section '%s'\n", *(pSub+1));
-    /* FIXME: what are the optional flags at the end of an entry used for ?? */
-
-    /* get the location of the registry key from that section */
-    if (!(regsectionbuf = SETUPX_GetSectionEntries(filename, *(pSub+1))))
-    {
-	ERR("couldn't get registry section entries !\n");
-	goto end;
-    }
-    /* sectionbuf is > 1024 bytes anyway, so use it */
-    GenFormStrWithoutPlaceHolders16(sectionbuf, regsectionbuf, hInf);
-    ret = SETUPX_LookupRegistryString(sectionbuf, buffer, MAX_PATH);
-    TRACE("return '%s'\n", buffer);
-    INIT_LDD(ldd, ldid);
-    ldd.pszPath = buffer;
-    CtlSetLdd16(&ldd);
-end:
-    SETUPX_FreeSubStrings(pSub);
-    if (sectionbuf)	HeapFree(GetProcessHeap(), 0, sectionbuf);
-    if (entrybuf)	HeapFree(GetProcessHeap(), 0, entrybuf);
-    if (regsectionbuf)	HeapFree(GetProcessHeap(), 0, regsectionbuf);
-    return ret;
-}
-
-/*
- * Translate a logical disk identifier (LDID) into its string representation
- * I'm afraid this can be totally replaced by CtlGetLddPath().
- */
-static BOOL SETUPX_IP_TranslateLDID(int ldid, LPSTR *p, HINF16 hInf)
-{
-    BOOL handled = FALSE;
-    LOGDISKDESC_S ldd;
-
-    ldd.cbSize = sizeof(LOGDISKDESC_S);
-    ldd.ldid = ldid;
-    if (CtlFindLdd16(&ldd) == ERR_VCP_LDDFIND)
-    {
-	/* hmm, it seems the installers already do the work for us
-	 * (by calling CtlSetLddPath) that SETUPX_AddCustomLDID
-	 * is supposed to do. Grmbl ;-)
-	 * Well, I'll leave it here anyway, but print error... */
-	ERR("hmm, LDID %d not registered yet !?\n", ldid);
-	handled = SETUPX_AddCustomLDID(ldid, hInf);
-    }
-    else
-	handled = TRUE;
-
-    SETUPX_GetLdd(&ldd);
-
-    if (!handled)
-    {
-        FIXME("What is LDID %d ??\n", ldid);
-	*p = "LDID_FIXME";
-    }
-    else
-	*p = ldd.pszPath;
-
-    return handled;
-}
-
-/***********************************************************************
- *		GenFormStrWithoutPlaceHolders (SETUPX.103)
- *
- * ought to be pretty much implemented, I guess...
- */
-void WINAPI GenFormStrWithoutPlaceHolders16( LPSTR szDst, LPCSTR szSrc, HINF16 hInf)
-{
-    LPCSTR pSrc = szSrc, pSrcEnd = szSrc + strlen(szSrc);
-    LPSTR pDst = szDst, p, pPHBegin;
-    int count;
-
-    TRACE("(%p, '%s', %04x);\n", szDst, szSrc, hInf);
-    while (pSrc < pSrcEnd)
-    {
-	p = strchr(pSrc, '%');
-	if (p)
-	{
-	    count = (int)p - (int)pSrc;
-	    strncpy(pDst, pSrc, count);
-	    pSrc += count;
-	    pDst += count;
-	    pPHBegin = p+1;
-	    p = strchr(pPHBegin, '%');
-	    if (p)
-	    {
-		char placeholder[80]; /* that really ought to be enough ;) */
-		int ldid;
-		BOOL done = TRUE;
-		count = (int)p - (int)pPHBegin;
-		strncpy(placeholder, pPHBegin, count);
-		placeholder[count] = '\0';
-		ldid = atoi(placeholder);
-		if (ldid)
-		{
-		    LPSTR p;
-		    done = SETUPX_IP_TranslateLDID(ldid, &p, hInf);
-		    strcpy(pDst, p);
-		    if (done)
-			pDst += strlen(pDst);
-		}
-		else
-		{ /* hmm, string placeholder. Need to look up
-		     in the [strings] section of the hInf */
-		    DWORD ret;
-		    char buf[256]; /* long enough ? */
-
-		    ret = GetPrivateProfileStringA("strings", placeholder, "",
-		    			buf, 256, IP_GetFileName(hInf));
-		    if (ret)
-		    {
-			strcpy(pDst, buf);
-			pDst += strlen(buf);
-		    }
-		    else
-		    {
-		        ERR("placeholder string '%s' not found !\n", placeholder);
-			done = FALSE;
-		    }
-		}
-		if (!done)
-		{ /* copy raw placeholder string over */
-		    count = (int)p - (int)pPHBegin + 2;
-		    strncpy(pDst, pPHBegin-1, count);
-		    pDst += count;
-
-		}
-		pSrc = p+1;
-		continue;
-	    }
-	}
-
-	/* copy the remaining source string over */
-	strncpy(pDst, pSrc, (int)pSrcEnd - (int)pSrc + 1);
-	break;
-    }
-    TRACE("ret '%s'\n", szDst);
-}
-
-/*
- * Copy all items in a CopyFiles entry over to the destination
- *
- * - VNLP_xxx is what is given as flags for a .INF CopyFiles section
- */
-static BOOL SETUPX_CopyFiles(LPSTR *pSub, HINF16 hInf)
-{
-    BOOL bSingle = FALSE;
-    unsigned int n;
-    LPCSTR filename = IP_GetFileName(hInf);
-    LPSTR pCopyEntry;
-    char pDstStr[MAX_PATH];
-    LPSTR pSrcDir, pDstDir;
-    LPSTR pFileEntries, p;
-    WORD ldid;
-    LOGDISKDESC_S ldd;
-    LPSTR *pSubFile;
-    LPSTR pSrcFile, pDstFile;
-    WORD flag;
-
-    for (n=0; n < *(DWORD *)pSub; n++)
-    {
-	pCopyEntry = *(pSub+1+n);
-	if (*pCopyEntry == '@')
-	{
-	    pCopyEntry++;
-	    bSingle = TRUE;
-	}
-	else
-	    bSingle = FALSE;
-
-	/* get source directory for that entry */
-	INIT_LDD(ldd, LDID_SRCPATH);
-	SETUPX_GetLdd(&ldd);
-	pSrcDir = ldd.pszPath;
-
-        /* get destination directory for that entry */
-	if (!(GetPrivateProfileStringA("DestinationDirs", pCopyEntry, "",
-					pDstStr, sizeof(pDstStr), filename)))
-	{
-	    /* hmm, not found; try the default entry */
-	    if (!(GetPrivateProfileStringA("DestinationDirs", "DefaultDestDir", "", pDstStr, sizeof(pDstStr), filename)))
-	    {
-		WARN("DefaultDestDir not found.\n");
-	        continue;
-	    }
-	}
-
-	/* translate destination dir if given as LDID */
-	ldid = atoi(pDstStr);
-	if (ldid)
-	{
-	    if (!(SETUPX_IP_TranslateLDID(ldid, &pDstDir, hInf)))
-		continue;
-	}
-	else
-	    pDstDir = pDstStr;
-
-	/* now that we have the destination dir, register file copying */
-
-	if (bSingle)
-	{
-	    VcpQueueCopy16(pCopyEntry, pCopyEntry, pSrcDir, pDstDir, LDID_SRCPATH, ldid ? ldid : 0xffff, 0, VFNL_COPY, 0);
-	    return TRUE;
-	}
-
-	/* entry wasn't a single file, so let's iterate over section */
-	pFileEntries = SETUPX_GetSectionEntries(filename, pCopyEntry);
-        if (pFileEntries == NULL) continue;
-        for (p=pFileEntries; *p; p +=strlen(p)+1)
-	{
-	    pSubFile = SETUPX_GetSubStrings(p, ',');
-	    pSrcFile = *(pSubFile+1);
-	    pDstFile = (*(DWORD *)pSubFile > 1) ? *(pSubFile+2) : pSrcFile;
-	    TRACE("copying file '%s\\%s' to '%s\\%s'\n", pSrcDir, pSrcFile, pDstDir, pDstFile);
-	    flag = 0;
-	    if (*(DWORD *)pSubFile > 2)
-	    {
-		if ((flag = atoi(*(pSubFile+3)))) /* ah, flag */
-		{
-		    if (flag & 0x2c)
-		    FIXME("VNLP_xxx flag %d not handled yet.\n", flag);
-		}
-		else
-		{
-		    FIXME("temp file name '%s' given. Need to register in wininit.ini !\n", *(pSubFile+3));
-		    /* we probably need to set VIRTNODE.vhstrDstFinalName to
-		     * the final destination name, and the temp name is merely
-		     * the copy destination */
-		}
-	    }
-	    VcpQueueCopy16(pSrcFile, pDstFile, pSrcDir, pDstDir, LDID_SRCPATH, ldid ? ldid : 0xffff, 0, VFNL_COPY|flag, 0);
-	    SETUPX_FreeSubStrings(pSubFile);
-	}
-    }
-
-    return TRUE;
-}
-
-/***********************************************************************
- *		GenInstall (SETUPX.101)
- *
- * generic installer function for .INF file sections
- *
- * This is not perfect - patch whenever you can !
- *
- * wFlags == GENINSTALL_DO_xxx
- * e.g. NetMeeting:
- * first call GENINSTALL_DO_REGSRCPATH | GENINSTALL_DO_FILES,
- * second call GENINSTALL_DO_LOGCONFIG | CFGAUTO | INI2REG | REG | INI
- */
-RETERR16 WINAPI GenInstall16(HINF16 hInfFile, LPCSTR szInstallSection, WORD wFlags)
-{
-    LPCSTR filename = IP_GetFileName(hInfFile);
-    LPSTR pEntries, p, pEnd;
-    DWORD len;
-    LPSTR *pSub;
-
-    FIXME("(%04x, '%s', %04x), semi-stub. Please implement additional operations here !\n", hInfFile, szInstallSection, wFlags);
-    pEntries = SETUPX_GetSectionEntries(filename, szInstallSection);
-    if (!pEntries)
-    {
-	ERR("couldn't find entries for section '%s' !\n", szInstallSection);
-	return ERR_IP_SECT_NOT_FOUND;
-    }
-    for (p=pEntries; *p; p +=strlen(p)+1)
-    {
-	pEnd = strchr(p, '=');
-	if (!pEnd) continue;
-	pSub = SETUPX_GetSubStrings(pEnd+1, ','); /* split entries after the '=' */
-	SETUPX_IsolateSubString(&p, &pEnd);
-	len = (int)pEnd - (int)p;
-
-	if (wFlags & GENINSTALL_DO_FILES)
-	{
-	    if (!strncasecmp(p, "CopyFiles", len))
-	    {
-	        SETUPX_CopyFiles(pSub, hInfFile);
-		continue;
-	    }
-#if IMPLEMENT_THAT
-	    else
-	    if (!strncasecmp(p, "DelFiles", len))
-	    {
-	        SETUPX_DelFiles(filename, szInstallSection, pSub);
-		continue;
-	    }
-#endif
-	}
-	if (wFlags & GENINSTALL_DO_INI)
-	{
-#if IMPLEMENT_THAT
-	    if (!strncasecmp(p, "UpdateInis", len))
-	    {
-	        SETUPX_UpdateInis(filename, szInstallSection, pSub);
-		continue;
-	    }
-#endif
-	}
-	if (wFlags & GENINSTALL_DO_REG)
-	{
-#if IMPLEMENT_THAT
-	    /* probably use SUReg*() functions here */
-	    if (!strncasecmp(p, "AddReg", len))
-	    {
-	        SETUPX_AddReg(filename, szInstallSection, pSub);
-		continue;
-	    }
-	    else
-	    if (!strncasecmp(p, "DelReg", len))
-	    {
-	        SETUPX_DelReg(filename, szInstallSection, pSub);
-		continue;
-	    }
-#endif
-	}
-
-	SETUPX_FreeSubStrings(pSub);
-    }
-    HeapFree(GetProcessHeap(), 0, pEntries);
-    return OK;
-}
diff --git a/dlls/setupapi/stubs.c b/dlls/setupapi/stubs.c
index d4b7834..36fee7d 100644
--- a/dlls/setupapi/stubs.c
+++ b/dlls/setupapi/stubs.c
@@ -1,4 +1,3 @@
-/* -*- tab-width: 8; c-basic-offset: 8 -*- */
 /*
  * SetupAPI stubs
  *
@@ -27,25 +26,6 @@
 
 
 /***********************************************************************
- *		SetupCloseFileQueue (SETUPAPI.56)
- */
-VOID WINAPI SetupCloseFileQueue(HSPFILEQ QueueHandle)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-}
-
-/***********************************************************************
- *		SetupCommitFileQueueA (SETUPAPI.59)
- */
-BOOL WINAPI SetupCommitFileQueueA(HWND Owner, HSPFILEQ QueueHandle,
-				  PSP_FILE_CALLBACK_A MsgHandler,
-				  PVOID Context)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-	return FALSE;
-}
-
-/***********************************************************************
  *		SetupIterateCabinetA (SETUPAPI.205)
  */
 BOOL WINAPI SetupIterateCabinetA(PCSTR CabinetFile, DWORD Reserved,
@@ -67,171 +47,20 @@
 
 
 /***********************************************************************
- *		SetupGetLineTextA (SETUPAPI.177)
+ *		TPWriteProfileString16 (SETUPX.62)
  */
-BOOL WINAPI SetupGetLineTextA (PINFCONTEXT Context, HINF InfHandle,
-                        PCSTR Section, PCSTR Key, LPSTR ReturnBuffer,
-                        DWORD ReturnBufferSize, PDWORD RequiredSize)
+BOOL WINAPI TPWriteProfileString16( LPCSTR section, LPCSTR entry, LPCSTR string )
 {
-	FIXME("not implemented (setupapi.dll) \n");
-	return 0;
-}
-
-/***********************************************************************
- *		SetupGetStringFieldA (SETUPAPI.187)
- */
-BOOL WINAPI SetupGetStringFieldA(PINFCONTEXT Context, DWORD FieldIndex, 
-                                 LPSTR ReturnBuffer, DWORD ReturnBufferSize,
-                                 PDWORD RequiredSize)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-	return 0;
+    FIXME( "%s %s %s: stub\n", debugstr_a(section), debugstr_a(entry), debugstr_a(string) );
+    return TRUE;
 }
 
 
 /***********************************************************************
- *		SetupFindNextLine (SETUPAPI.159)
+ *		suErrorToIds16  (SETUPX.61)
  */
-BOOL WINAPI SetupFindNextLine (PINFCONTEXT ContextIn, PINFCONTEXT ContextOut)
+DWORD WINAPI suErrorToIds16( WORD w1, WORD w2 )
 {
-	FIXME("not implemented (setupapi.dll) \n");
-	return 0;
+    FIXME( "%x %x: stub\n", w1, w2 );
+    return 0;
 }
-
-
-/***********************************************************************
- *		SetupInitDefaultQueueCallback (SETUPAPI.191)
- */
-PVOID WINAPI SetupInitDefaultQueueCallback(HWND OwnerWindow)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-	return 0;
-}
-
-/***********************************************************************
- *		SetupInitDefaultQueueCallbackEx (SETUPAPI.192)
- */
-PVOID WINAPI SetupInitDefaultQueueCallbackEx(HWND OwnerWindow,
-					     HWND AlternativeProgressWindow,
-					     UINT ProgressMessage,
-					     DWORD res1,
-					     PVOID res2)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-	return 0;
-}
-
-/***********************************************************************
- *		SetupCloseInfFile (SETUPAPI.57)
- */
-VOID WINAPI SetupCloseInfFile (HINF InfHandle)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-}
-
-
-/***********************************************************************
- *		SetupDefaultQueueCallbackA (SETUPAPI.68)
- */
-UINT WINAPI SetupDefaultQueueCallbackA (PVOID Context, UINT Notification,
-                                        UINT Param1, UINT Param2)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-	return 0;
-}
-
-
-/***********************************************************************
- *		SetupFindFirstLineA (SETUPAPI.157)
- */
-BOOL WINAPI SetupFindFirstLineA (HINF InfHandle, PCSTR Section, PCSTR Key,
-                                 PINFCONTEXT Context)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-	return 0;
-}
-
-/***********************************************************************
- *		SetupGetLineByIndexA (SETUPAPI.173)
- */
-BOOL WINAPI SetupGetLineByIndexA (HINF InfHandle, PCSTR Section, DWORD Index,
-                                 PINFCONTEXT Context)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-	return FALSE;
-}
-
-
-/***********************************************************************
- *		SetupInstallFromInfSectionA (SETUPAPI.201)
- */
-BOOL WINAPI SetupInstallFromInfSectionA (HWND Owner, HINF InfHandle, PCSTR SectionName,
-                                         UINT Flags, HKEY RelativeKeyRoot, PCSTR SourceRootPath,
-                                         UINT CopyFlags, PSP_FILE_CALLBACK_A MsgHandler,
-                                         PVOID Context, HDEVINFO DeviceInfoSet,
-                                         PSP_DEVINFO_DATA DeviceInfoData)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-	return 0;
-}
-
-/***********************************************************************
- *		SetupOpenAppendInfFileA (SETUPAPI.209)
- */
-BOOL WINAPI SetupOpenAppendInfFileA (PCSTR FileName, HINF InfHandle,
-				    PUINT ErrorLine)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-	return FALSE;
-}
-
-/***********************************************************************
- *		SetupOpenFileQueue (SETUPAPI.211)
- */
-HSPFILEQ WINAPI SetupOpenFileQueue (VOID)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-	return (HSPFILEQ) INVALID_HANDLE_VALUE;
-}
-
-/***********************************************************************
- *		SetupOpenInfFileA (SETUPAPI.212)
- */
-HINF WINAPI SetupOpenInfFileA (PCSTR FileName, PCSTR InfClass, DWORD InfStyle,
-                               PUINT ErrorLine)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-	return 0;
-}
-
-/***********************************************************************
- *		SetupQueueCopyA (SETUPAPI.230)
- */
-BOOL WINAPI SetupQueueCopyA (HSPFILEQ QueueHandle, PCSTR SourceRootPath, PCSTR SourcePath,
-			     PCSTR SourceFileName, PCSTR SourceDescription, PCSTR SourceTagFile,
-			     PCSTR TargetDirectory, PCSTR TargetFileName, DWORD CopyStyle)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-	return FALSE;
-}
-
-/***********************************************************************
- *		SetupSetDirectoryIdA (SETUPAPI.259)
- */
-BOOL WINAPI SetupSetDirectoryIdA (HINF InfHandle,
-				  DWORD Id,
-				  PCSTR Directory)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-	return FALSE;
-}
-
-
-/***********************************************************************
- *		SetupTermDefaultQueueCallback (SETUPAPI.267)
- */
-VOID WINAPI SetupTermDefaultQueueCallback (PVOID Callback)
-{
-	FIXME("not implemented (setupapi.dll) \n");
-}
-
diff --git a/dlls/setupapi/virtcopy.c b/dlls/setupapi/virtcopy.c
index a32927e..4fbcc3e 100644
--- a/dlls/setupapi/virtcopy.c
+++ b/dlls/setupapi/virtcopy.c
@@ -24,8 +24,9 @@
 #include <string.h>
 #include "winbase.h"
 #include "winuser.h"
-#include "setupx16.h"
 #include "winreg.h"
+#include "setupapi.h"
+#include "setupx16.h"
 #include "setupapi_private.h"
 #include "wine/debug.h"
 
@@ -660,12 +661,15 @@
     if (!(VCP_UI_GetDialogTemplate(&template32)))
 	return VCPN_FAIL;
 
-    hDlgCopy = CreateDialogIndirectParamA(SETUPAPI_hInstance, template32, 0,
-		    				VCP_UI_FileCopyDlgProc, 0);
-    if (!hDlgCopy)
-	return VCPN_FAIL;
-    SetDlgItemTextA(hDlgCopy, SOURCESTRORD, "Scanning ...");
-    SetDlgItemTextA(hDlgCopy, DESTSTRORD, "NOT_IMPLEMENTED_YET");
+    if (vn_num > 10)  /* hack */
+    {
+        hDlgCopy = CreateDialogIndirectParamA(SETUPAPI_hInstance, template32, 0,
+                                              VCP_UI_FileCopyDlgProc, 0);
+        if (!hDlgCopy)
+            return VCPN_FAIL;
+        SetDlgItemTextA(hDlgCopy, SOURCESTRORD, "Scanning ...");
+        SetDlgItemTextA(hDlgCopy, DESTSTRORD, "NOT_IMPLEMENTED_YET");
+    }
     strcpy(buf, REG_INSTALLEDFILES);
     if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyFiles))
 	return VCPN_FAIL;
@@ -749,7 +753,7 @@
 	    res = VCP_UI_CopyStart();
 	    break;
 	case VCPM_VSTATCOPYEND:
-	    DestroyWindow(hDlgCopy);
+	    if (hDlgCopy) DestroyWindow(hDlgCopy);
 	    break;
 	default:
 	    FIXME("unhandled msg 0x%04x\n", uMsg);
diff --git a/include/setupapi.h b/include/setupapi.h
index 955610e..df3de22 100644
--- a/include/setupapi.h
+++ b/include/setupapi.h
@@ -16,8 +16,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#ifndef __SETUPAPI__
-#define __SETUPAPI__
+#ifndef _INC_SETUPAPI
+#define _INC_SETUPAPI
 
 #include "commctrl.h"
 
@@ -39,12 +39,86 @@
    UINT  Line;
 } INFCONTEXT, *PINFCONTEXT;
 
+typedef struct _SP_ALTPLATFORM_INFO_V2
+{
+    DWORD cbSize;
+    DWORD Platform;
+    DWORD MajorVersion;
+    DWORD MinorVersion;
+    WORD  ProcessorArchitecture;
+    union
+    {
+        WORD  Reserved;
+        WORD  Flags;
+    } DUMMYUNIONNAME;
+    DWORD FirstValidatedMajorVersion;
+    DWORD FirstValidatedMinorVersion;
+} SP_ALTPLATFORM_INFO_V2, *PSP_ALTPLATFORM_INFO_V2;
+
+#define SP_ALTPLATFORM_FLAGS_VERSION_RANGE 0x0001
+
+typedef struct _SP_ALTPLATFORM_INFO_V1
+{
+    DWORD cbSize;
+    DWORD Platform;
+    DWORD MajorVersion;
+    DWORD MinorVersion;
+    WORD  ProcessorArchitecture;
+    WORD  Reserved;
+} SP_ALTPLATFORM_INFO_V1, *PSP_ALTPLATFORM_INFO_V1;
+
+typedef SP_ALTPLATFORM_INFO_V2 SP_ALTPLATFORM_INFO;
+typedef PSP_ALTPLATFORM_INFO_V2 PSP_ALTPLATFORM_INFO;
+
+typedef struct _SP_FILE_COPY_PARAMS_A
+{
+    DWORD    cbSize;
+    HSPFILEQ QueueHandle;
+    PCSTR    SourceRootPath;
+    PCSTR    SourcePath;
+    PCSTR    SourceFilename;
+    PCSTR    SourceDescription;
+    PCSTR    SourceTagfile;
+    PCSTR    TargetDirectory;
+    PCSTR    TargetFilename;
+    DWORD    CopyStyle;
+    HINF     LayoutInf;
+    PCSTR    SecurityDescriptor;
+} SP_FILE_COPY_PARAMS_A, *PSP_FILE_COPY_PARAMS_A;
+
+typedef struct _SP_FILE_COPY_PARAMS_W
+{
+    DWORD    cbSize;
+    HSPFILEQ QueueHandle;
+    PCWSTR   SourceRootPath;
+    PCWSTR   SourcePath;
+    PCWSTR   SourceFilename;
+    PCWSTR   SourceDescription;
+    PCWSTR   SourceTagfile;
+    PCWSTR   TargetDirectory;
+    PCWSTR   TargetFilename;
+    DWORD    CopyStyle;
+    HINF     LayoutInf;
+    PCWSTR   SecurityDescriptor;
+} SP_FILE_COPY_PARAMS_W, *PSP_FILE_COPY_PARAMS_W;
+
+DECL_WINELIB_TYPE_AW(SP_FILE_COPY_PARAMS_)
+DECL_WINELIB_TYPE_AW(PSP_FILE_COPY_PARAMS_)
+
 typedef UINT (CALLBACK *PSP_FILE_CALLBACK_A)( PVOID Context, UINT Notification,
                                               UINT Param1, UINT Param2 );
 typedef UINT (CALLBACK *PSP_FILE_CALLBACK_W)( PVOID Context, UINT Notification,
                                               UINT Param1, UINT Param2 );
 #define PSP_FILE_CALLBACK WINELIB_NAME_AW(PSP_FILE_CALLBACK_)
 
+#define LINE_LEN                    256
+#define MAX_INF_STRING_LENGTH       4096
+#define MAX_TITLE_LEN               60
+#define MAX_INSTRUCTION_LEN         256
+#define MAX_LABEL_LEN               30
+#define MAX_SERVICE_NAME_LEN        256
+#define MAX_SUBTITLE_LEN            256
+#define SP_MAX_MACHINENAME_LENGTH   (MAX_PATH + 3)
 
 /* Device Information structure (references a device instance that is a member
    of a device information set) */
@@ -56,4 +130,392 @@
    DWORD Reserved;
 } SP_DEVINFO_DATA, *PSP_DEVINFO_DATA;
 
-#endif /* __SETUPAPI__ */
+#define INF_STYLE_NONE           0x00
+#define INF_STYLE_OLDNT          0x01
+#define INF_STYLE_WIN4           0x02
+#define INF_STYLE_CACHE_ENABLE   0x10
+#define INF_STYLE_CACHE_DISABLE  0x20
+
+#define FILEOP_COPY              0
+#define FILEOP_RENAME            1
+#define FILEOP_DELETE            2
+#define FILEOP_BACKUP            3
+
+#define FILEOP_ABORT             0
+#define FILEOP_DOIT              1
+#define FILEOP_SKIP              2
+#define FILEOP_RETRY             FILEOP_DOIT
+#define FILEOP_NEWPATH           4
+
+typedef struct _FILEPATHS_A
+{
+    PCSTR  Target;
+    PCSTR  Source;
+    UINT   Win32Error;
+    DWORD  Flags;
+} FILEPATHS_A, *PFILEPATHS_A;
+
+typedef struct _FILEPATHS_W
+{
+    PCWSTR Target;
+    PCWSTR Source;
+    UINT   Win32Error;
+    DWORD  Flags;
+} FILEPATHS_W, *PFILEPATHS_W;
+
+DECL_WINELIB_TYPE_AW(FILEPATHS_)
+DECL_WINELIB_TYPE_AW(PFILEPATHS_)
+
+#define SPFILENOTIFY_STARTQUEUE           0x0001
+#define SPFILENOTIFY_ENDQUEUE             0x0002
+#define SPFILENOTIFY_STARTSUBQUEUE        0x0003
+#define SPFILENOTIFY_ENDSUBQUEUE          0x0004
+#define SPFILENOTIFY_STARTDELETE          0x0005
+#define SPFILENOTIFY_ENDDELETE            0x0006
+#define SPFILENOTIFY_DELETEERROR          0x0007
+#define SPFILENOTIFY_STARTRENAME          0x0008
+#define SPFILENOTIFY_ENDRENAME            0x0009
+#define SPFILENOTIFY_RENAMEERROR          0x000a
+#define SPFILENOTIFY_STARTCOPY            0x000b
+#define SPFILENOTIFY_ENDCOPY              0x000c
+#define SPFILENOTIFY_COPYERROR            0x000d
+#define SPFILENOTIFY_NEEDMEDIA            0x000e
+#define SPFILENOTIFY_QUEUESCAN            0x000f
+#define SPFILENOTIFY_CABINETINFO          0x0010
+#define SPFILENOTIFY_FILEINCABINET        0x0011
+#define SPFILENOTIFY_NEEDNEWCABINET       0x0012
+#define SPFILENOTIFY_FILEEXTRACTED        0x0013
+#define SPFILENOTIFY_FILEOPDELAYED        0x0014
+#define SPFILENOTIFY_STARTBACKUP          0x0015
+#define SPFILENOTIFY_BACKUPERROR          0x0016
+#define SPFILENOTIFY_ENDBACKUP            0x0017
+#define SPFILENOTIFY_QUEUESCAN_EX         0x0018
+#define SPFILENOTIFY_STARTREGISTRATION    0x0019
+#define SPFILENOTIFY_ENDREGISTRATION      0x0020
+#define SPFILENOTIFY_QUEUESCAN_SIGNERINFO 0x0040
+
+#define SPFILENOTIFY_LANGMISMATCH         0x00010000
+#define SPFILENOTIFY_TARGETEXISTS         0x00020000
+#define SPFILENOTIFY_TARGETNEWER          0x00040000
+
+#define SPINST_LOGCONFIG                  0x00000001
+#define SPINST_INIFILES                   0x00000002
+#define SPINST_REGISTRY                   0x00000004
+#define SPINST_INI2REG                    0x00000008
+#define SPINST_FILES                      0x00000010
+#define SPINST_BITREG                     0x00000020
+#define SPINST_REGSVR                     0x00000040
+#define SPINST_UNREGSVR                   0x00000080
+#define SPINST_PROFILEITEMS               0x00000100
+#define SPINST_COPYINF                    0x00000200
+#define SPINST_ALL                        0x000003ff
+#define SPINST_SINGLESECTION              0x00010000
+#define SPINST_LOGCONFIG_IS_FORCED        0x00020000
+#define SPINST_LOGCONFIGS_ARE_OVERRIDES   0x00040000
+#define SPINST_REGISTERCALLBACKAWARE      0x00080000
+
+#define SP_COPY_DELETESOURCE              0x00000001
+#define SP_COPY_REPLACEONLY               0x00000002
+#define SP_COPY_NEWER                     0x00000004
+#define SP_COPY_NEWER_OR_SAME             SP_COPY_NEWER
+#define SP_COPY_NOOVERWRITE               0x00000008
+#define SP_COPY_NODECOMP                  0x00000010
+#define SP_COPY_LANGUAGEAWARE             0x00000020
+#define SP_COPY_SOURCE_ABSOLUTE           0x00000040
+#define SP_COPY_SOURCEPATH_ABSOLUTE       0x00000080
+#define SP_COPY_IN_USE_NEEDS_REBOOT       0x00000100
+#define SP_COPY_FORCE_IN_USE              0x00000200
+#define SP_COPY_NOSKIP                    0x00000400
+#define SP_FLAG_CABINETCONTINUATION       0x00000800
+#define SP_COPY_FORCE_NOOVERWRITE         0x00001000
+#define SP_COPY_FORCE_NEWER               0x00002000
+#define SP_COPY_WARNIFSKIP                0x00004000
+#define SP_COPY_NOBROWSE                  0x00008000
+#define SP_COPY_NEWER_ONLY                0x00010000
+#define SP_COPY_SOURCE_SIS_MASTER         0x00020000
+#define SP_COPY_OEMINF_CATALOG_ONLY       0x00040000
+#define SP_COPY_REPLACE_BOOT_FILE         0x00080000
+#define SP_COPY_NOPRUNE                   0x00100000
+#define SP_COPY_OEM_F6_INF                0x00200000
+
+#define FLG_ADDREG_DELREG_BIT             0x00008000
+#define FLG_ADDREG_BINVALUETYPE           0x00000001
+#define FLG_ADDREG_NOCLOBBER              0x00000002
+#define FLG_ADDREG_DELVAL                 0x00000004
+#define FLG_ADDREG_APPEND                 0x00000008
+#define FLG_ADDREG_KEYONLY                0x00000010
+#define FLG_ADDREG_OVERWRITEONLY          0x00000020
+#define FLG_ADDREG_64BITKEY               0x00001000
+#define FLG_ADDREG_KEYONLY_COMMON         0x00002000
+#define FLG_ADDREG_32BITKEY               0x00004000
+#define FLG_ADDREG_TYPE_SZ                0x00000000
+#define FLG_ADDREG_TYPE_MULTI_SZ          0x00010000
+#define FLG_ADDREG_TYPE_EXPAND_SZ         0x00020000
+#define FLG_ADDREG_TYPE_BINARY           (0x00000000 | FLG_ADDREG_BINVALUETYPE)
+#define FLG_ADDREG_TYPE_DWORD            (0x00010000 | FLG_ADDREG_BINVALUETYPE)
+#define FLG_ADDREG_TYPE_NONE             (0x00020000 | FLG_ADDREG_BINVALUETYPE)
+#define FLG_ADDREG_TYPE_MASK             (0xFFFF0000 | FLG_ADDREG_BINVALUETYPE)
+
+#define FLG_DELREG_VALUE                 (0x00000000)
+#define FLG_DELREG_TYPE_MASK             FLG_ADDREG_TYPE_MASK
+#define FLG_DELREG_TYPE_SZ               FLG_ADDREG_TYPE_SZ
+#define FLG_DELREG_TYPE_MULTI_SZ         FLG_ADDREG_TYPE_MULTI_SZ
+#define FLG_DELREG_TYPE_EXPAND_SZ        FLG_ADDREG_TYPE_EXPAND_SZ
+#define FLG_DELREG_TYPE_BINARY           FLG_ADDREG_TYPE_BINARY
+#define FLG_DELREG_TYPE_DWORD            FLG_ADDREG_TYPE_DWORD
+#define FLG_DELREG_TYPE_NONE             FLG_ADDREG_TYPE_NONE
+#define FLG_DELREG_64BITKEY              FLG_ADDREG_64BITKEY
+#define FLG_DELREG_KEYONLY_COMMON        FLG_ADDREG_KEYONLY_COMMON
+#define FLG_DELREG_32BITKEY              FLG_ADDREG_32BITKEY
+#define FLG_DELREG_OPERATION_MASK        (0x000000FE)
+#define FLG_DELREG_MULTI_SZ_DELSTRING    (FLG_DELREG_TYPE_MULTI_SZ | FLG_ADDREG_DELREG_BIT | 0x00000002)
+
+/* Class installer function codes */
+#define DIF_SELECTDEVICE                    0x01
+#define DIF_INSTALLDEVICE                   0x02
+#define DIF_ASSIGNRESOURCES                 0x03
+#define DIF_PROPERTIES                      0x04
+#define DIF_REMOVE                          0x05
+#define DIF_FIRSTTIMESETUP                  0x06
+#define DIF_FOUNDDEVICE                     0x07
+#define DIF_SELECTCLASSDRIVERS              0x08
+#define DIF_VALIDATECLASSDRIVERS            0x09
+#define DIF_INSTALLCLASSDRIVERS             0x0a
+#define DIF_CALCDISKSPACE                   0x0b
+#define DIF_DESTROYPRIVATEDATA              0x0c
+#define DIF_VALIDATEDRIVER                  0x0d
+#define DIF_MOVEDEVICE                      0x0e
+#define DIF_DETECT                          0x0f
+#define DIF_INSTALLWIZARD                   0x10
+#define DIF_DESTROYWIZARDDATA               0x11
+#define DIF_PROPERTYCHANGE                  0x12
+#define DIF_ENABLECLASS                     0x13
+#define DIF_DETECTVERIFY                    0x14
+#define DIF_INSTALLDEVICEFILES              0x15
+#define DIF_UNREMOVE                        0x16
+#define DIF_SELECTBESTCOMPATDRV             0x17
+#define DIF_ALLOW_INSTALL                   0x18
+#define DIF_REGISTERDEVICE                  0x19
+#define DIF_NEWDEVICEWIZARD_PRESELECT       0x1a
+#define DIF_NEWDEVICEWIZARD_SELECT          0x1b
+#define DIF_NEWDEVICEWIZARD_PREANALYZE      0x1c
+#define DIF_NEWDEVICEWIZARD_POSTANALYZE     0x1d
+#define DIF_NEWDEVICEWIZARD_FINISHINSTALL   0x1e
+#define DIF_UNUSED1                         0x1f
+#define DIF_INSTALLINTERFACES               0x20
+#define DIF_DETECTCANCEL                    0x21
+#define DIF_REGISTER_COINSTALLERS           0x22
+#define DIF_ADDPROPERTYPAGE_ADVANCED        0x23
+#define DIF_ADDPROPERTYPAGE_BASIC           0x24
+#define DIF_RESERVED1                       0x25
+#define DIF_TROUBLESHOOTER                  0x26
+#define DIF_POWERMESSAGEWAKE                0x27
+#define DIF_ADDREMOTEPROPERTYPAGE_ADVANCED  0x28
+#define DIF_UPDATEDRIVER_UI                 0x29
+#define DIF_RESERVED2                       0x30
+
+/* Directory ids */
+#define DIRID_ABSOLUTE                (-1)
+#define DIRID_ABSOLUTE_16BIT          0xffff
+#define DIRID_NULL                    0
+#define DIRID_SRCPATH                 1
+#define DIRID_WINDOWS                 10
+#define DIRID_SYSTEM                  11
+#define DIRID_DRIVERS                 12
+#define DIRID_IOSUBSYS                DIRID_DRIVERS
+#define DIRID_INF                     17
+#define DIRID_HELP                    18
+#define DIRID_FONTS                   20
+#define DIRID_VIEWERS                 21
+#define DIRID_COLOR                   23
+#define DIRID_APPS                    24
+#define DIRID_SHARED                  25
+#define DIRID_BOOT                    30
+#define DIRID_SYSTEM16                50
+#define DIRID_SPOOL                   51
+#define DIRID_SPOOLDRIVERS            52
+#define DIRID_USERPROFILE             53
+#define DIRID_LOADER                  54
+#define DIRID_PRINTPROCESSOR          55
+#define DIRID_DEFAULT                 DIRID_SYSTEM
+
+#define DIRID_COMMON_STARTMENU        16406
+#define DIRID_COMMON_PROGRAMS         16407
+#define DIRID_COMMON_STARTUP          16408
+#define DIRID_COMMON_DESKTOPDIRECTORY 16409
+#define DIRID_COMMON_FAVORITES        16415
+#define DIRID_COMMON_APPDATA          16419
+#define DIRID_PROGRAM_FILES           16422
+#define DIRID_SYSTEM_X86              16425
+#define DIRID_PROGRAM_FILES_X86       16426
+#define DIRID_PROGRAM_FILES_COMMON    16427
+#define DIRID_PROGRAM_FILES_COMMONX86 16428
+#define DIRID_COMMON_TEMPLATES        16429
+#define DIRID_COMMON_DOCUMENTS        16430
+
+#define DIRID_USER                    0x8000
+
+
+/* Error code */
+
+#define ERROR_EXPECTED_SECTION_NAME       (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0)
+#define ERROR_BAD_SECTION_NAME_LINE       (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|1)
+#define ERROR_SECTION_NAME_TOO_LONG       (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|2)
+#define ERROR_GENERAL_SYNTAX              (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|3)
+#define ERROR_WRONG_INF_STYLE             (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x100)
+#define ERROR_SECTION_NOT_FOUND           (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x101)
+#define ERROR_LINE_NOT_FOUND              (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x102)
+#define ERROR_NO_BACKUP                   (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x103)
+#define ERROR_NO_ASSOCIATED_CLASS         (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x200)
+#define ERROR_CLASS_MISMATCH              (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x201)
+#define ERROR_DUPLICATE_FOUND             (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x202)
+#define ERROR_NO_DRIVER_SELECTED          (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x203)
+#define ERROR_KEY_DOES_NOT_EXIST          (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x204)
+#define ERROR_INVALID_DEVINST_NAME        (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x205)
+#define ERROR_INVALID_CLASS               (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x206)
+#define ERROR_DEVINST_ALREADY_EXISTS      (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x207)
+#define ERROR_DEVINFO_NOT_REGISTERED      (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x208)
+#define ERROR_INVALID_REG_PROPERTY        (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x209)
+#define ERROR_NO_INF                      (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x20A)
+#define ERROR_NO_SUCH_DEVINST             (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x20B)
+#define ERROR_CANT_LOAD_CLASS_ICON        (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x20C)
+#define ERROR_INVALID_CLASS_INSTALLER     (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x20D)
+#define ERROR_DI_DO_DEFAULT               (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x20E)
+#define ERROR_DI_NOFILECOPY               (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x20F)
+#define ERROR_INVALID_HWPROFILE           (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x210)
+#define ERROR_NO_DEVICE_SELECTED          (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x211)
+#define ERROR_DEVINFO_LIST_LOCKED         (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x212)
+#define ERROR_DEVINFO_DATA_LOCKED         (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x213)
+#define ERROR_DI_BAD_PATH                 (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x214)
+#define ERROR_NO_CLASSINSTALL_PARAMS      (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x215)
+#define ERROR_FILEQUEUE_LOCKED            (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x216)
+#define ERROR_BAD_SERVICE_INSTALLSECT     (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x217)
+#define ERROR_NO_CLASS_DRIVER_LIST        (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x218)
+#define ERROR_NO_ASSOCIATED_SERVICE       (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x219)
+#define ERROR_NO_DEFAULT_DEVICE_INTERFACE (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x21A)
+#define ERROR_DEVICE_INTERFACE_ACTIVE     (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x21B)
+#define ERROR_DEVICE_INTERFACE_REMOVED    (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x21C)
+#define ERROR_BAD_INTERFACE_INSTALLSECT   (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x21D)
+#define ERROR_NO_SUCH_INTERFACE_CLASS     (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x21E)
+#define ERROR_INVALID_REFERENCE_STRING    (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x21F)
+#define ERROR_INVALID_MACHINENAME         (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x220)
+#define ERROR_REMOTE_COMM_FAILURE         (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x221)
+#define ERROR_MACHINE_UNAVAILABLE         (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x222)
+#define ERROR_NO_CONFIGMGR_SERVICES       (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x223)
+#define ERROR_INVALID_PROPPAGE_PROVIDER   (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x224)
+#define ERROR_NO_SUCH_DEVICE_INTERFACE    (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x225)
+#define ERROR_DI_POSTPROCESSING_REQUIRED  (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x226)
+#define ERROR_INVALID_COINSTALLER         (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x227)
+#define ERROR_NO_COMPAT_DRIVERS           (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x228)
+#define ERROR_NO_DEVICE_ICON              (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x229)
+#define ERROR_INVALID_INF_LOGCONFIG       (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x22A)
+#define ERROR_DI_DONT_INSTALL             (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x22B)
+#define ERROR_INVALID_FILTER_DRIVER       (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x22C)
+#define ERROR_NON_WINDOWS_NT_DRIVER       (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x22D)
+#define ERROR_NON_WINDOWS_DRIVER          (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x22E)
+#define ERROR_NO_CATALOG_FOR_OEM_INF      (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x22F)
+#define ERROR_DEVINSTALL_QUEUE_NONNATIVE  (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x230)
+#define ERROR_NOT_DISABLEABLE             (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x231)
+#define ERROR_CANT_REMOVE_DEVINST         (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x232)
+#define ERROR_INVALID_TARGET              (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x233)
+#define ERROR_DRIVER_NONNATIVE            (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x234)
+#define ERROR_IN_WOW64                    (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x235)
+#define ERROR_SET_SYSTEM_RESTORE_POINT    (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x236)
+#define ERROR_INCORRECTLY_COPIED_INF      (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x237)
+#define ERROR_SCE_DISABLED                (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x238)
+#define ERROR_NO_DEFAULT_INTERFACE_DEVICE ERROR_NO_DEFAULT_DEVICE_INTERFACE
+#define ERROR_INTERFACE_DEVICE_ACTIVE     ERROR_DEVICE_INTERFACE_ACTIVE
+#define ERROR_INTERFACE_DEVICE_REMOVED    ERROR_DEVICE_INTERFACE_REMOVED
+#define ERROR_NO_SUCH_INTERFACE_DEVICE    ERROR_NO_SUCH_DEVICE_INTERFACE
+#define ERROR_NOT_INSTALLED               (APPLICATION_ERROR_MASK|ERROR_SEVERITY_ERROR|0x1000)
+
+HINF     WINAPI SetupOpenInfFileA( PCSTR name, PCSTR class, DWORD style, UINT *error );
+HINF     WINAPI SetupOpenInfFileW( PCWSTR name, PCWSTR class, DWORD style, UINT *error );
+#define         SetupOpenInfFile WINELIB_NAME_AW(SetupOpenInfFile)
+BOOL     WINAPI SetupOpenAppendInfFileA( PCSTR, HINF, UINT * );
+BOOL     WINAPI SetupOpenAppendInfFileW( PCWSTR, HINF, UINT * );
+#define         SetupOpenAppendInfFile WINELIB_NAME_AW(SetupOpenAppendInfFile)
+void     WINAPI SetupCloseInfFile( HINF hinf );
+BOOL     WINAPI SetupGetLineByIndexA( HINF, PCSTR, DWORD, INFCONTEXT * );
+BOOL     WINAPI SetupGetLineByIndexW( HINF, PCWSTR, DWORD, INFCONTEXT * );
+#define         SetupGetLineByIndex WINELIB_NAME_AW(SetupGetLineByIndex)
+LONG     WINAPI SetupGetLineCountA( HINF hinf, PCSTR section );
+LONG     WINAPI SetupGetLineCountW( HINF hinf, PCWSTR section );
+#define         SetupGetLineCount WINELIB_NAME_AW(SetupGetLineCount)
+BOOL     WINAPI SetupFindFirstLineA( HINF hinf, PCSTR section, PCSTR key, INFCONTEXT *context );
+BOOL     WINAPI SetupFindFirstLineW( HINF hinf, PCWSTR section, PCWSTR key, INFCONTEXT *context );
+#define         SetupFindFirstLine WINELIB_NAME_AW(SetupFindFirstLine)
+BOOL     WINAPI SetupFindNextLine( const INFCONTEXT *, INFCONTEXT * );
+BOOL     WINAPI SetupFindNextMatchLineA( const INFCONTEXT *, PCSTR, INFCONTEXT * );
+BOOL     WINAPI SetupFindNextMatchLineW( const INFCONTEXT *, PCWSTR, INFCONTEXT * );
+#define         SetupFindNextMatchLine WINELIB_NAME_AW(SetupFindNextMatchLine)
+BOOL     WINAPI SetupGetLineTextA( const INFCONTEXT *, HINF, PCSTR, PCSTR, PSTR, DWORD, DWORD * );
+BOOL     WINAPI SetupGetLineTextW( const INFCONTEXT *, HINF, PCWSTR, PCWSTR, PWSTR, DWORD, DWORD * );
+#define         SetupGetLineText WINELIB_NAME_AW(SetupGetLineText)
+DWORD    WINAPI SetupGetFieldCount( const INFCONTEXT * );
+BOOL     WINAPI SetupGetIntField( const INFCONTEXT *, DWORD, INT * );
+BOOL     WINAPI SetupGetStringFieldA( const INFCONTEXT *, DWORD, PSTR, DWORD, DWORD * );
+BOOL     WINAPI SetupGetStringFieldW( const INFCONTEXT *, DWORD, PWSTR, DWORD, DWORD * );
+#define         SetupGetStringField WINELIB_NAME_AW(SetupGetStringField)
+BOOL     WINAPI SetupGetBinaryField( const INFCONTEXT *, DWORD, BYTE *, DWORD, DWORD * );
+BOOL     WINAPI SetupGetMultiSzFieldA( const INFCONTEXT *, DWORD, PSTR, DWORD, DWORD * );
+BOOL     WINAPI SetupGetMultiSzFieldW( const INFCONTEXT *, DWORD, PWSTR, DWORD, DWORD * );
+#define         SetupGetMultiSzField WINELIB_NAME_AW(SetupGetMultiSzField)
+BOOL     WINAPI SetupSetDirectoryIdA( HINF, DWORD, PCSTR );
+BOOL     WINAPI SetupSetDirectoryIdW( HINF, DWORD, PCWSTR );
+#define         SetupSetDirectoryId WINELIB_NAME_AW(SetupSetDirectoryId)
+HSPFILEQ WINAPI SetupOpenFileQueue(void);
+BOOL     WINAPI SetupCloseFileQueue( HSPFILEQ );
+BOOL     WINAPI SetupSetFileQueueAlternatePlatformA( HSPFILEQ, PSP_ALTPLATFORM_INFO, PCSTR );
+BOOL     WINAPI SetupSetFileQueueAlternatePlatformW( HSPFILEQ, PSP_ALTPLATFORM_INFO, PCWSTR );
+#define         SetupSetFileQueueAlternatePlatform WINELIB_NAME_AW(SetupSetFileQueueAlternatePlatform)
+BOOL     WINAPI SetupQueueCopyA(HSPFILEQ,PCSTR,PCSTR,PCSTR,PCSTR,PCSTR,PCSTR,PCSTR,DWORD);
+BOOL     WINAPI SetupQueueCopyW(HSPFILEQ,PCWSTR,PCWSTR,PCWSTR,PCWSTR,PCWSTR,PCWSTR,PCWSTR,DWORD);
+#define         SetupQueueCopy WINELIB_NAME_AW(SetupQueueCopy)
+BOOL     WINAPI SetupQueueCopyIndirectA( PSP_FILE_COPY_PARAMS_A );
+BOOL     WINAPI SetupQueueCopyIndirectW( PSP_FILE_COPY_PARAMS_W );
+#define         SetupQueueCopyIndirect WINELIB_NAME_AW(SetupQueueCopyIndirect)
+BOOL     WINAPI SetupQueueDefaultCopyA( HSPFILEQ, HINF, PCSTR, PCSTR, PCSTR, DWORD );
+BOOL     WINAPI SetupQueueDefaultCopyW( HSPFILEQ, HINF, PCWSTR, PCWSTR, PCWSTR, DWORD );
+#define         SetupQueueDefaultCopy WINELIB_NAME_AW(SetupQueueDefaultCopy)
+BOOL     WINAPI SetupQueueDeleteA( HSPFILEQ, PCSTR, PCSTR );
+BOOL     WINAPI SetupQueueDeleteW( HSPFILEQ, PCWSTR, PCWSTR );
+#define         SetupQueueDelete WINELIB_NAME_AW(SetupQueueDelete)
+BOOL     WINAPI SetupQueueRenameA( HSPFILEQ, PCSTR, PCSTR, PCSTR, PCSTR );
+BOOL     WINAPI SetupQueueRenameW( HSPFILEQ, PCWSTR, PCWSTR, PCWSTR, PCWSTR );
+#define         SetupQueueRename WINELIB_NAME_AW(SetupQueueRename)
+BOOL     WINAPI SetupCommitFileQueueA( HWND, HSPFILEQ, PSP_FILE_CALLBACK_A, PVOID );
+BOOL     WINAPI SetupCommitFileQueueW( HWND, HSPFILEQ, PSP_FILE_CALLBACK_W, PVOID );
+#define         SetupCommitFileQueue WINELIB_NAME_AW(SetupCommitFileQueue)
+BOOL     WINAPI SetupScanFileQueueA( HSPFILEQ, DWORD, HWND, PSP_FILE_CALLBACK_A, PVOID, PDWORD );
+BOOL     WINAPI SetupScanFileQueueW( HSPFILEQ, DWORD, HWND, PSP_FILE_CALLBACK_W, PVOID, PDWORD );
+#define         SetupScanFileQueue WINELIB_NAME_AW(SetupScanFileQueue)
+BOOL     WINAPI SetupGetFileQueueCount( HSPFILEQ, UINT, PUINT );
+BOOL     WINAPI SetupGetFileQueueFlags( HSPFILEQ, PDWORD );
+BOOL     WINAPI SetupSetFileQueueFlags( HSPFILEQ, DWORD, DWORD );
+BOOL     WINAPI SetupQueueCopySectionA( HSPFILEQ, PCSTR, HINF, HINF, PCSTR, DWORD );
+BOOL     WINAPI SetupQueueCopySectionW( HSPFILEQ, PCWSTR, HINF, HINF, PCWSTR, DWORD );
+#define         SetupQueueCopySection WINELIB_NAME_AW(SetupQueueCopySection)
+BOOL     WINAPI SetupQueueDeleteSectionA( HSPFILEQ, HINF, HINF, PCSTR );
+BOOL     WINAPI SetupQueueDeleteSectionW( HSPFILEQ, HINF, HINF, PCWSTR );
+#define         SetupQueueDeleteSection WINELIB_NAME_AW(SetupQueueDeleteSection)
+BOOL     WINAPI SetupQueueRenameSectionA( HSPFILEQ, HINF, HINF, PCSTR );
+BOOL     WINAPI SetupQueueRenameSectionW( HSPFILEQ, HINF, HINF, PCWSTR );
+#define         SetupQueueRenameSection WINELIB_NAME_AW(SetupQueueRenameSection)
+PVOID    WINAPI SetupInitDefaultQueueCallback( HWND );
+PVOID    WINAPI SetupInitDefaultQueueCallbackEx( HWND, HWND, UINT, DWORD, PVOID );
+void     WINAPI SetupTermDefaultQueueCallback( PVOID );
+UINT     WINAPI SetupDefaultQueueCallbackA( PVOID, UINT, UINT_PTR, UINT_PTR );
+UINT     WINAPI SetupDefaultQueueCallbackW( PVOID, UINT, UINT_PTR, UINT_PTR );
+#define         SetupDefaultQueueCallback WINELIB_NAME_AW(SetupDefaultQueueCallback)
+BOOL     WINAPI SetupInstallFilesFromInfSectionA( HINF, HINF, HSPFILEQ, PCSTR, PCSTR, UINT );
+BOOL     WINAPI SetupInstallFilesFromInfSectionW( HINF, HINF, HSPFILEQ, PCWSTR, PCWSTR, UINT );
+#define         SetupInstallFilesFromInfSection WINELIB_NAME_AW(SetupInstallFilesFromInfSection)
+BOOL     WINAPI SetupInstallFromInfSectionA(HWND,HINF,PCSTR,UINT,HKEY,PCSTR,UINT,
+                                            PSP_FILE_CALLBACK_A,PVOID,HDEVINFO,PSP_DEVINFO_DATA);
+BOOL     WINAPI SetupInstallFromInfSectionW(HWND,HINF,PCWSTR,UINT,HKEY,PCWSTR,UINT,
+                                            PSP_FILE_CALLBACK_W,PVOID,HDEVINFO,PSP_DEVINFO_DATA);
+#define         SetupInstallFromInfSection WINELIB_NAME_AW(SetupInstallFromInfSection)
+
+
+#endif /* _INC_SETUPAPI */
diff --git a/include/winnt.h b/include/winnt.h
index a8d9a80..76976e8 100644
--- a/include/winnt.h
+++ b/include/winnt.h
@@ -212,6 +212,13 @@
 #define OPTIONAL
 #endif
 
+/* Error Masks */
+#define APPLICATION_ERROR_MASK       0x20000000
+#define ERROR_SEVERITY_SUCCESS       0x00000000
+#define ERROR_SEVERITY_INFORMATIONAL 0x40000000
+#define ERROR_SEVERITY_WARNING       0x80000000
+#define ERROR_SEVERITY_ERROR         0xC0000000
+
 /* Standard data types */
 typedef const void                  *PCVOID,   *LPCVOID;
 typedef int             BOOL,       *PBOOL,    *LPBOOL;