Started porting Setupapi.dll from WINE to ReactOS.
authorSteven Edwards <winehacker@gmail.com>
Sat, 24 Jan 2004 15:39:47 +0000 (15:39 +0000)
committerSteven Edwards <winehacker@gmail.com>
Sat, 24 Jan 2004 15:39:47 +0000 (15:39 +0000)
svn path=/trunk/; revision=7863

19 files changed:
reactos/include/wine/setupapi.h [new file with mode: 0644]
reactos/lib/setupapi/Makefile.in [new file with mode: 0644]
reactos/lib/setupapi/Makefile.ros-template [new file with mode: 0644]
reactos/lib/setupapi/devinst.c [new file with mode: 0644]
reactos/lib/setupapi/devinst16.c [new file with mode: 0644]
reactos/lib/setupapi/dirid.c [new file with mode: 0644]
reactos/lib/setupapi/infparse.c [new file with mode: 0644]
reactos/lib/setupapi/install.c [new file with mode: 0644]
reactos/lib/setupapi/makefile [new file with mode: 0644]
reactos/lib/setupapi/parser.c [new file with mode: 0644]
reactos/lib/setupapi/queue.c [new file with mode: 0644]
reactos/lib/setupapi/setupapi.rc [new file with mode: 0644]
reactos/lib/setupapi/setupapi.spec [new file with mode: 0644]
reactos/lib/setupapi/setupapi_private.h [new file with mode: 0644]
reactos/lib/setupapi/setupcab.c [new file with mode: 0644]
reactos/lib/setupapi/setupx16.h [new file with mode: 0644]
reactos/lib/setupapi/setupx_main.c [new file with mode: 0644]
reactos/lib/setupapi/stubs.c [new file with mode: 0644]
reactos/lib/setupapi/virtcopy.c [new file with mode: 0644]

diff --git a/reactos/include/wine/setupapi.h b/reactos/include/wine/setupapi.h
new file mode 100644 (file)
index 0000000..0100b00
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Compatibility header
+ *
+ * This header is wrapper to allow compilation of Wine DLLs under ReactOS
+ * build system. It contains definitions commonly refered to as Wineisms
+ * and definitions that are missing in w32api.
+ */
+
+#ifndef __WINE_SETUPAPI_H
+#define __WINE_SETUPAPI_H
+
+#include_next <setupapi.h>
+
+#define FLG_ADDREG_DELREG_BIT             0x00008000
+#define FLG_DELREG_KEYONLY_COMMON        FLG_ADDREG_KEYONLY_COMMON
+#define FLG_DELREG_MULTI_SZ_DELSTRING    (FLG_DELREG_TYPE_MULTI_SZ | FLG_ADDREG_DELREG_BIT | 0x00000002)
+#define FLG_DELREG_TYPE_MULTI_SZ         FLG_ADDREG_TYPE_MULTI_SZ
+#define FLG_ADDREG_KEYONLY_COMMON         0x00002000
+#define FLG_ADDREG_DELREG_BIT             0x00008000
+#define SPINST_COPYINF                    0x00000200
+
+#endif /* __WINE_SETUPAPI_H */
diff --git a/reactos/lib/setupapi/Makefile.in b/reactos/lib/setupapi/Makefile.in
new file mode 100644 (file)
index 0000000..198ddaf
--- /dev/null
@@ -0,0 +1,32 @@
+EXTRADEFS = -D_SETUPAPI_
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = setupapi.dll
+IMPORTS   = user32 version advapi32 kernel32 ntdll
+ALTNAMES  = setupx.dll
+EXTRALIBS = $(LIBUNICODE)
+
+SPEC_SRCS16 = $(ALTNAMES:.dll=.spec)
+
+C_SRCS = \
+       devinst.c \
+       dirid.c \
+       install.c \
+       parser.c \
+       queue.c \
+       setupcab.c \
+       stubs.c
+
+C_SRCS16 = \
+       devinst16.c \
+       infparse.c \
+       setupx_main.c \
+       virtcopy.c
+
+RC_SRCS= setupapi.rc
+
+@MAKE_DLL_RULES@
+
+### Dependencies:
diff --git a/reactos/lib/setupapi/Makefile.ros-template b/reactos/lib/setupapi/Makefile.ros-template
new file mode 100644 (file)
index 0000000..a8d0fc1
--- /dev/null
@@ -0,0 +1,21 @@
+# $Id: Makefile.ros-template,v 1.1 2004/01/24 15:39:18 sedwards Exp $
+
+TARGET_NAME = setupapi
+
+TARGET_OBJECTS = @C_SRCS@
+
+TARGET_CFLAGS = @EXTRADEFS@ -D__REACTOS__
+
+TARGET_SDKLIBS = @IMPORTS@ libwine.a ntdll.a libwine_unicode.a
+
+TARGET_BASE = 0x76160000
+
+TARGET_RC_SRCS = @RC_SRCS@
+TARGET_RC_BINSRC = @RC_BINSRC@
+TARGET_RC_BINARIES = @RC_BINARIES@
+
+default: all
+
+DEP_OBJECTS = $(TARGET_OBJECTS)
+
+include $(TOOLS_PATH)/depend.mk
diff --git a/reactos/lib/setupapi/devinst.c b/reactos/lib/setupapi/devinst.c
new file mode 100644 (file)
index 0000000..7d84bf6
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * SetupAPI device installer
+ *
+ * Copyright 2000 Andreas Mohr 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "setupapi.h"
+#include "wine/debug.h"
+
+
+WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
+
+/***********************************************************************
+ *             SetupDiGetDeviceInterfaceDetailA (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetDeviceInterfaceDetailA(
+      HDEVINFO DeviceInfoSet,
+      PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
+      PSP_DEVICE_INTERFACE_DETAIL_DATA_A DeviceInterfaceDetailData,
+      DWORD DeviceInterfaceDetailDataSize,
+      PDWORD RequiredSize,
+      PSP_DEVINFO_DATA DeviceInfoData )
+{
+    FIXME("\n");
+    return FALSE;
+}
+
+/***********************************************************************
+ *             SetupDiGetDeviceInterfaceDetailW (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetDeviceInterfaceDetailW(
+      HDEVINFO DeviceInfoSet,
+      PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData,
+      PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData,
+      DWORD DeviceInterfaceDetailDataSize,
+      PDWORD RequiredSize,
+      PSP_DEVINFO_DATA DeviceInfoData )
+{
+    FIXME("\n");
+    return FALSE;
+}
+
+/***********************************************************************
+ *             SetupDiEnumDeviceInterfaces (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiEnumDeviceInterfaces(
+       HDEVINFO DeviceInfoSet,
+       PSP_DEVINFO_DATA DeviceInfoData,
+       CONST GUID * InterfaceClassGuid,
+       DWORD MemberIndex,
+       PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData)
+{
+    FIXME("\n");
+    return FALSE;
+}
+
+/***********************************************************************
+ *             SetupDiGetClassDevsA (SETUPAPI.@)
+ */
+HDEVINFO WINAPI SetupDiGetClassDevsA(
+       CONST GUID *class,
+       LPCSTR enumstr,
+       HWND parent,
+       DWORD flags)
+{
+    FIXME("%s %s %p %08lx\n",debugstr_guid(class),enumstr,parent,flags);
+
+    return (HDEVINFO) INVALID_HANDLE_VALUE;
+}
+
+/***********************************************************************
+ *             SetupDiGetClassDevsW (SETUPAPI.@)
+ */
+HDEVINFO WINAPI SetupDiGetClassDevsW(
+       CONST GUID *class,
+       LPCWSTR enumstr,
+       HWND parent,
+       DWORD flags)
+{
+    FIXME("%s %s %p %08lx\n",debugstr_guid(class),debugstr_w(enumstr),parent,flags);
+
+    return (HDEVINFO) INVALID_HANDLE_VALUE;
+}
+/***********************************************************************
+ *             SetupDiEnumDeviceInfo (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiEnumDeviceInfo(
+        HDEVINFO  devinfo,
+        DWORD  index,
+        PSP_DEVINFO_DATA info)
+{
+    FIXME("%p %ld %p\n", devinfo, index, info );
+
+    if(info==NULL)
+        return FALSE;
+    if(info->cbSize < sizeof(*info))
+        return FALSE;
+
+    return FALSE;
+}
+
+/***********************************************************************
+ *             SetupDiDestroyDeviceInfoList (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiDestroyDeviceInfoList( HDEVINFO devinfo )
+{
+    FIXME("%04lx\n", (DWORD)devinfo);
+    return FALSE;
+}
+
+/***********************************************************************
+ *             SetupDiGetDeviceRegistryPropertyA (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetDeviceRegistryPropertyA(
+        HDEVINFO  devinfo,
+        PSP_DEVINFO_DATA  DeviceInfoData,
+        DWORD   Property,
+        PDWORD  PropertyRegDataType,
+        PBYTE   PropertyBuffer,
+        DWORD   PropertyBufferSize,
+        PDWORD  RequiredSize)
+{
+    FIXME("%04lx %p %ld %p %p %ld %p\n", (DWORD)devinfo, DeviceInfoData,
+        Property, PropertyRegDataType, PropertyBuffer, PropertyBufferSize,
+        RequiredSize);
+    return FALSE;
+}
diff --git a/reactos/lib/setupapi/devinst16.c b/reactos/lib/setupapi/devinst16.c
new file mode 100644 (file)
index 0000000..0c40262
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * SetupAPI device installer
+ *
+ * Copyright 2000 Andreas Mohr 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 <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "setupx16.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
+
+/***********************************************************************
+ *             DiGetClassDevs (SETUPX.304)
+ * Return a list of installed system devices.
+ * Uses HKLM\\ENUM to list devices.
+ */
+RETERR16 WINAPI DiGetClassDevs16(LPLPDEVICE_INFO16 lplpdi,
+                                 LPCSTR lpszClassName, HWND16 hwndParent, INT16 iFlags)
+{
+    LPDEVICE_INFO16 lpdi;
+
+    FIXME("(%p, '%s', %04x, %04x), semi-stub.\n",
+          lplpdi, lpszClassName, hwndParent, iFlags);
+    lpdi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DEVICE_INFO16));
+    lpdi->cbSize = sizeof(DEVICE_INFO16);
+    *lplpdi = (LPDEVICE_INFO16)MapLS(lpdi);
+    return OK;
+}
+
+/***********************************************************************
+ *             DiBuildCompatDrvList (SETUPX.300)
+ */
+RETERR16 WINAPI DiBuildCompatDrvList16(LPDEVICE_INFO16 lpdi)
+{
+    FIXME("(%p): stub\n", lpdi);
+    lpdi->lpCompatDrvList = NULL;
+    return FALSE;
+}
+
+/***********************************************************************
+ *             DiCallClassInstaller (SETUPX.308)
+ */
+RETERR16 WINAPI DiCallClassInstaller16(/*DI_FUNCTIONS*/WORD diFctn, LPDEVICE_INFO16 lpdi)
+{
+    FIXME("(%x, %p): stub\n", diFctn, lpdi);
+    return FALSE;
+}
+
+/***********************************************************************
+ *             DiCreateDevRegKey (SETUPX.318)
+ */
+RETERR16 WINAPI DiCreateDevRegKey16(LPDEVICE_INFO16 lpdi,
+                                    VOID* p2, WORD w3,
+                                    LPCSTR s4, WORD w5)
+{
+    FIXME("(%p, %p, %x, %s, %x): stub\n", lpdi, p2, w3, debugstr_a(s4), w5);
+    return FALSE;
+}
+
+/***********************************************************************
+ *             DiDeleteDevRegKey (SETUPX.344)
+ */
+RETERR16 WINAPI DiDeleteDevRegKey16(LPDEVICE_INFO16 lpdi, INT16 iFlags)
+{
+    FIXME("(%p, %x): stub\n", lpdi, iFlags);
+    return FALSE;
+}
+
+/***********************************************************************
+ *             DiCreateDeviceInfo (SETUPX.303)
+ */
+RETERR16 WINAPI DiCreateDeviceInfo16(LPLPDEVICE_INFO16 lplpdi,
+                                     LPCSTR lpszDescription, DWORD dnDevnode,
+                                     HKEY16 hkey, LPCSTR lpszRegsubkey,
+                                     LPCSTR lpszClassName, HWND16 hwndParent)
+{
+    LPDEVICE_INFO16 lpdi;
+    FIXME("(%p %s %08lx %x %s %s %x): stub\n", lplpdi,
+          debugstr_a(lpszDescription), dnDevnode, hkey,
+          debugstr_a(lpszRegsubkey), debugstr_a(lpszClassName), hwndParent);
+    lpdi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DEVICE_INFO16));
+    lpdi->cbSize = sizeof(DEVICE_INFO16);
+    strcpy(lpdi->szClassName, lpszClassName);
+    lpdi->hwndParent = hwndParent;
+    *lplpdi = (LPDEVICE_INFO16)MapLS(lpdi);
+    return OK;
+}
+
+/***********************************************************************
+ *             DiDestroyDeviceInfoList (SETUPX.305)
+ */
+RETERR16 WINAPI DiDestroyDeviceInfoList16(LPDEVICE_INFO16 lpdi)
+{
+    FIXME("(%p): stub\n", lpdi);
+    return FALSE;
+}
+
+/***********************************************************************
+ *             DiOpenDevRegKey (SETUPX.319)
+ */
+RETERR16 WINAPI DiOpenDevRegKey16(LPDEVICE_INFO16 lpdi,
+                                  LPHKEY16 lphk,INT16 iFlags)
+{
+    FIXME("(%p %p %d): stub\n", lpdi, lphk, iFlags);
+    return FALSE;
+}
diff --git a/reactos/lib/setupapi/dirid.c b/reactos/lib/setupapi/dirid.c
new file mode 100644 (file)
index 0000000..98fd99e
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * 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 "config.h"
+#include "wine/port.h"
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winternl.h"
+#include "winerror.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.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( "unknown 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;
+
+           if (user_dirids)
+                new = HeapReAlloc( GetProcessHeap(), 0, user_dirids,
+                                                  new_size * sizeof(*new) );
+           else
+                new = HeapAlloc( GetProcessHeap(), 0, 
+                                                  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;
+}
+
+
+/***********************************************************************
+ *             SetupSetDirectoryIdA    (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/reactos/lib/setupapi/infparse.c b/reactos/lib/setupapi/infparse.c
new file mode 100644 (file)
index 0000000..c508db6
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * SetupX .inf file parsing functions
+ *
+ * Copyright 2000 Andreas Mohr 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
+ *
+ * FIXME:
+ * - return values ???
+ * - this should be reimplemented at some point to have its own
+ *   file parsing instead of using profile functions,
+ *   as some SETUPX exports probably demand that
+ *   (IpSaveRestorePosition, IpFindNextMatchLine, ...).
+ */
+
+#include <stdarg.h>
+#include <string.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winternl.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.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) FIXME( "unsupported flag: GENINSTALL_DO_REGSRCPATH\n" );
+    if (genflags & GENINSTALL_DO_CFGAUTO) FIXME( "unsupported flag: GENINSTALL_DO_CFGAUTO\n" );
+    if (genflags & GENINSTALL_DO_PERUSER) FIXME( "unsupported flag: GENINSTALL_DO_PERUSER\n" );
+
+    context = SetupInitDefaultQueueCallback( 0 );
+    if (!SetupInstallFromInfSectionA( 0, hinf, section, flags, 0, NULL,
+                                      SP_COPY_NEWER_OR_SAME, SetupDefaultQueueCallbackA,
+                                      context, 0, 0 ))
+        ret = get_last_error();
+
+    SetupTermDefaultQueueCallback( context );
+    return ret;
+}
diff --git a/reactos/lib/setupapi/install.c b/reactos/lib/setupapi/install.c
new file mode 100644 (file)
index 0000000..abf7645
--- /dev/null
@@ -0,0 +1,675 @@
+/*
+ * 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 "config.h"
+#include "wine/port.h"
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winternl.h"
+#include "winerror.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "setupapi.h"
+#include "setupapi_private.h"
+#include "wine/unicode.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 ? strtoulW( 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 %p %s\n", root_key, debugstr_w(buffer) );
+            continue;
+        }
+        TRACE( "key %p %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 )
+{
+    INFCONTEXT context;
+
+    BOOL ok = SetupFindFirstLineW( hinf, field, NULL, &context );
+
+    for (; ok; ok = SetupFindNextLine( &context, &context ))
+    {
+        WCHAR buffer[MAX_INF_STRING_LENGTH];
+        WCHAR  filename[MAX_INF_STRING_LENGTH];
+        WCHAR  section[MAX_INF_STRING_LENGTH];
+        WCHAR  entry[MAX_INF_STRING_LENGTH];
+        WCHAR  string[MAX_INF_STRING_LENGTH];
+        LPWSTR divider;
+
+        if (!SetupGetStringFieldW( &context, 1, filename,
+                                   sizeof(filename)/sizeof(WCHAR), NULL ))
+            continue;
+
+        if (!SetupGetStringFieldW( &context, 2, section,
+                                   sizeof(section)/sizeof(WCHAR), NULL ))
+            continue;
+
+        if (!SetupGetStringFieldW( &context, 4, buffer,
+                                   sizeof(buffer)/sizeof(WCHAR), NULL ))
+            continue;
+
+        divider = strchrW(buffer,'=');
+        if (divider)
+        {
+            *divider = 0;
+            strcpyW(entry,buffer);
+            divider++;
+            strcpyW(string,divider);
+        }
+        else
+        {
+            strcpyW(entry,buffer);
+            string[0]=0;
+        }
+
+        TRACE("Writing %s = %s in %s of file %s\n",debugstr_w(entry),
+               debugstr_w(string),debugstr_w(section),debugstr_w(filename));
+        WritePrivateProfileStringW(section,entry,string,filename);
+
+    }
+    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 = TRUE;
+        if (!iterate_section_fields( hinf, section, DelReg, registry_callback, &info ))
+            return FALSE;
+        info.delete = FALSE;
+        if (!iterate_section_fields( hinf, section, AddReg, 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/reactos/lib/setupapi/makefile b/reactos/lib/setupapi/makefile
new file mode 100644 (file)
index 0000000..18c58b8
--- /dev/null
@@ -0,0 +1,9 @@
+# $Id: makefile,v 1.1 2004/01/24 15:39:18 sedwards Exp $
+
+PATH_TO_TOP = ../..
+
+TARGET_TYPE = winedll
+
+include $(PATH_TO_TOP)/rules.mak
+
+include $(TOOLS_PATH)/helper.mk
diff --git a/reactos/lib/setupapi/parser.c b/reactos/lib/setupapi/parser.c
new file mode 100644 (file)
index 0000000..87066aa
--- /dev/null
@@ -0,0 +1,1797 @@
+/*
+ * 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 "config.h"
+#include "wine/port.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "winternl.h"
+#include "winerror.h"
+#include "setupapi.h"
+
+#include "wine/unicode.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 (array)
+       new_array = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, array, new_count * elem );
+    else
+       new_array = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * elem );
+
+    if (new_array)
+        *count = new_count;
+    else
+       if (array)
+           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 = strtolW( 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;
+}
+
+
+/* close the current line and prepare for parsing a new one */
+static void close_current_line( struct parser *parser )
+{
+    struct line *cur_line = parser->line;
+
+    if (cur_line)
+    {
+        /* if line has a single field and no key, the field is the key too */
+        if (cur_line->nb_fields == 1 && cur_line->key_field == -1)
+            cur_line->key_field = cur_line->first_field;
+    }
+    parser->line = 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++;
+            close_current_line( parser );
+            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;
+        }
+    }
+    close_current_line( parser );
+    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( PINFCONTEXT context_in, PINFCONTEXT 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( PINFCONTEXT context_in, PCSTR key,
+                                     PINFCONTEXT 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( PINFCONTEXT context_in, PCWSTR key,
+                                     PINFCONTEXT 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( PINFCONTEXT context, HINF hinf, PCWSTR section_name,
+                               PCWSTR key_name, PWSTR buffer, DWORD size, PDWORD 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( PINFCONTEXT context, HINF hinf, PCSTR section_name,
+                               PCSTR key_name, PSTR buffer, DWORD size, PDWORD 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( PINFCONTEXT 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( PINFCONTEXT context, DWORD index, PSTR buffer,
+                                  DWORD size, PDWORD 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( PINFCONTEXT context, DWORD index, PWSTR buffer,
+                                  DWORD size, PDWORD 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( PINFCONTEXT context, DWORD index, PINT 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( PINFCONTEXT context, DWORD index, BYTE *buffer,
+                                 DWORD size, LPDWORD 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 <<= 4) > 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++) TRACE( " %02x", buffer[i - index] );
+        TRACE( "\n" );
+    }
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *             SetupGetMultiSzFieldA    (SETUPAPI.@)
+ */
+BOOL WINAPI SetupGetMultiSzFieldA( PINFCONTEXT context, DWORD index, PSTR buffer,
+                                   DWORD size, LPDWORD 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( PINFCONTEXT context, DWORD index, PWSTR buffer,
+                                   DWORD size, LPDWORD 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/reactos/lib/setupapi/queue.c b/reactos/lib/setupapi/queue.c
new file mode 100644 (file)
index 0000000..d757868
--- /dev/null
@@ -0,0 +1,1376 @@
+/*
+ * 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 "config.h"
+#include "wine/port.h"
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winternl.h"
+#include "winerror.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "setupapi.h"
+#include "wine/unicode.h"
+#include "setupapi_private.h"
+#include "winver.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 *t, *op = queue->head;
+
+    while( op )
+    {
+        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 );
+        t = op;
+        op = op->next;
+        HeapFree( GetProcessHeap(), 0, t );
+    }
+}
+
+/* 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;
+}
+
+BOOL static do_file_copyW( LPCWSTR source, LPCWSTR target, DWORD style)
+{
+    BOOL rc = FALSE;
+    BOOL docopy = TRUE;
+
+    TRACE("copy %s to %s style 0x%lx\n",debugstr_w(source),debugstr_w(target),style);
+
+    /* before copy processing */
+    if (style & SP_COPY_REPLACEONLY)
+    {
+        if (GetFileAttributesW(target) == INVALID_FILE_ATTRIBUTES)
+            docopy = FALSE;
+    }
+    if (style & (SP_COPY_NEWER_OR_SAME | SP_COPY_NEWER_ONLY | SP_COPY_FORCE_NEWER))
+    {
+        DWORD VersionSizeSource=0;
+        DWORD VersionSizeTarget=0;
+        DWORD zero=0;
+
+        /*
+         * This is sort of an interesting workaround. You see, calling
+         * GetVersionInfoSize on a builtin dll loads that dll into memory
+         * and we do not properly unload builtin dlls.. so we effectively
+         * lock into memory all the targets we are replacing. This leads
+         * to problems when we try to register the replaced dlls.
+         *
+         * So I will test for the existence of the files first so that
+         * we just basically unconditionally replace the builtin versions.
+         */
+        if ((GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES) &&
+            (GetFileAttributesW(source) != INVALID_FILE_ATTRIBUTES))
+        {
+            VersionSizeSource = GetFileVersionInfoSizeW(source,&zero);
+            VersionSizeTarget = GetFileVersionInfoSizeW(target,&zero);
+        }
+
+        TRACE("SizeTarget %li ... SizeSource %li\n",VersionSizeTarget,
+                VersionSizeSource);
+
+        if (VersionSizeSource && VersionSizeTarget)
+        {
+            LPVOID VersionSource;
+            LPVOID VersionTarget;
+            VS_FIXEDFILEINFO *TargetInfo;
+            VS_FIXEDFILEINFO *SourceInfo;
+            INT length;
+            WCHAR  SubBlock[2]={'\\',0};
+            DWORD  ret;
+
+            VersionSource = HeapAlloc(GetProcessHeap(),0,VersionSizeSource);
+            VersionTarget = HeapAlloc(GetProcessHeap(),0,VersionSizeTarget);
+
+            ret = GetFileVersionInfoW(source,0,VersionSizeSource,VersionSource);
+            if (ret)
+              ret = GetFileVersionInfoW(target, 0, VersionSizeTarget,
+                    VersionTarget);
+
+            if (ret)
+            {
+                ret = VerQueryValueW(VersionSource, SubBlock,
+                                    (LPVOID*)&SourceInfo, &length);
+                if (ret)
+                    ret = VerQueryValueW(VersionTarget, SubBlock,
+                                         (LPVOID*)&TargetInfo, &length);
+
+                if (ret)
+                {
+                    TRACE("Versions: Source %li.%li target %li.%li\n",
+                      SourceInfo->dwFileVersionMS, SourceInfo->dwFileVersionLS,
+                      TargetInfo->dwFileVersionMS, TargetInfo->dwFileVersionLS);
+
+                    if (TargetInfo->dwFileVersionMS > SourceInfo->dwFileVersionMS)
+                    {
+                        FIXME("Notify that target version is greater..\n");
+                        docopy = FALSE;
+                    }
+                    else if ((TargetInfo->dwFileVersionMS == SourceInfo->dwFileVersionMS)
+                             && (TargetInfo->dwFileVersionLS > SourceInfo->dwFileVersionLS))
+                    {
+                        FIXME("Notify that target version is greater..\n");
+                        docopy = FALSE;
+                    }
+                    else if ((style & SP_COPY_NEWER_ONLY) &&
+                        (TargetInfo->dwFileVersionMS ==
+                         SourceInfo->dwFileVersionMS)
+                        &&(TargetInfo->dwFileVersionLS ==
+                        SourceInfo->dwFileVersionLS))
+                    {
+                        FIXME("Notify that target version is greater..\n");
+                        docopy = FALSE;
+                    }
+                }
+            }
+            HeapFree(GetProcessHeap(),0,VersionSource);
+            HeapFree(GetProcessHeap(),0,VersionTarget);
+        }
+    }
+    if (style & (SP_COPY_NOOVERWRITE | SP_COPY_FORCE_NOOVERWRITE))
+    {
+        if (GetFileAttributesW(target) != INVALID_FILE_ATTRIBUTES)
+        {
+            FIXME("Notify user target file exists\n");
+            docopy = FALSE;
+        }
+    }
+    if (style & (SP_COPY_NODECOMP | SP_COPY_LANGUAGEAWARE | SP_COPY_FORCE_IN_USE |
+                 SP_COPY_IN_USE_NEEDS_REBOOT | SP_COPY_NOSKIP | SP_COPY_WARNIFSKIP))
+    {
+        ERR("Unsupported style(s) 0x%lx\n",style);
+    }
+
+    if (docopy)
+    {
+        rc = CopyFileW(source,target,FALSE);
+        TRACE("Did copy... rc was %i\n",rc);
+    }
+
+    /* after copy processing */
+    if (style & SP_COPY_DELETESOURCE)
+    {
+       if (rc)
+            DeleteFileW(source);
+    }
+
+    return rc;
+}
+
+/***********************************************************************
+ *            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, (UINT)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 (do_file_copyW( op_result == FILEOP_NEWPATH ? newpath : paths.Source,
+                               paths.Target, op->style )) 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/reactos/lib/setupapi/setupapi.rc b/reactos/lib/setupapi/setupapi.rc
new file mode 100644 (file)
index 0000000..e218c3c
--- /dev/null
@@ -0,0 +1,38 @@
+#include <defines.h>
+#include <reactos/resource.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+       FILEVERSION     RES_UINT_FV_MAJOR,RES_UINT_FV_MINOR,RES_UINT_FV_REVISION,RES_UINT_FV_BUILD
+       PRODUCTVERSION  RES_UINT_PV_MAJOR,RES_UINT_PV_MINOR,RES_UINT_PV_REVISION,RES_UINT_PV_BUILD      
+       FILEFLAGSMASK   0x3fL
+#ifdef _DEBUG
+       FILEFLAGS       0x1L
+#else
+       FILEFLAGS       0x0L
+#endif
+       FILEOS          0x40004L
+       FILETYPE        0x2L
+       FILESUBTYPE     0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName",       RES_STR_COMPANY_NAME
+            VALUE "FileDescription",   "WINE IMM32 API Client DLL\0"
+            VALUE "FileVersion",       RES_STR_FILE_VERSION
+            VALUE "InternalName",      "imm32\0"
+            VALUE "LegalCopyright",    RES_STR_LEGAL_COPYRIGHT
+            VALUE "OriginalFilename",  "imm32.dll\0"
+            VALUE "ProductName",       RES_STR_PRODUCT_NAME
+            VALUE "ProductVersion",    RES_STR_PRODUCT_VERSION
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
diff --git a/reactos/lib/setupapi/setupapi.spec b/reactos/lib/setupapi/setupapi.spec
new file mode 100644 (file)
index 0000000..fbd58dd
--- /dev/null
@@ -0,0 +1,329 @@
+@ stdcall CM_Connect_MachineW(wstr ptr)
+@ stdcall CM_Disconnect_Machine(long)
+@ stub CM_Free_Log_Conf_Handle
+@ stub CM_Free_Res_Des_Handle
+@ stub CM_Get_DevNode_Status_Ex
+@ stub CM_Get_Device_ID_ExW
+@ stub CM_Get_First_Log_Conf_Ex
+@ stub CM_Get_Next_Res_Des_Ex
+@ stub CM_Get_Res_Des_Data_Ex
+@ stub CM_Get_Res_Des_Data_Size_Ex
+@ stub CM_Locate_DevNode_ExW
+@ stub CM_Reenumerate_DevNode_Ex
+@ 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
+@ stdcall InstallHinfSection(long long str long)
+@ 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)
+@ stdcall SetupCloseInfFile(long)
+@ stub SetupCommitFileQueue
+@ stdcall SetupCommitFileQueueA(long long ptr ptr)
+@ stdcall SetupCommitFileQueueW(long long ptr ptr)
+@ stub SetupCopyErrorA
+@ stub SetupCopyErrorW
+@ stdcall SetupCopyOEMInfA(str str long long ptr long ptr ptr)
+@ stub SetupCopyOEMInfW
+@ stub SetupCreateDiskSpaceListA
+@ stub SetupCreateDiskSpaceListW
+@ stub SetupDecompressOrCopyFileA
+@ stub SetupDecompressOrCopyFileW
+@ stub SetupDefaultQueueCallback
+@ stdcall SetupDefaultQueueCallbackA(ptr long long long)
+@ stdcall SetupDefaultQueueCallbackW(ptr long long long)
+@ stub SetupDeleteErrorA
+@ stub SetupDeleteErrorW
+@ stub SetupDestroyDiskSpaceList
+@ stub SetupDiAskForOEMDisk
+@ stub SetupDiBuildClassInfoList
+@ stdcall SetupDiBuildClassInfoListExW(long ptr long ptr wstr ptr)
+@ stub SetupDiBuildDriverInfoList
+@ stub SetupDiCallClassInstaller
+@ stub SetupDiCancelDriverInfoSearch
+@ stub SetupDiChangeState
+@ stub SetupDiClassGuidsFromNameA
+@ stdcall SetupDiClassGuidsFromNameExW(wstr ptr long ptr wstr ptr)
+@ stub SetupDiClassGuidsFromNameW
+@ stub SetupDiClassNameFromGuidA
+@ stdcall SetupDiClassNameFromGuidExW(ptr ptr long ptr wstr ptr)
+@ stub SetupDiClassNameFromGuidW
+@ stub SetupDiCreateDevRegKeyA
+@ stub SetupDiCreateDevRegKeyW
+@ stub SetupDiCreateDeviceInfoA
+@ stdcall SetupDiCreateDeviceInfoList(ptr ptr)
+@ stdcall SetupDiCreateDeviceInfoListExW(ptr long str ptr)
+@ stub SetupDiCreateDeviceInfoW
+@ stub SetupDiDeleteDevRegKey
+@ stub SetupDiDeleteDeviceInfo
+@ stub SetupDiDestroyClassImageList
+@ stdcall SetupDiDestroyDeviceInfoList(long)
+@ stub SetupDiDestroyDriverInfoList
+@ stub SetupDiDrawMiniIcon
+@ stdcall SetupDiEnumDeviceInfo(long long ptr)
+@ stdcall SetupDiEnumDeviceInterfaces(long ptr ptr long ptr)
+@ stub SetupDiEnumDriverInfoA
+@ stub SetupDiEnumDriverInfoW
+@ stub SetupDiGetActualSectionToInstallA
+@ stub SetupDiGetActualSectionToInstallW
+@ stub SetupDiGetClassBitmapIndex
+@ stub SetupDiGetClassDescriptionA
+@ stdcall SetupDiGetClassDescriptionExW(ptr ptr long ptr wstr ptr)
+@ stub SetupDiGetClassDescriptionW
+@ stub SetupDiGetClassDevPropertySheetsA
+@ stub SetupDiGetClassDevPropertySheetsW
+@ stdcall SetupDiGetClassDevsA(ptr ptr long long)
+@ stdcall SetupDiGetClassDevsExA(ptr str ptr long ptr str ptr)
+@ stdcall SetupDiGetClassDevsExW(ptr wstr ptr long ptr wstr ptr)
+@ stdcall SetupDiGetClassDevsW(ptr ptr long long)
+@ stub SetupDiGetClassImageIndex
+@ stub SetupDiGetClassImageList
+@ stub SetupDiGetClassImageListExW
+@ stub SetupDiGetClassInstallParamsA
+@ stub SetupDiGetClassInstallParamsW
+@ stub SetupDiGetDeviceInfoListClass
+@ stdcall SetupDiGetDeviceInfoListDetailA(ptr ptr)
+@ stdcall SetupDiGetDeviceInfoListDetailW(ptr ptr)
+@ stub SetupDiGetDeviceInstallParamsA
+@ stub SetupDiGetDeviceInstallParamsW
+@ stub SetupDiGetDeviceInstanceIdA
+@ stub SetupDiGetDeviceInstanceIdW
+@ stdcall SetupDiGetDeviceRegistryPropertyA(long ptr long ptr ptr long ptr)
+@ stub SetupDiGetDeviceRegistryPropertyW
+@ stub SetupDiGetDriverInfoDetailA
+@ stub SetupDiGetDriverInfoDetailW
+@ stub SetupDiGetDriverInstallParamsA
+@ stub SetupDiGetDriverInstallParamsW
+@ stub SetupDiGetDeviceInterfaceAlias
+@ stdcall SetupDiGetDeviceInterfaceDetailA(long ptr ptr long ptr ptr)
+@ stdcall SetupDiGetDeviceInterfaceDetailW(long ptr ptr long ptr ptr)
+@ 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
+@ stdcall SetupDiOpenClassRegKeyExW(ptr long long wstr ptr)
+@ stub SetupDiOpenDevRegKey
+@ stub SetupDiOpenDeviceInfoA
+@ stub SetupDiOpenDeviceInfoW
+@ stub SetupDiOpenDeviceInterfaceRegKey
+@ 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)
+@ stdcall SetupFindFirstLineW(long wstr wstr ptr)
+@ stdcall SetupFindNextLine(ptr ptr)
+@ stdcall SetupFindNextMatchLineA(ptr str ptr)
+@ stdcall SetupFindNextMatchLineW(ptr wstr ptr)
+@ stub SetupFreeSourceListA
+@ stub SetupFreeSourceListW
+@ stdcall SetupGetBinaryField(ptr long ptr long ptr)
+@ stdcall SetupGetFieldCount(ptr)
+@ stub SetupGetFileCompressionInfoA
+@ stub SetupGetFileCompressionInfoW
+@ stdcall SetupGetFileQueueCount(long long ptr)
+@ stdcall SetupGetFileQueueFlags(long ptr)
+@ stub SetupGetInfFileListA
+@ stub SetupGetInfFileListW
+@ stub SetupGetInfInformationA
+@ stub SetupGetInfInformationW
+@ stdcall SetupGetIntField(ptr long ptr)
+@ stdcall SetupGetLineByIndexA(long str long ptr)
+@ stdcall SetupGetLineByIndexW(long wstr long ptr)
+@ stdcall SetupGetLineCountA(long str)
+@ stdcall SetupGetLineCountW(long wstr)
+@ stdcall SetupGetLineTextA(ptr long str str ptr long ptr)
+@ stdcall SetupGetLineTextW(ptr long wstr wstr ptr long ptr)
+@ stdcall SetupGetMultiSzFieldA(ptr long ptr long ptr)
+@ stdcall SetupGetMultiSzFieldW(ptr long ptr long ptr)
+@ stub SetupGetSourceFileLocationA
+@ stub SetupGetSourceFileLocationW
+@ stub SetupGetSourceFileSizeA
+@ stub SetupGetSourceFileSizeW
+@ stub SetupGetSourceInfoA
+@ stub SetupGetSourceInfoW
+@ stdcall SetupGetStringFieldA(ptr long ptr long ptr)
+@ stdcall SetupGetStringFieldW(ptr long ptr long ptr)
+@ stub SetupGetTargetPathA
+@ stub SetupGetTargetPathW
+@ stdcall SetupInitDefaultQueueCallback(long)
+@ stdcall SetupInitDefaultQueueCallbackEx(long long long long ptr)
+@ stub SetupInitializeFileLogA
+@ stub SetupInitializeFileLogW
+@ stub SetupInstallFileA
+@ stub SetupInstallFileExA
+@ stub SetupInstallFileExW
+@ stub SetupInstallFileW
+@ stdcall SetupInstallFilesFromInfSectionA(long long long str str long)
+@ stdcall SetupInstallFilesFromInfSectionW(long long long wstr wstr long)
+@ stdcall SetupInstallFromInfSectionA(long long str long long str long ptr ptr long ptr)
+@ stdcall SetupInstallFromInfSectionW(long long wstr long long wstr long ptr ptr long ptr)
+@ stub SetupInstallServicesFromInfSectionA
+@ stub SetupInstallServicesFromInfSectionW
+@ stdcall SetupIterateCabinetA(str long ptr ptr)
+@ stdcall SetupIterateCabinetW(wstr long ptr ptr)
+@ stub SetupLogFileA
+@ stub SetupLogFileW
+@ stdcall SetupOpenAppendInfFileA(str long ptr)
+@ stdcall SetupOpenAppendInfFileW(wstr long ptr)
+@ stdcall SetupOpenFileQueue()
+@ stdcall SetupOpenInfFileA(str str long ptr)
+@ stdcall SetupOpenInfFileW(wstr wstr long ptr)
+@ 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 SetupQueryInfOriginalFileInformationW
+@ stub SetupQuerySourceListA
+@ stub SetupQuerySourceListW
+@ stub SetupQuerySpaceRequiredOnDriveA
+@ stub SetupQuerySpaceRequiredOnDriveW
+@ stdcall SetupQueueCopyA(long str str str str str str str long)
+@ stdcall SetupQueueCopyIndirectA(ptr)
+@ stdcall SetupQueueCopyIndirectW(ptr)
+@ stdcall SetupQueueCopySectionA(long str long long str long)
+@ stdcall SetupQueueCopySectionW(long wstr long long wstr long)
+@ stdcall SetupQueueCopyW(long wstr wstr wstr wstr wstr wstr wstr long)
+@ stdcall SetupQueueDefaultCopyA(long long str str str long)
+@ stdcall SetupQueueDefaultCopyW(long long wstr wstr wstr long)
+@ stdcall SetupQueueDeleteA(long str str)
+@ stdcall SetupQueueDeleteSectionA(long long long str)
+@ stdcall SetupQueueDeleteSectionW(long long long wstr)
+@ stdcall SetupQueueDeleteW(long wstr wstr)
+@ stdcall SetupQueueRenameA(long str str str str)
+@ stdcall SetupQueueRenameSectionA(long long long str)
+@ stdcall SetupQueueRenameSectionW(long long long wstr)
+@ stdcall SetupQueueRenameW(long wstr wstr wstr wstr)
+@ 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)
+@ stdcall SetupScanFileQueueW(long long long ptr ptr ptr)
+@ stdcall SetupSetDirectoryIdA(long long str)
+@ stub SetupSetDirectoryIdExA
+@ stub SetupSetDirectoryIdExW
+@ stdcall SetupSetDirectoryIdW(long long wstr)
+@ stdcall SetupSetFileQueueFlags(long long long)
+@ stub SetupSetPlatformPathOverrideA
+@ stub SetupSetPlatformPathOverrideW
+@ stub SetupSetSourceListA
+@ stub SetupSetSourceListW
+@ stdcall SetupTermDefaultQueueCallback(ptr)
+@ 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 pSetupAddMiniIconToList
+@ stub pSetupAddTagToGroupOrderListEntry
+@ stub pSetupAppendStringToMultiSz
+@ stub pSetupDirectoryIdToPath
+@ stub pSetupGetField
+@ stub pSetupGetOsLoaderDriveAndPath
+@ stub pSetupGetVersionDatum
+@ stub pSetupGuidFromString
+@ stub pSetupIsGuidNull
+@ stub pSetupMakeSurePathExists
+@ stub pSetupStringFromGuid
diff --git a/reactos/lib/setupapi/setupapi_private.h b/reactos/lib/setupapi/setupapi_private.h
new file mode 100644 (file)
index 0000000..443c3e4
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2001 Andreas Mohr
+ *
+ * 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
+ */
+
+#ifndef __SETUPAPI_PRIVATE_H
+#define __SETUPAPI_PRIVATE_H
+
+#define COPYFILEDLGORD 1000
+#define SOURCESTRORD   500
+#define DESTSTRORD     501
+#define PROGRESSORD    502
+
+
+#define REG_INSTALLEDFILES "System\\CurrentControlSet\\Control\\InstalledFiles"
+#define REGPART_RENAME "\\Rename"
+#define REG_VERSIONCONFLICT "Software\\Microsoft\\VersionConflictManager"
+
+/* 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 );
+
+/* from msvcrt/sys/stat.h */
+#define _S_IWRITE 0x0080
+#define _S_IREAD  0x0100
+
+#endif /* __SETUPAPI_PRIVATE_H */
diff --git a/reactos/lib/setupapi/setupcab.c b/reactos/lib/setupapi/setupcab.c
new file mode 100644 (file)
index 0000000..c13503a
--- /dev/null
@@ -0,0 +1,686 @@
+/* 
+ * Setupapi cabinet routines
+ *
+ * Copyright 2003 Gregory M. Turner
+ *
+ * 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
+ *
+ *
+ * Many useful traces are commented in code, uncomment them if you have
+ * trouble and run with --debugmsg +setupapi
+ * 
+ */
+
+#include <stdarg.h>
+#include "string.h"
+#include "stdlib.h"
+
+#include "wine/debug.h"
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "setupapi.h"
+#include "setupapi_private.h"
+#include "fdi.h"
+#include "wine/unicode.h"
+
+#include "fcntl.h"
+#include "share.h"
+
+#include "wine/debug.h"
+
+static HINSTANCE CABINET_hInstance = 0;
+
+static HFDI (__cdecl *sc_FDICreate)(PFNALLOC, PFNFREE, PFNOPEN,
+                PFNREAD, PFNWRITE, PFNCLOSE, PFNSEEK, int, PERF);
+
+static BOOL (__cdecl *sc_FDICopy)(HFDI, char *, char *, int,
+                PFNFDINOTIFY, PFNFDIDECRYPT, void *);
+
+static BOOL (__cdecl *sc_FDIDestroy)(HFDI);
+
+#define SC_HSC_A_MAGIC 0xACABFEED
+typedef struct {
+  UINT magic;
+  HFDI hfdi;
+  PSP_FILE_CALLBACK_A msghandler;
+  PVOID context;
+  CHAR most_recent_cabinet_name[MAX_PATH];
+} SC_HSC_A, *PSC_HSC_A;
+
+#define SC_HSC_W_MAGIC 0x0CABFEED
+typedef struct {
+  UINT magic;
+  HFDI hfdi;
+  PSP_FILE_CALLBACK_W msghandler;
+  PVOID context;
+  WCHAR most_recent_cabinet_name[MAX_PATH];
+} SC_HSC_W, *PSC_HSC_W;
+
+WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
+
+static BOOL LoadCABINETDll(void)
+{
+  if (!CABINET_hInstance) {
+    CABINET_hInstance = LoadLibraryA("cabinet.dll");
+    if (CABINET_hInstance)  {
+      sc_FDICreate = (void *)GetProcAddress(CABINET_hInstance, "FDICreate");
+      sc_FDICopy = (void *)GetProcAddress(CABINET_hInstance, "FDICopy");
+      sc_FDIDestroy = (void *)GetProcAddress(CABINET_hInstance, "FDIDestroy");
+      return TRUE;
+    } else {
+      ERR("load cabinet dll failed.\n");
+      return FALSE;
+    }
+  } else
+    return TRUE;
+}
+
+static void UnloadCABINETDll(void)
+{
+  if (CABINET_hInstance) {
+    FreeLibrary(CABINET_hInstance);
+    CABINET_hInstance = 0;
+  }
+}
+
+/* FDICreate callbacks */
+
+static void *sc_cb_alloc(ULONG cb)
+{
+  return malloc(cb);
+}
+
+static void sc_cb_free(void *pv)
+{
+  free(pv);
+}
+
+static INT_PTR sc_cb_open(char *pszFile, int oflag, int pmode)
+{
+  DWORD creation = 0, sharing = 0;
+  int ioflag = 0;
+  INT_PTR ret = 0;
+  SECURITY_ATTRIBUTES sa;
+
+  /* TRACE("(pszFile == %s, oflag == %d, pmode == %d)\n", debugstr_a(pszFile), oflag, pmode); */
+
+  switch(oflag & (_O_RDONLY | _O_WRONLY | _O_RDWR)) {
+  case _O_RDONLY:
+    ioflag |= GENERIC_READ;
+    break;
+  case _O_WRONLY:
+    ioflag |= GENERIC_WRITE;
+    break;
+  case _O_RDWR:
+    ioflag |= GENERIC_READ & GENERIC_WRITE;
+    break;
+  case _O_WRONLY | _O_RDWR: /* hmmm.. */
+    ERR("_O_WRONLY & _O_RDWR in oflag?\n");
+    return -1;
+  }
+
+  if (oflag & _O_CREAT) {
+    if (oflag & _O_EXCL)
+      creation = CREATE_NEW;
+    else if (oflag & _O_TRUNC)
+      creation = CREATE_ALWAYS;
+    else
+      creation = OPEN_ALWAYS;
+  } else  /* no _O_CREAT */ {
+    if (oflag & _O_TRUNC)
+      creation = TRUNCATE_EXISTING;
+    else
+      creation = OPEN_EXISTING;
+  }
+
+  switch( pmode & 0x70 ) {
+    case _SH_DENYRW:
+      sharing = 0L;
+      break;
+    case _SH_DENYWR:
+      sharing = FILE_SHARE_READ;
+      break;
+    case _SH_DENYRD:
+      sharing = FILE_SHARE_WRITE;
+      break;
+    case _SH_COMPAT:
+    case _SH_DENYNO:
+      sharing = FILE_SHARE_READ | FILE_SHARE_WRITE;
+      break;
+    default:
+      ERR("<-- -1 (Unhandled pmode 0x%x)\n", pmode);
+      return -1;
+  }
+
+  if (oflag & ~(_O_BINARY | _O_TRUNC | _O_EXCL | _O_CREAT | _O_RDWR | _O_WRONLY | _O_NOINHERIT))
+    WARN("unsupported oflag 0x%04x\n",oflag);
+
+  sa.nLength              = sizeof( SECURITY_ATTRIBUTES );
+  sa.lpSecurityDescriptor = NULL;
+  sa.bInheritHandle       = (ioflag & _O_NOINHERIT) ? FALSE : TRUE;
+
+  ret = (INT_PTR) CreateFileA(pszFile, ioflag, sharing, &sa, creation, FILE_ATTRIBUTE_NORMAL, NULL);
+
+  /* TRACE("<-- %d\n", ret); */
+
+  return ret;
+}
+
+static UINT sc_cb_read(INT_PTR hf, void *pv, UINT cb)
+{
+  DWORD num_read;
+  BOOL rslt;
+
+  /* TRACE("(hf == %d, pv == ^%p, cb == %u)\n", hf, pv, cb); */
+
+  rslt = ReadFile((HANDLE) hf, pv, cb, &num_read, NULL);
+
+
+  /* eof and failure both give "-1" return */
+  if ((! rslt) || ((cb > 0) && (num_read == 0))) {
+    /* TRACE("<-- -1\n"); */
+    return -1;
+  }
+
+  /* TRACE("<-- %lu\n", num_read); */
+  return num_read;
+}
+
+static UINT sc_cb_write(INT_PTR hf, void *pv, UINT cb)
+{
+  DWORD num_written;
+  /* BOOL rv; */
+
+  /* TRACE("(hf == %d, pv == ^%p, cb == %u)\n", hf, pv, cb); */
+
+  if ( /* (rv = */ WriteFile((HANDLE) hf, pv, cb, &num_written, NULL) /* ) */
+       && (num_written == cb)) {
+    /* TRACE("<-- %lu\n", num_written); */
+    return num_written;
+  } else {
+    /* TRACE("rv == %d, num_written == %lu, cb == %u\n", rv, num_written,cb); */
+    /* TRACE("<-- -1\n"); */
+    return -1;
+  }
+}
+
+static int sc_cb_close(INT_PTR hf)
+{
+  /* TRACE("(hf == %d)\n", hf); */
+
+  if (CloseHandle((HANDLE) hf))
+    return 0;
+  else
+    return -1;
+}
+
+static long sc_cb_lseek(INT_PTR hf, long dist, int seektype)
+{
+  DWORD ret;
+
+  /* TRACE("(hf == %d, dist == %ld, seektype == %d)\n", hf, dist, seektype); */
+
+  if (seektype < 0 || seektype > 2)
+    return -1;
+
+  if (((ret = SetFilePointer((HANDLE) hf, dist, NULL, seektype)) != INVALID_SET_FILE_POINTER) || !GetLastError()) {
+    /* TRACE("<-- %lu\n", ret); */
+    return ret;
+  } else {
+    /* TRACE("<-- -1\n"); */
+    return -1;
+  }
+}
+
+#define SIZEOF_MYSTERIO (MAX_PATH*3)
+
+/* FDICopy callbacks */
+
+static INT_PTR sc_FNNOTIFY_A(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
+{
+  FILE_IN_CABINET_INFO_A fici;
+  PSC_HSC_A phsc;
+  CABINET_INFO_A ci;
+  FILEPATHS_A fp;
+  UINT err;
+
+  CHAR mysterio[SIZEOF_MYSTERIO]; /* how big? undocumented! probably 256... */
+
+  memset(&(mysterio[0]), 0, SIZEOF_MYSTERIO);
+
+  TRACE("(fdint == %d, pfdin == ^%p)\n", fdint, pfdin);
+
+  if (pfdin && pfdin->pv && (*((void **) pfdin->pv) == (void *)SC_HSC_A_MAGIC))
+    phsc = (PSC_HSC_A) pfdin->pv;
+  else {
+    ERR("pv %p is not an SC_HSC_A.\n", (pfdin) ? pfdin->pv : NULL);
+    return -1;
+  }
+
+  switch (fdint) {
+  case fdintCABINET_INFO:
+    TRACE("Cabinet info notification\n");
+    /* TRACE("  Cabinet name: %s\n", debugstr_a(pfdin->psz1));
+    TRACE("  Cabinet disk: %s\n", debugstr_a(pfdin->psz2));
+    TRACE("  Cabinet path: %s\n", debugstr_a(pfdin->psz3));
+    TRACE("  Cabinet Set#: %d\n", pfdin->setID);
+    TRACE("  Cabinet Cab#: %d\n", pfdin->iCabinet); */
+    WARN("SPFILENOTIFY_CABINETINFO undocumented: guess implementation.\n");
+    ci.CabinetFile = &(phsc->most_recent_cabinet_name[0]);
+    ci.CabinetPath = pfdin->psz3;
+    ci.DiskName = pfdin->psz2;
+    ci.SetId = pfdin->setID;
+    ci.CabinetNumber = pfdin->iCabinet;
+    phsc->msghandler(phsc->context, SPFILENOTIFY_CABINETINFO, (UINT) &ci, 0);
+    return 0;
+  case fdintPARTIAL_FILE:
+    TRACE("Partial file notification\n");
+    /* TRACE("  Partial file name: %s\n", debugstr_a(pfdin->psz1)); */
+    return 0;
+  case fdintCOPY_FILE:
+    TRACE("Copy file notification\n");
+    TRACE("  File name: %s\n", debugstr_a(pfdin->psz1));
+    /* TRACE("  File size: %ld\n", pfdin->cb);
+    TRACE("  File date: %u\n", pfdin->date);
+    TRACE("  File time: %u\n", pfdin->time);
+    TRACE("  File attr: %u\n", pfdin->attribs); */
+    fici.NameInCabinet = pfdin->psz1;
+    fici.FileSize = pfdin->cb;
+    fici.Win32Error = 0;
+    fici.DosDate = pfdin->date;
+    fici.DosTime = pfdin->time;
+    fici.DosAttribs = pfdin->attribs;
+    memset(&(fici.FullTargetName[0]), 0, MAX_PATH);
+    err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEINCABINET,
+                           (UINT) &fici, (UINT) pfdin->psz1);
+    if (err == FILEOP_DOIT) {
+      TRACE("  Callback specified filename: %s\n", debugstr_a(&(fici.FullTargetName[0])));
+      if (!fici.FullTargetName[0]) {
+        WARN("  Empty return string causing abort.");
+        SetLastError(ERROR_PATH_NOT_FOUND);
+        return -1;
+      }
+      return sc_cb_open(&(fici.FullTargetName[0]), _O_BINARY | _O_CREAT | _O_WRONLY,  _S_IREAD | _S_IWRITE);
+    } else {
+      TRACE("  Callback skipped file.\n");
+      return 0;
+    }
+  case fdintCLOSE_FILE_INFO:
+    TRACE("Close file notification\n");
+    /* TRACE("  File name: %s\n", debugstr_a(pfdin->psz1));
+    TRACE("  Exec file? %s\n", (pfdin->cb) ? "Yes" : "No");
+    TRACE("  File hndl: %d\n", pfdin->hf); */
+    fp.Source = &(phsc->most_recent_cabinet_name[0]);
+    fp.Target = pfdin->psz1;
+    fp.Win32Error = 0;
+    fp.Flags = 0;
+    /* the following should be a fixme -- but it occurs too many times */
+    WARN("Should set file date/time/attribs (and execute files?)\n");
+    err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEEXTRACTED, (UINT) &fp, 0);
+    if (sc_cb_close(pfdin->hf))
+      WARN("_close failed.\n");
+    if (err) {
+      SetLastError(err);
+      return FALSE;
+    } else
+      return TRUE;
+  case fdintNEXT_CABINET:
+    TRACE("Next cabinet notification\n");
+    /* TRACE("  Cabinet name: %s\n", debugstr_a(pfdin->psz1));
+    TRACE("  Cabinet disk: %s\n", debugstr_a(pfdin->psz2));
+    TRACE("  Cabinet path: %s\n", debugstr_a(pfdin->psz3));
+    TRACE("  Cabinet Set#: %d\n", pfdin->setID);
+    TRACE("  Cabinet Cab#: %d\n", pfdin->iCabinet); */
+    ci.CabinetFile = pfdin->psz1;
+    ci.CabinetPath = pfdin->psz3;
+    ci.DiskName = pfdin->psz2;
+    ci.SetId = pfdin->setID;
+    ci.CabinetNumber = pfdin->iCabinet;
+    /* remember the new cabinet name */
+    strcpy(&(phsc->most_recent_cabinet_name[0]), pfdin->psz1);
+    err = phsc->msghandler(phsc->context, SPFILENOTIFY_NEEDNEWCABINET, (UINT) &ci, (UINT) &(mysterio[0]));
+    if (err) {
+      SetLastError(err);
+      return -1;
+    } else {
+      if (mysterio[0]) {
+        /* some easy paranoia.  no such carefulness exists on the wide API IIRC */
+        mysterio[SIZEOF_MYSTERIO - 1] = '\0';
+        strncpy(pfdin->psz3, &(mysterio[0]), 255);
+        mysterio[255] = '\0';
+      }
+      return 0;
+    }
+  default:
+    FIXME("Unknown notification type %d.\n", fdint);
+    return 0;
+  }
+}
+
+static INT_PTR sc_FNNOTIFY_W(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
+{
+  FILE_IN_CABINET_INFO_W fici;
+  PSC_HSC_W phsc;
+  CABINET_INFO_W ci;
+  FILEPATHS_W fp;
+  UINT err;
+  int len;
+
+  WCHAR mysterio[SIZEOF_MYSTERIO]; /* how big? undocumented! */
+  WCHAR buf[MAX_PATH], buf2[MAX_PATH];
+  CHAR charbuf[MAX_PATH];
+
+  memset(&(mysterio[0]), 0, SIZEOF_MYSTERIO * sizeof(WCHAR));
+  memset(&(buf[0]), 0, MAX_PATH * sizeof(WCHAR));
+  memset(&(buf2[0]), 0, MAX_PATH * sizeof(WCHAR));
+  memset(&(charbuf[0]), 0, MAX_PATH);
+
+  TRACE("(fdint == %d, pfdin == ^%p)\n", fdint, pfdin);
+
+  if (pfdin && pfdin->pv && (*((void **) pfdin->pv) == (void *)SC_HSC_W_MAGIC))
+    phsc = (PSC_HSC_W) pfdin->pv;
+  else {
+    ERR("pv %p is not an SC_HSC_W.\n", (pfdin) ? pfdin->pv : NULL);
+    return -1;
+  }
+
+  switch (fdint) {
+  case fdintCABINET_INFO:
+    TRACE("Cabinet info notification\n");
+    /* TRACE("  Cabinet name: %s\n", debugstr_a(pfdin->psz1));
+    TRACE("  Cabinet disk: %s\n", debugstr_a(pfdin->psz2));
+    TRACE("  Cabinet path: %s\n", debugstr_a(pfdin->psz3));
+    TRACE("  Cabinet Set#: %d\n", pfdin->setID);
+    TRACE("  Cabinet Cab#: %d\n", pfdin->iCabinet); */
+    WARN("SPFILENOTIFY_CABINETINFO undocumented: guess implementation.\n");
+    ci.CabinetFile = &(phsc->most_recent_cabinet_name[0]);
+    len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz3, -1, &(buf[0]), MAX_PATH);
+    if ((len > MAX_PATH) || (len <= 1))
+      buf[0] = '\0';
+    ci.CabinetPath = &(buf[0]);
+    len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz2, -1, &(buf2[0]), MAX_PATH);
+    if ((len > MAX_PATH) || (len <= 1))
+      buf2[0] = '\0';
+    ci.DiskName = &(buf2[0]);
+    ci.SetId = pfdin->setID;
+    ci.CabinetNumber = pfdin->iCabinet;
+    phsc->msghandler(phsc->context, SPFILENOTIFY_CABINETINFO, (UINT) &ci, 0);
+    return 0;
+  case fdintPARTIAL_FILE:
+    TRACE("Partial file notification\n");
+    /* TRACE("  Partial file name: %s\n", debugstr_a(pfdin->psz1)); */
+    return 0;
+  case fdintCOPY_FILE:
+    TRACE("Copy file notification\n");
+    TRACE("  File name: %s\n", debugstr_a(pfdin->psz1));
+    /* TRACE("  File size: %ld\n", pfdin->cb);
+    TRACE("  File date: %u\n", pfdin->date);
+    TRACE("  File time: %u\n", pfdin->time);
+    TRACE("  File attr: %u\n", pfdin->attribs); */
+    len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz1, -1, &(buf2[0]), MAX_PATH);
+    if ((len > MAX_PATH) || (len <= 1))
+      buf2[0] = '\0';
+    fici.NameInCabinet = &(buf2[0]);
+    fici.FileSize = pfdin->cb;
+    fici.Win32Error = 0;
+    fici.DosDate = pfdin->date;
+    fici.DosTime = pfdin->time;
+    fici.DosAttribs = pfdin->attribs;
+    memset(&(fici.FullTargetName[0]), 0, MAX_PATH * sizeof(WCHAR));
+    err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEINCABINET,
+                           (UINT) &fici, (UINT) pfdin->psz1);
+    if (err == FILEOP_DOIT) {
+      TRACE("  Callback specified filename: %s\n", debugstr_w(&(fici.FullTargetName[0])));
+      if (fici.FullTargetName[0]) {
+        len = strlenW(&(fici.FullTargetName[0])) + 1;
+        if ((len > MAX_PATH ) || (len <= 1))
+          return 0;
+        if (!WideCharToMultiByte(CP_ACP, 0, &(fici.FullTargetName[0]), len, &(charbuf[0]), MAX_PATH, 0, 0))
+          return 0;
+      } else {
+        WARN("Empty buffer string caused abort.\n");
+        SetLastError(ERROR_PATH_NOT_FOUND);
+        return -1;
+      }
+      return sc_cb_open(&(charbuf[0]), _O_BINARY | _O_CREAT | _O_WRONLY,  _S_IREAD | _S_IWRITE);
+    } else {
+      TRACE("  Callback skipped file.\n");
+      return 0;
+    }
+  case fdintCLOSE_FILE_INFO:
+    TRACE("Close file notification\n");
+    /* TRACE("  File name: %s\n", debugstr_a(pfdin->psz1));
+    TRACE("  Exec file? %s\n", (pfdin->cb) ? "Yes" : "No");
+    TRACE("  File hndl: %d\n", pfdin->hf); */
+    fp.Source = &(phsc->most_recent_cabinet_name[0]);
+    len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz1, -1, &(buf[0]), MAX_PATH);
+    if ((len > MAX_PATH) || (len <= 1))
+      buf[0] = '\0';
+    fp.Target = &(buf[0]);
+    fp.Win32Error = 0;
+    fp.Flags = 0;
+    /* a valid fixme -- but occurs too many times */
+    /* FIXME("Should set file date/time/attribs (and execute files?)\n"); */
+    err = phsc->msghandler(phsc->context, SPFILENOTIFY_FILEEXTRACTED, (UINT) &fp, 0);
+    if (sc_cb_close(pfdin->hf))
+      WARN("_close failed.\n");
+    if (err) {
+      SetLastError(err);
+      return FALSE;
+    } else
+      return TRUE;
+  case fdintNEXT_CABINET:
+    TRACE("Next cabinet notification\n");
+    /* TRACE("  Cabinet name: %s\n", debugstr_a(pfdin->psz1));
+    TRACE("  Cabinet disk: %s\n", debugstr_a(pfdin->psz2));
+    TRACE("  Cabinet path: %s\n", debugstr_a(pfdin->psz3));
+    TRACE("  Cabinet Set#: %d\n", pfdin->setID);
+    TRACE("  Cabinet Cab#: %d\n", pfdin->iCabinet); */
+    /* remember the new cabinet name */
+    len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz1, -1, &(phsc->most_recent_cabinet_name[0]), MAX_PATH);
+    if ((len > MAX_PATH) || (len <= 1))
+      phsc->most_recent_cabinet_name[0] = '\0';
+    ci.CabinetFile = &(phsc->most_recent_cabinet_name[0]);
+    len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz3, -1, &(buf[0]), MAX_PATH);
+    if ((len > MAX_PATH) || (len <= 1))
+      buf[0] = '\0';
+    ci.CabinetPath = &(buf[0]);
+    len = 1 + MultiByteToWideChar(CP_ACP, 0, pfdin->psz2, -1, &(buf2[0]), MAX_PATH);
+    if ((len > MAX_PATH) || (len <= 1))
+      buf2[0] = '\0';
+    ci.DiskName = &(buf2[0]);
+    ci.SetId = pfdin->setID;
+    ci.CabinetNumber = pfdin->iCabinet;
+    err = phsc->msghandler(phsc->context, SPFILENOTIFY_NEEDNEWCABINET, (UINT) &ci, (UINT) &(mysterio[0]));
+    if (err) {
+      SetLastError(err);
+      return -1;
+    } else {
+      if (mysterio[0]) {
+        len = strlenW(&(mysterio[0])) + 1;
+        if ((len > 255) || (len <= 1))
+          return 0;
+        if (!WideCharToMultiByte(CP_ACP, 0, &(mysterio[0]), len, pfdin->psz3, 255, 0, 0))
+          return 0;
+      }
+      return 0;
+    }
+  default:
+    FIXME("Unknown notification type %d.\n", fdint);
+    return 0;
+  }
+}
+
+/***********************************************************************
+ *             SetupIterateCabinetA (SETUPAPI.@)
+ */
+BOOL WINAPI SetupIterateCabinetA(PCSTR CabinetFile, DWORD Reserved,
+                                 PSP_FILE_CALLBACK_A MsgHandler, PVOID Context)
+{
+
+  SC_HSC_A my_hsc;
+  ERF erf;
+  CHAR pszCabinet[MAX_PATH], pszCabPath[MAX_PATH], *p;
+  DWORD fpnsize;
+  BOOL ret;
+
+
+  TRACE("(CabinetFile == %s, Reserved == %lu, MsgHandler == ^%p, Context == ^%p)\n",
+        debugstr_a(CabinetFile), Reserved, MsgHandler, Context);
+
+  if (! LoadCABINETDll()) 
+    return FALSE;
+
+  memset(&my_hsc, 0, sizeof(SC_HSC_A));
+  pszCabinet[0] = '\0';
+  pszCabPath[0] = '\0';
+
+  fpnsize = strlen(CabinetFile);
+  if (fpnsize >= MAX_PATH) {
+    SetLastError(ERROR_BAD_PATHNAME);
+    return FALSE;
+  }
+
+  fpnsize = GetFullPathNameA(CabinetFile, MAX_PATH, &(pszCabPath[0]), &p);
+  if (fpnsize > MAX_PATH) {
+    SetLastError(ERROR_BAD_PATHNAME);
+    return FALSE;
+  }
+
+  if (p) {
+    strcpy(pszCabinet, p);
+    *p = '\0';
+  } else {
+    strcpy(pszCabinet, CabinetFile);
+    pszCabPath[0] = '\0';
+  }
+
+  TRACE("path: %s, cabfile: %s\n", debugstr_a(pszCabPath), debugstr_a(pszCabinet));
+
+  /* remember the cabinet name */
+  strcpy(&(my_hsc.most_recent_cabinet_name[0]), pszCabinet);
+
+  my_hsc.magic = SC_HSC_A_MAGIC;
+  my_hsc.msghandler = MsgHandler;
+  my_hsc.context = Context;
+  my_hsc.hfdi = sc_FDICreate( sc_cb_alloc, sc_cb_free, sc_cb_open, sc_cb_read,
+                           sc_cb_write, sc_cb_close, sc_cb_lseek, cpuUNKNOWN, &erf );
+
+  if (!my_hsc.hfdi) return FALSE;
+
+  ret = ( sc_FDICopy(my_hsc.hfdi, pszCabinet, pszCabPath,
+                     0, sc_FNNOTIFY_A, NULL, &my_hsc)     ) ? TRUE : FALSE;
+
+  sc_FDIDestroy(my_hsc.hfdi);
+  return ret;
+}
+
+/***********************************************************************
+ *             SetupIterateCabinetW (SETUPAPI.@)
+ */
+BOOL WINAPI SetupIterateCabinetW(PCWSTR CabinetFile, DWORD Reserved,
+                                 PSP_FILE_CALLBACK_W MsgHandler, PVOID Context)
+{
+  CHAR CabinetFile_A[MAX_PATH];
+  unsigned int len;
+  SC_HSC_W my_hsc;
+  ERF erf;
+  CHAR pszCabinet[MAX_PATH], pszCabPath[MAX_PATH], *p;
+  DWORD fpnsize;
+  BOOL ret;
+
+  TRACE("(CabinetFile == %s, Reserved == %lu, MsgHandler == ^%p, Context == ^%p)\n",
+        debugstr_w(CabinetFile), Reserved, MsgHandler, Context);
+
+  if (!LoadCABINETDll())
+    return FALSE;
+
+  if (!CabinetFile) return FALSE;
+  if (!WideCharToMultiByte(CP_ACP, 0, CabinetFile, -1, CabinetFile_A, MAX_PATH, 0, 0))
+      return FALSE;
+
+  memset(&my_hsc, 0, sizeof(SC_HSC_W));
+  pszCabinet[0] = '\0';
+  pszCabPath[0] = '\0';
+
+  fpnsize = GetFullPathNameA(CabinetFile_A, MAX_PATH, &(pszCabPath[0]), &p);
+  if (fpnsize > MAX_PATH) {
+    SetLastError(ERROR_BAD_PATHNAME);
+    return FALSE;
+  }
+
+  if (p) {
+    strcpy(pszCabinet, p);
+    *p = '\0';
+  } else {
+    strcpy(pszCabinet, CabinetFile_A);
+    pszCabPath[0] = '\0';
+  }
+
+  TRACE("path: %s, cabfile: %s\n", debugstr_a(pszCabPath), debugstr_a(pszCabinet));
+
+  /* remember the cabinet name */
+  len = 1 + MultiByteToWideChar(CP_ACP, 0, pszCabinet, -1,
+             &(my_hsc.most_recent_cabinet_name[0]), MAX_PATH);
+  if (len > MAX_PATH)
+    return FALSE;
+  else if (len <= 1)
+    my_hsc.most_recent_cabinet_name[0] = '\0';
+  my_hsc.magic = SC_HSC_W_MAGIC;
+  my_hsc.msghandler = MsgHandler;
+  my_hsc.context = Context;
+  my_hsc.hfdi = sc_FDICreate( sc_cb_alloc, sc_cb_free, sc_cb_open, sc_cb_read,
+                              sc_cb_write, sc_cb_close, sc_cb_lseek, cpuUNKNOWN, &erf );
+
+  if (!my_hsc.hfdi) return FALSE;
+
+  ret = ( sc_FDICopy(my_hsc.hfdi, pszCabinet, pszCabPath,
+                     0, sc_FNNOTIFY_W, NULL, &my_hsc)     ) ? TRUE : FALSE;
+
+  sc_FDIDestroy(my_hsc.hfdi);
+  return ret;
+}
+
+
+/***********************************************************************
+ * DllMain
+ *
+ * PARAMS
+ *     hinstDLL    [I] handle to the DLL's instance
+ *     fdwReason   [I]
+ *     lpvReserved [I] reserved, must be NULL
+ *
+ * RETURNS
+ *     Success: TRUE
+ *     Failure: FALSE
+ */
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+    switch (fdwReason) {
+    case DLL_PROCESS_ATTACH:
+        DisableThreadLibraryCalls(hinstDLL);
+        break;
+    case DLL_PROCESS_DETACH:
+        UnloadCABINETDll();
+        break;
+    }
+
+    return TRUE;
+}
diff --git a/reactos/lib/setupapi/setupx16.h b/reactos/lib/setupapi/setupx16.h
new file mode 100644 (file)
index 0000000..a738212
--- /dev/null
@@ -0,0 +1,562 @@
+/*
+ * Copyright 2000 Andreas Mohr 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
+ */
+
+#ifndef __SETUPX16_H
+#define __SETUPX16_H
+
+#include "wine/windef16.h"
+
+typedef UINT16 HINF16;
+typedef UINT16 LOGDISKID16;
+typedef UINT16 VHSTR;
+
+#define LINE_LEN       256
+
+/* error codes stuff */
+
+typedef UINT16 RETERR16;
+#define OK             0
+#define IP_ERROR       (UINT16)100
+#define TP_ERROR       (UINT16)200
+#define VCP_ERROR      (UINT16)300
+#define GEN_ERROR      (UINT16)400
+#define DI_ERROR       (UINT16)500
+
+enum {
+       ERR_IP_INVALID_FILENAME = IP_ERROR+1,
+       ERR_IP_ALLOC_ERR,
+       ERR_IP_INVALID_SECT_NAME,
+       ERR_IP_OUT_OF_HANDLES,
+       ERR_IP_INF_NOT_FOUND,
+       ERR_IP_INVALID_INFFILE,
+       ERR_IP_INVALID_HINF,
+       ERR_IP_INVALID_FIELD,
+       ERR_IP_SECT_NOT_FOUND,
+       ERR_IP_END_OF_SECTION,
+       ERR_IP_PROFILE_NOT_FOUND,
+       ERR_IP_LINE_NOT_FOUND,
+       ERR_IP_FILEREAD,
+       ERR_IP_TOOMANYINFFILES,
+       ERR_IP_INVALID_SAVERESTORE,
+       ERR_IP_INVALID_INFTYPE
+};
+
+/****** virtual copy operations ******/
+
+typedef DWORD LPEXPANDVTBL;
+
+typedef struct {
+       DWORD           dwSoFar;
+       DWORD           dwTotal;
+} VCPPROGRESS, *LPVCPPROGRESS;
+
+typedef struct {
+       WORD            cbSize;
+       LOGDISKID16     ldid;
+       VHSTR           vhstrRoot;
+       VHSTR           vhstrVolumeLabel;
+       VHSTR           vhstrDiskName;
+       WORD            wVolumeTime;
+       WORD            wVolumeDate;
+       DWORD           dwSerialNumber;
+       WORD            fl;
+       LPARAM          lparamRef;
+
+       VCPPROGRESS     prgFileRead;
+       VCPPROGRESS     prgByteRead;
+
+       VCPPROGRESS     prgFileWrite;
+       VCPPROGRESS     prgByteWrite;
+} VCPDISKINFO, *LPVCPDISKINFO;
+
+typedef struct {
+       LOGDISKID16     ldid;
+       VHSTR           vhstrDir;
+       VHSTR           vhstrFileName;
+} VCPFILESPEC, *LPVCPFILESPEC;
+
+typedef struct {
+       UINT16          uiMDate;
+       UINT16          uiMTime;
+       UINT16          uiADate;
+       UINT16          uiATime;
+       UINT16          uiAttr;
+       DWORD           llenIn;
+       DWORD           llenOut;
+} VCPFATTR, *LPVCPFATTR;
+
+typedef struct {
+       UINT16          uDate;
+       UINT16          uTime;
+       DWORD           dwSize;
+} VCPFILESTAT, *LPVCPFILESTAT;
+
+typedef struct
+{
+       HFILE16         hFileSrc;
+       HFILE16         hFileDst;
+       VCPFATTR        fAttr;
+       WORD            dosError;
+       VHSTR           vhstrFileName;
+       WPARAM          vcpm;
+} VIRTNODEEX, *LPVIRTNODEEX;
+
+typedef struct {
+       WORD            cbSize;
+       VCPFILESPEC     vfsSrc;
+       VCPFILESPEC     vfsDst;
+       WORD            fl;
+       LPARAM          lParam;
+       LPEXPANDVTBL    lpExpandVtbl;
+       LPVIRTNODEEX    lpvnex;
+       VHSTR           vhstrDstFinalName;
+       VCPFILESTAT     vFileStat;
+} VIRTNODE, *LPVIRTNODE;
+
+typedef struct {
+       WORD            cbSize;
+       VCPPROGRESS     prgDiskRead;
+       VCPPROGRESS     prgFileRead;
+       VCPPROGRESS     prgByteRead;
+
+       VCPPROGRESS     prgDiskWrite;
+       VCPPROGRESS     prgFileWrite;
+       VCPPROGRESS     prgByteWrite;
+
+       LPVCPDISKINFO   lpvdiIn;
+       LPVCPDISKINFO   lpvdiOut;
+       LPVIRTNODE      lpvn;
+} VCPSTATUS, *LPVCPSTATUS;
+
+#define CNFL_BACKUP            0x0001
+#define CNFL_DELETEONFAILURE   0x0002
+#define CNFL_RENAMEONSUCCESS   0x0004
+#define CNFL_CONTINUATION      0x0008
+#define CNFL_SKIPPED           0x0010
+#define CNFL_IGNOREERRORS      0x0020
+#define CNFL_RETRYFILE         0x0040
+#define CNFL_COPIED            0x0080
+#define VNFL_UNIQUE            0x0000
+#define VNFL_MULTIPLEOK                0x0100
+#define VNFL_DESTROYOLD                0x0200
+#define VNFL_COPY              0x0000
+#define VNFL_DELETE            0x0800
+#define VNFL_RENAME            0x1000
+#define VNFL_NODE_TYPE         (VNFL_RENAME|VNFL_DELETE|VNFL_COPY)
+#define VNFL_CREATED           0x2000
+#define VNFL_REJECTED          0x4000
+#define VNFL_DEVICEINSTALLER   0x8000
+
+enum {
+       ERR_VCP_IOFAIL = VCP_ERROR+1,
+       ERR_VCP_STRINGTOOLONG,
+       ERR_VCP_NOMEM,
+       ERR_VCP_QUEUEFULL,
+       ERR_VCP_NOVHSTR,
+       ERR_VCP_OVERFLOW,
+       ERR_VCP_BADARG,
+       ERR_VCP_UNINIT,
+       ERR_VCP_NOTFOUND,
+       ERR_VCP_BUSY,
+       ERR_VCP_INTERRUPTED,
+       ERR_VCP_BADDEST,
+       ERR_VCP_SKIPPED,
+       ERR_VCP_IO,
+       ERR_VCP_LOCKED,
+       ERR_VCP_WRONGDISK,
+       ERR_VCP_CHANGEMODE,
+       ERR_VCP_LDDINVALID,
+       ERR_VCP_LDDFIND,
+       ERR_VCP_LDDUNINIT,
+       ERR_VCP_LDDPATH_INVALID,
+       ERR_VCP_NOEXPANSION,
+       ERR_VCP_NOTOPEN,
+       ERR_VCP_NO_DIGITAL_SIGNATURE_CATALOG,
+       ERR_VCP_NO_DIGITAL_SIGNATURE_FILE
+};
+
+#define VCPN_OK                0
+#define VCPN_PROCEED   0
+#define VCPN_ABORT     -1
+#define VCPN_RETRY     -2
+#define VCPN_IGNORE    -3
+#define VCPN_SKIP      -4
+#define VCPN_FORCE     -5
+#define VCPN_DEFER     -6
+#define VCPN_FAIL      -7
+#define VCPN_RETRYFILE -8
+
+#define VCPFL_ABANDON          0x00
+#define VCPFL_BACKUP           0x01
+#define VCPFL_COPY             0x02
+#define VCPFL_BACKUPANDCOPY    (VCPFL_BACKUP|VCPFL_COPY)
+#define VCPFL_INSPECIFIEDORDER 0x04
+#define VCPFL_DELETE           0x08
+#define VCPFL_RENAME           0x10
+#define VCPFL_ALL              (VCPFL_COPY|VCPFL_DELETE|VCPFL_RENAME)
+
+#define CFNL_BACKUP            0x0001
+#define CFNL_DELETEONFAILURE   0x0002
+#define CFNL_RENAMEONSUCCESS   0x0004
+#define CFNL_CONTINUATION      0x0008
+#define CFNL_SKIPPED           0x0010
+#define CFNL_IGNOREERRORS      0x0020
+#define CFNL_RETRYFILE         0x0040
+#define CFNL_COPIED            0x0080
+#define VFNL_MULTIPLEOK                0x0100
+#define VFNL_DESTROYOLD                0x0200
+#define VFNL_NOW               0x0400
+#define VFNL_COPY              0x0000
+#define VFNL_DELETE            0x0800
+#define VFNL_RENAME            0x1000
+#define VFNL_CREATED           0x2000
+#define VFNL_REJECTED          0x4000
+#define VCPM_DISKCLASS         0x01
+#define VCPM_DISKFIRST         0x0100
+#define VCPM_DISKLAST          0x01ff
+
+enum {
+       VCPM_DISKCREATEINFO = VCPM_DISKFIRST,
+       VCPM_DISKGETINFO,
+       VCPM_DISKDESTROYINFO,
+       VCPM_DISKPREPINFO,
+       VCPM_DISKENSURE,
+       VCPM_DISKPROMPT,
+       VCPM_DISKFORMATBEGIN,
+       VCPM_DISKFORMATTING,
+       VCPM_DISKFORMATEND
+};
+
+#define VCPM_FILEINCLASS       0x02
+#define VCPM_FILEOUTCLASS      0x03
+#define VCPM_FILEFIRSTIN       0x0200
+#define VCPM_FILEFIRSTOUT      0x0300
+#define VCPM_FILELAST          0x03ff
+
+enum {
+       VCPM_FILEOPENIN = VCPM_FILEFIRSTIN,
+       VCPM_FILEGETFATTR,
+       VCPM_FILECLOSEIN,
+       VCPM_FILECOPY,
+       VCPM_FILENEEDED,
+
+       VCPM_FILEOPENOUT = VCPM_FILEFIRSTOUT,
+       VCPM_FILESETFATTR,
+       VCPM_FILECLOSEOUT,
+       VCPM_FILEFINALIZE,
+       VCPM_FILEDELETE,
+       VCPM_FILERENAME
+};
+
+#define VCPM_NODECLASS         0x04
+#define VCPM_NODEFIRST         0x0400
+#define VCPM_NODELAST          0x04ff
+
+enum {
+       VCPM_NODECREATE = VCPM_NODEFIRST,
+       VCPM_NODEACCEPT,
+       VCPM_NODEREJECT,
+       VCPM_NODEDESTROY,
+       VCPM_NODECHANGEDESTDIR,
+       VCPM_NODECOMPARE
+};
+
+#define VCPM_TALLYCLASS                0x05
+#define VCPM_TALLYFIRST                0x0500
+#define VCPM_TALLYLAST         0x05ff
+
+enum {
+       VCPM_TALLYSTART = VCPM_TALLYFIRST,
+       VCPM_TALLYEND,
+       VCPM_TALLYFILE,
+       VCPM_TALLYDISK
+};
+
+#define VCPM_VERCLASS          0x06
+#define VCPM_VERFIRST          0x0600
+#define VCPM_VERLAST           0x06ff
+
+enum {
+       VCPM_VERCHECK = VCPM_VERFIRST,
+       VCPM_VERCHECKDONE,
+       VCPM_VERRESOLVECONFLICT
+};
+
+#define VCPM_VSTATCLASS                0x07
+#define VCPM_VSTATFIRST                0x0700
+#define VCPM_VSTATLAST         0x07ff
+
+enum {
+       VCPM_VSTATSTART = VCPM_VSTATFIRST,
+       VCPM_VSTATEND,
+       VCPM_VSTATREAD,
+       VCPM_VSTATWRITE,
+       VCPM_VSTATNEWDISK,
+       VCPM_VSTATCLOSESTART,
+       VCPM_VSTATCLOSEEND,
+       VCPM_VSTATBACKUPSTART,
+       VCPM_VSTATBACKUPEND,
+       VCPM_VSTATRENAMESTART,
+       VCPM_VSTATRENAMEEND,
+       VCPM_VSTATCOPYSTART,
+       VCPM_VSTATCOPYEND,
+       VCPM_VSTATDELETESTART,
+       VCPM_VSTATDELETEEND,
+       VCPM_VSTATPATHCHECKSTART,
+       VCPM_VSTATPATHCHECKEND,
+       VCPM_VSTATCERTIFYSTART,
+       VCPM_VSTATCERTIFYEND,
+       VCPM_VSTATUSERABORT,
+       VCPM_VSTATYIELD
+};
+
+#define VCPM_PATHCLASS         0x08
+#define VCPM_PATHFIRST         0x0800
+#define VCPM_PATHLAST          0x08ff
+
+enum {
+       VCPM_BUILDPATH = VCPM_PATHFIRST,
+       VCPM_UNIQUEPATH,
+       VCPM_CHECKPATH
+};
+
+#define VCPM_PATCHCLASS                0x09
+#define VCPM_PATCHFIRST                0x0900
+#define VCPM_PATCHLAST         0x09ff
+
+enum {
+       VCPM_FILEPATCHBEFORECPY = VCPM_PATCHFIRST,
+       VCPM_FILEPATCHAFTERCPY,
+       VCPM_FILEPATCHINFOPEN,
+       VCPM_FILEPATCHINFCLOSE
+};
+
+#define VCPM_CERTCLASS         0x0a
+#define VCPM_CERTFIRST         0x0a00
+#define VCPM_CERTLAST          0x0aff
+
+enum {
+       VCPM_FILECERTIFY = VCPM_CERTFIRST,
+       VCPM_FILECERTIFYWARN
+};
+
+typedef LRESULT (CALLBACK *VIFPROC)(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam, LPARAM lParam, LPARAM lparamRef);
+
+typedef int (CALLBACK *VCPENUMPROC)(LPVIRTNODE lpvn, LPARAM lparamRef);
+
+RETERR16 WINAPI VcpOpen16(VIFPROC vifproc, LPARAM lparamMsgRef);
+
+/* VcpQueueCopy flags */
+#define VNLP_SYSCRITICAL       0x0001
+#define VNLP_SETUPCRITICAL     0x0002
+#define VNLP_NOVERCHECK                0x0004
+#define VNLP_FORCETEMP         0x0008
+#define VNLP_IFEXISTS          0x0010
+#define VNLP_KEEPNEWER         0x0020
+#define VNLP_PATCHIFEXIST      0x0040
+#define VNLP_NOPATCH           0x0080
+#define VNLP_CATALOGCERT       0x0100
+#define VNLP_NEEDCERTIFY       0x0200
+#define VNLP_COPYIFEXISTS      0x0400
+
+RETERR16 WINAPI VcpQueueCopy16(
+       LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName,
+       LPCSTR lpszSrcDir, LPCSTR lpszDstDir,
+       LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst,
+       LPEXPANDVTBL lpExpandVtbl,
+       WORD fl, LPARAM lParam
+);
+RETERR16 VcpFlush16(WORD fl, LPCSTR lpszBackupDest);
+RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest);
+
+/* VcpExplain flags */
+enum {
+       VCPEX_SRC_DISK,
+       VCPEX_SRC_CABINET,
+       VCPEX_SRC_LOCN,
+       VCPEX_DST_LOCN,
+       VCPEX_SRC_FILE,
+       VCPEX_DST_FILE,
+       VCPEX_DST_FILE_FINAL,
+       VCPEX_DOS_ERROR,
+       VCPEX_MESSAGE,
+       VCPEX_DOS_SOLUTION,
+       VCPEX_SRC_FULL,
+       VCPEX_DST_FULL,
+       VCPEX_DST_FULL_FINAL
+};
+
+LPCSTR WINAPI VcpExplain16(LPVIRTNODE lpVn, DWORD dwWhat);
+
+/****** logical disk management ******/
+
+typedef struct _LOGDISKDESC_S { /* ldd */
+       WORD        cbSize;       /* struct size */
+       LOGDISKID16 ldid;         /* logical disk ID */
+       LPSTR       pszPath;      /* path this descriptor points to */
+       LPSTR       pszVolLabel;  /* volume label of the disk related to it */
+       LPSTR       pszDiskName;  /* name of this disk */
+       WORD        wVolTime;     /* modification time of volume label */
+       WORD        wVolDate;     /* modification date */
+       DWORD       dwSerNum;     /* serial number of disk */
+       WORD        wFlags;
+} LOGDISKDESC_S, *LPLOGDISKDESC;
+
+/** logical disk identifiers (LDID) **/
+
+/* predefined LDIDs */
+#define LDID_PREDEF_START      0x0001
+#define LDID_PREDEF_END                0x7fff
+
+/* registry-assigned LDIDs */
+#define LDID_VAR_START         0x7000
+#define LDID_VAR_END           0x7fff
+
+/* dynamically assigned LDIDs */
+#define LDID_ASSIGN_START      0x8000
+#define LDID_ASSIGN_END                0xbfff
+
+#define LDID_NULL              0
+#define LDID_ABSOLUTE          ((UINT)-1)
+#define LDID_SRCPATH           1               /* setup source path */
+#define LDID_SETUPTEMP         2               /* setup temp dir */
+#define LDID_UNINSTALL         3               /* uninstall dir */
+#define LDID_BACKUP            4               /* backup dir */
+#define LDID_SETUPSCRATCH      5               /* setup scratch dir */
+#define LDID_WIN               10              /* win dir */
+#define LDID_SYS               11              /* win system dir */
+#define LDID_IOS               12              /* win Iosubsys dir */
+#define LDID_CMD               13              /* win command dir */
+#define LDID_CPL               14              /* win control panel dir */
+#define LDID_PRINT             15              /* win printer dir */
+#define LDID_MAIL              16              /* win mail dir */
+#define LDID_INF               17              /* win inf dir */
+#define LDID_HELP              18              /* win help dir */
+#define LDID_WINADMIN          19              /* admin dir */
+#define LDID_FONTS             20              /* win fonts dir */
+#define LDID_VIEWERS           21              /* win viewers dir */
+#define LDID_VMM32             22              /* win VMM32 dir */
+#define LDID_COLOR             23              /* win color mngment dir */
+#define LDID_APPS              24              /* win apps dir */
+#define LDID_SHARED            25              /* win shared dir */
+#define LDID_WINBOOT           26              /* guaranteed win boot drive */
+#define LDID_MACHINE           27              /* machine specific files */
+#define LDID_HOST_WINBOOT      28
+#define LDID_BOOT              30              /* boot drive root dir */
+#define LDID_BOOT_HOST         31              /* boot drive host root dir */
+#define LDID_OLD_WINBOOT       32              /* root subdir */
+#define LDID_OLD_WIN           33              /* old windows dir */
+
+/* flags for GenInstall() */
+#define GENINSTALL_DO_FILES            1
+#define GENINSTALL_DO_INI              2
+#define GENINSTALL_DO_REG              4
+#define GENINSTALL_DO_INI2REG          8
+#define GENINSTALL_DO_CFGAUTO          16
+#define GENINSTALL_DO_LOGCONFIG                32
+#define GENINSTALL_DO_REGSRCPATH       64
+#define GENINSTALL_DO_PERUSER          128
+
+#define GEINISTALL_DO_INIREG           14
+#define GENINSTALL_DO_ALL              255
+
+/*
+ * flags for InstallHinfSection()
+ * 128 can be added, too. This means that the .inf file is provided by you
+ * instead of being a 32 bit file (i.e. Windows .inf file).
+ * In this case all files you install must be in the same dir
+ * as your .inf file on the install disk.
+ */
+#define HOW_NEVER_REBOOT               0
+#define HOW_ALWAYS_SILENT_REBOOT       1
+#define HOW_ALWAYS_PROMPT_REBOOT       2
+#define HOW_SILENT_REBOOT              3
+#define HOW_PROMPT_REBOOT              4
+
+/****** device installation stuff ******/
+
+#define MAX_CLASS_NAME_LEN     32
+#define MAX_DEVNODE_ID_LEN     256
+#define MAX_GUID_STR           50
+
+typedef struct _DEVICE_INFO
+{
+       UINT16              cbSize;
+       struct _DEVICE_INFO *lpNextDi;
+       char                szDescription[LINE_LEN];
+       DWORD               dnDevnode;
+       HKEY                hRegKey;
+       char                szRegSubkey[MAX_DEVNODE_ID_LEN];
+       char                szClassName[MAX_CLASS_NAME_LEN];
+       DWORD               Flags;
+       HWND16              hwndParent;
+       /*LPDRIVER_NODE*/ LPVOID      lpCompatDrvList;
+       /*LPDRIVER_NODE*/ LPVOID      lpClassDrvList;
+       /*LPDRIVER_NODE*/ LPVOID      lpSelectedDriver;
+       ATOM                atDriverPath;
+       ATOM                atTempInfFile;
+       HINSTANCE16         hinstClassInstaller;
+       HINSTANCE16         hinstClassPropProvidor;
+       HINSTANCE16         hinstDevicePropProvidor;
+       HINSTANCE16         hinstBasicPropProvidor;
+       FARPROC16           fpClassInstaller;
+       FARPROC16           fpClassEnumPropPages;
+       FARPROC16           fpDeviceEnumPropPages;
+       FARPROC16           fpEnumBasicProperties;
+       DWORD               dwSetupReserved;
+       DWORD               dwClassInstallReserved;
+       /*GENCALLBACKPROC*/ LPVOID     gicpGenInstallCallBack;
+
+       LPARAM              gicplParam;
+       UINT16              InfType;
+
+       HINSTANCE16         hinstPrivateProblemHandler;
+       FARPROC16           fpPrivateProblemHandler;
+       LPARAM              lpClassInstallParams;
+       struct _DEVICE_INFO *lpdiChildList;
+       DWORD               dwFlagsEx;
+       /*LPDRIVER_INFO*/ LPVOID       lpCompatDrvInfoList;
+       /*LPDRIVER_INFO*/ LPVOID      lpClassDrvInfoList;
+       char                szClassGUID[MAX_GUID_STR];
+} DEVICE_INFO16, *LPDEVICE_INFO16, **LPLPDEVICE_INFO16;
+
+
+extern void WINAPI GenFormStrWithoutPlaceHolders16(LPSTR,LPCSTR,HINF16);
+extern RETERR16 WINAPI IpOpen16(LPCSTR,HINF16 *);
+extern RETERR16 WINAPI IpClose16(HINF16);
+extern RETERR16 WINAPI CtlSetLdd16(LPLOGDISKDESC);
+extern RETERR16 WINAPI CtlGetLdd16(LPLOGDISKDESC);
+extern RETERR16 WINAPI CtlFindLdd16(LPLOGDISKDESC);
+extern RETERR16 WINAPI CtlAddLdd16(LPLOGDISKDESC);
+extern RETERR16 WINAPI CtlDelLdd16(LOGDISKID16);
+extern RETERR16 WINAPI CtlGetLddPath16(LOGDISKID16 ldid, LPSTR szPath);
+extern RETERR16 WINAPI GenInstall16(HINF16,LPCSTR,WORD);
+
+typedef struct tagLDD_LIST {
+        LPLOGDISKDESC pldd;
+        struct tagLDD_LIST *next;
+} LDD_LIST;
+
+#define INIT_LDD(ldd, LDID) \
+  do { \
+   memset(&(ldd), 0, sizeof(LOGDISKDESC_S)); \
+   (ldd).cbSize = sizeof(LOGDISKDESC_S); \
+   ldd.ldid = LDID; \
+  } while(0)
+
+#endif /* __SETUPX16_H */
diff --git a/reactos/lib/setupapi/setupx_main.c b/reactos/lib/setupapi/setupx_main.c
new file mode 100644 (file)
index 0000000..4f5b952
--- /dev/null
@@ -0,0 +1,736 @@
+/*
+ *      SETUPX library
+ *
+ *      Copyright 1998,2000  Andreas Mohr
+ *
+ * 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
+ *
+ * FIXME: Rather non-functional functions for now.
+ *
+ * See:
+ * http://www.geocities.com/SiliconValley/Network/5317/drivers.html
+ * http://willemer.de/informatik/windows/inf_info.htm (German)
+ * http://www.microsoft.com/ddk/ddkdocs/win98ddk/devinst_12uw.htm
+ * DDK: setupx.h
+ * http://mmatrix.tripod.com/customsystemfolder/infsysntaxfull.html
+ * http://www.rdrop.com/~cary/html/inf_faq.html
+ * http://support.microsoft.com/support/kb/articles/q194/6/40.asp
+ *
+ * Stuff tested with:
+ * - rs405deu.exe (German Acroread 4.05 setup)
+ * - ie5setup.exe
+ * - Netmeeting
+ *
+ * FIXME:
+ * - string handling is... weird ;) (buflen etc.)
+ * - memory leaks ?
+ * - separate that mess (but probably only when it's done completely)
+ *
+ * SETUPX consists of several parts with the following acronyms/prefixes:
+ * Di  device installer (devinst.c ?)
+ * Gen generic installer (geninst.c ?)
+ * Ip  .INF parsing (infparse.c)
+ * LDD logical device descriptor (ldd.c ?)
+ * LDID        logical device ID
+ * SU   setup (setup.c ?)
+ * Tp  text processing (textproc.c ?)
+ * Vcp virtual copy module (vcp.c ?)
+ * ...
+ *
+ * The SETUPX DLL is NOT thread-safe. That's why many installers urge you to
+ * "close all open applications".
+ * All in all the design of it seems to be a bit weak.
+ * Not sure whether my implementation of it is better, though ;-)
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "winerror.h"
+#include "wine/winuser16.h"
+#include "wownt32.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "setupapi.h"
+#include "setupx16.h"
+#include "setupapi_private.h"
+#include "winerror.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
+
+/***********************************************************************
+ *             SURegOpenKey (SETUPX.47)
+ */
+DWORD WINAPI SURegOpenKey( HKEY hkey, LPCSTR lpszSubKey, PHKEY retkey )
+{
+    FIXME("(%p,%s,%p), semi-stub.\n",hkey,debugstr_a(lpszSubKey),retkey);
+    return RegOpenKeyA( hkey, lpszSubKey, retkey );
+}
+
+/***********************************************************************
+ *             SURegQueryValueEx (SETUPX.50)
+ */
+DWORD WINAPI SURegQueryValueEx( HKEY hkey, LPSTR lpszValueName,
+                                LPDWORD lpdwReserved, LPDWORD lpdwType,
+                                LPBYTE lpbData, LPDWORD lpcbData )
+{
+    FIXME("(%p,%s,%p,%p,%p,%ld), semi-stub.\n",hkey,debugstr_a(lpszValueName),
+          lpdwReserved,lpdwType,lpbData,lpcbData?*lpcbData:0);
+    return RegQueryValueExA( hkey, lpszValueName, lpdwReserved, lpdwType,
+                               lpbData, lpcbData );
+}
+
+/*
+ * Returns pointer to a string list with the first entry being number
+ * of strings.
+ *
+ * Hmm. Should this be InitSubstrData(), GetFirstSubstr() and GetNextSubstr()
+ * instead?
+ */
+static LPSTR *SETUPX_GetSubStrings(LPSTR start, char delimiter)
+{
+    LPSTR p, q;
+    LPSTR *res = NULL;
+    DWORD count = 0;
+    int len;
+
+    p = start;
+
+    while (1)
+    {
+       /* find beginning of real substring */
+       while ( (*p == ' ') || (*p == '\t') || (*p == '"') ) p++;
+
+       /* find end of real substring */
+       q = p;
+       while ( (*q)
+            && (*q != ' ') && (*q != '\t') && (*q != '"')
+            && (*q != ';') && (*q != delimiter) ) q++;
+       if (q == p)
+           break;
+       len = (int)q - (int)p;
+
+       /* alloc entry for new substring in steps of 32 units and copy over */
+       if (count % 32 == 0)
+       { /* 1 for count field + current count + 32 */
+           if (res)
+               res = HeapReAlloc(GetProcessHeap(), 0, res, (1+count+32)*sizeof(LPSTR));
+           else
+               res = HeapAlloc(GetProcessHeap(), 0, (1+count+32)*sizeof(LPSTR));           
+       }
+       *(res+1+count) = HeapAlloc(GetProcessHeap(), 0, len+1);
+       strncpy(*(res+1+count), p, len);
+       (*(res+1+count))[len] = '\0';
+       count++;
+
+       /* we are still within last substring (before delimiter),
+        * so get out of it */
+       while ((*q) && (*q != ';') && (*q != delimiter)) q++;
+       if ((!*q) || (*q == ';'))
+           break;
+       p = q+1;
+    }
+
+    /* put number of entries at beginning of list */
+    *(DWORD *)res = count;
+    return res;
+}
+
+static void SETUPX_FreeSubStrings(LPSTR *substr)
+{
+    DWORD count = *(DWORD *)substr;
+    LPSTR *pStrings = substr+1;
+    DWORD n;
+
+    for (n=0; n < count; n++)
+       HeapFree(GetProcessHeap(), 0, *pStrings++);
+
+    HeapFree(GetProcessHeap(), 0, substr);
+}
+
+
+/***********************************************************************
+ *             InstallHinfSection (SETUPX.527)
+ *
+ * hwnd = parent window
+ * hinst = instance of SETUPX.DLL
+ * lpszCmdLine = e.g. "DefaultInstall 132 C:\MYINSTALL\MYDEV.INF"
+ * Here "DefaultInstall" is the .inf file section to be installed (optional).
+ * The 132 value is made of the HOW_xxx flags and sometimes 128 (-> setupx16.h).
+ *
+ * nCmdShow = nCmdShow of CreateProcess
+ */
+RETERR16 WINAPI InstallHinfSection16( HWND16 hwnd, HINSTANCE16 hinst, LPCSTR lpszCmdLine, INT16 nCmdShow)
+{
+    LPSTR *pSub;
+    DWORD count;
+    HINF16 hInf = 0;
+    RETERR16 res = OK, tmp;
+    WORD wFlags;
+    BOOL reboot = FALSE;
+
+    TRACE("(%04x, %04x, %s, %d);\n", hwnd, hinst, lpszCmdLine, nCmdShow);
+
+    pSub = SETUPX_GetSubStrings((LPSTR)lpszCmdLine, ' ');
+
+    count = *(DWORD *)pSub;
+    if (count < 2) /* invalid number of arguments ? */
+       goto end;
+    if (IpOpen16(*(pSub+count), &hInf) != OK)
+    {
+       res = ERROR_FILE_NOT_FOUND; /* yes, correct */
+       goto end;
+    }
+    if (VcpOpen16(NULL, 0))
+       goto end;
+    if (GenInstall16(hInf, *(pSub+count-2), GENINSTALL_DO_ALL) != OK)
+       goto end;
+    wFlags = atoi(*(pSub+count-1)) & ~128;
+    switch (wFlags)
+    {
+       case HOW_ALWAYS_SILENT_REBOOT:
+       case HOW_SILENT_REBOOT:
+           reboot = TRUE;
+           break;
+       case HOW_ALWAYS_PROMPT_REBOOT:
+       case HOW_PROMPT_REBOOT:
+            if (MessageBoxA(HWND_32(hwnd), "You must restart Wine before the new settings will take effect.\n\nDo you want to exit Wine now ?", "Systems Settings Change", MB_YESNO|MB_ICONQUESTION) == IDYES)
+                reboot = TRUE;
+           break;
+       default:
+           ERR("invalid flags %d !\n", wFlags);
+           goto end;
+    }
+
+    res = OK;
+end:
+    tmp = VcpClose16(VCPFL_ALL, NULL);
+    if (tmp != OK)
+       res = tmp;
+    tmp = IpClose16(hInf);
+    if (tmp != OK)
+       res = tmp;
+    SETUPX_FreeSubStrings(pSub);
+    if (reboot)
+    {
+       /* FIXME: we should have a means of terminating all wine + wineserver */
+       MESSAGE("Program or user told me to restart. Exiting Wine...\n");
+       ExitProcess(1);
+    }
+
+    return res;
+}
+
+typedef struct
+{
+    LPCSTR RegValName;
+    LPCSTR StdString; /* fallback string; sub dir of windows directory */
+} LDID_DATA;
+
+static const LDID_DATA LDID_Data[34] =
+{
+    { /* 0 (LDID_NULL) -- not defined */
+       NULL,
+       NULL
+    },
+    { /* 1 (LDID_SRCPATH) = source of installation. hmm, what to do here ? */
+       "SourcePath", /* hmm, does SETUPX have to care about updating it ?? */
+       NULL
+    },
+    { /* 2 (LDID_SETUPTEMP) = setup temp dir */
+       "SetupTempDir",
+       NULL
+    },
+    { /* 3 (LDID_UNINSTALL) = uninstall backup dir */
+       "UninstallDir",
+       NULL
+    },
+    { /* 4 (LDID_BACKUP) = backup dir */
+       "BackupDir",
+       NULL
+    },
+    { /* 5 (LDID_SETUPSCRATCH) = setup scratch dir */
+       "SetupScratchDir",
+       NULL
+    },
+    { /* 6 -- not defined */
+       NULL,
+       NULL
+    },
+    { /* 7 -- not defined */
+       NULL,
+       NULL
+    },
+    { /* 8 -- not defined */
+       NULL,
+       NULL
+    },
+    { /* 9 -- not defined */
+       NULL,
+       NULL
+    },
+    { /* 10 (LDID_WIN) = windows dir */
+       "WinDir",
+        ""
+    },
+    { /* 11 (LDID_SYS) = system dir */
+       "SysDir",
+       NULL /* call GetSystemDirectory() instead */
+    },
+    { /* 12 (LDID_IOS) = IOSubSys dir */
+        NULL, /* FIXME: registry string ? */
+       "SYSTEM\\IOSUBSYS"
+    },
+    { /* 13 (LDID_CMD) = COMMAND dir */
+       NULL, /* FIXME: registry string ? */
+       "COMMAND"
+    },
+    { /* 14 (LDID_CPL) = control panel dir */
+       NULL,
+       ""
+    },
+    { /* 15 (LDID_PRINT) = windows printer dir */
+       NULL,
+       "SYSTEM" /* correct ?? */
+    },
+    { /* 16 (LDID_MAIL) = destination mail dir */
+       NULL,
+       ""
+    },
+    { /* 17 (LDID_INF) = INF dir */
+       "SetupScratchDir", /* correct ? */
+       "INF"
+    },
+    { /* 18 (LDID_HELP) = HELP dir */
+       NULL, /* ??? */
+       "HELP"
+    },
+    { /* 19 (LDID_WINADMIN) = Admin dir */
+       "WinAdminDir",
+       ""
+    },
+    { /* 20 (LDID_FONTS) = Fonts dir */
+       NULL, /* ??? */
+       "FONTS"
+    },
+    { /* 21 (LDID_VIEWERS) = Viewers */
+       NULL, /* ??? */
+       "SYSTEM\\VIEWERS"
+    },
+    { /* 22 (LDID_VMM32) = VMM32 dir */
+       NULL, /* ??? */
+       "SYSTEM\\VMM32"
+    },
+    { /* 23 (LDID_COLOR) = ICM dir */
+       "ICMPath",
+       "SYSTEM\\COLOR"
+    },
+    { /* 24 (LDID_APPS) = root of boot drive ? */
+       "AppsDir",
+       "C:\\"
+    },
+    { /* 25 (LDID_SHARED) = shared dir */
+       "SharedDir",
+       ""
+    },
+    { /* 26 (LDID_WINBOOT) = Windows boot dir */
+       "WinBootDir",
+       ""
+    },
+    { /* 27 (LDID_MACHINE) = machine specific files */
+       "MachineDir",
+       NULL
+    },
+    { /* 28 (LDID_HOST_WINBOOT) = Host Windows boot dir */
+       "HostWinBootDir",
+       NULL
+    },
+    { /* 29 -- not defined */
+       NULL,
+       NULL
+    },
+    { /* 30 (LDID_BOOT) = Root of boot drive */
+       "BootDir",
+       NULL
+    },
+    { /* 31 (LDID_BOOT_HOST) = Root of boot drive host */
+       "BootHost",
+       NULL
+    },
+    { /* 32 (LDID_OLD_WINBOOT) = subdir of root */
+       "OldWinBootDir",
+       NULL
+    },
+    { /* 33 (LDID_OLD_WIN) = old win dir */
+       "OldWinDir",
+       NULL
+    }
+    /* the rest (34-38) isn't too interesting, so I'll forget about it */
+};
+
+/*
+ * LDD  == Logical Device Descriptor
+ * LDID == Logical Device ID
+ *
+ * The whole LDD/LDID business might go into a separate file named
+ * ldd.c.
+ * At the moment I don't know what the hell these functions are really doing.
+ * That's why I added reporting stubs.
+ * The only thing I do know is that I need them for the LDD/LDID infrastructure.
+ * That's why I implemented them in a way that's suitable for my purpose.
+ */
+static LDD_LIST *pFirstLDD = NULL;
+
+static BOOL std_LDDs_done = FALSE;
+
+void SETUPX_CreateStandardLDDs(void)
+{
+    HKEY hKey = 0;
+    WORD n;
+    DWORD type, len;
+    LOGDISKDESC_S ldd;
+    char buffer[MAX_PATH];
+
+    /* has to be here, otherwise loop */
+    std_LDDs_done = TRUE;
+
+    RegOpenKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup", &hKey);
+
+    for (n=0; n < sizeof(LDID_Data)/sizeof(LDID_DATA); n++)
+    {
+       buffer[0] = '\0';
+
+       len = MAX_PATH;
+       if ( (hKey) && (LDID_Data[n].RegValName)
+       &&   (RegQueryValueExA(hKey, LDID_Data[n].RegValName,
+               NULL, &type, buffer, &len) == ERROR_SUCCESS)
+       &&   (type == REG_SZ) )
+       {
+           TRACE("found value '%s' for LDID %d\n", buffer, n);
+       }
+       else
+        switch(n)
+       {
+           case LDID_SRCPATH:
+               FIXME("LDID_SRCPATH: what exactly do we have to do here ?\n");
+               strcpy(buffer, "X:\\FIXME");
+               break;
+           case LDID_SYS:
+               GetSystemDirectoryA(buffer, MAX_PATH);
+               break;
+           case LDID_APPS:
+           case LDID_MACHINE:
+           case LDID_HOST_WINBOOT:
+           case LDID_BOOT:
+           case LDID_BOOT_HOST:
+               strcpy(buffer, "C:\\");
+               break;
+           default:
+               if (LDID_Data[n].StdString)
+               {
+                   DWORD len = GetWindowsDirectoryA(buffer, MAX_PATH);
+                   LPSTR p;
+                   p = buffer + len;
+                   *p++ = '\\';
+                   strcpy(p, LDID_Data[n].StdString);
+               }
+               break;
+        }
+       if (buffer[0])
+       {
+           INIT_LDD(ldd, n);
+           ldd.pszPath = buffer;
+           TRACE("LDID %d -> '%s'\n", ldd.ldid, ldd.pszPath);
+           CtlSetLdd16(&ldd);
+       }
+    }
+    if (hKey) RegCloseKey(hKey);
+}
+
+/***********************************************************************
+ * CtlDelLdd           (SETUPX.37)
+ *
+ * RETURN
+ *   ERR_VCP_LDDINVALID if ldid < LDID_ASSIGN_START.
+ */
+RETERR16 SETUPX_DelLdd(LOGDISKID16 ldid)
+{
+    LDD_LIST *pCurr, *pPrev = NULL;
+
+    TRACE("(%d)\n", ldid);
+
+    if (!std_LDDs_done)
+       SETUPX_CreateStandardLDDs();
+
+    if (ldid < LDID_ASSIGN_START)
+       return ERR_VCP_LDDINVALID;
+
+    pCurr = pFirstLDD;
+    /* search until we find the appropriate LDD or hit the end */
+    while ((pCurr != NULL) && (ldid > pCurr->pldd->ldid))
+    {
+        pPrev = pCurr;
+        pCurr = pCurr->next;
+    }
+    if ( (pCurr == NULL) /* hit end of list */
+      || (ldid != pCurr->pldd->ldid) )
+       return ERR_VCP_LDDFIND; /* correct ? */
+
+    /* ok, found our victim: eliminate it */
+
+    if (pPrev)
+       pPrev->next = pCurr->next;
+
+    if (pCurr == pFirstLDD)
+       pFirstLDD = NULL;
+    HeapFree(GetProcessHeap(), 0, pCurr);
+
+    return OK;
+}
+
+/***********************************************************************
+ *             CtlDelLdd (SETUPX.37)
+ */
+RETERR16 WINAPI CtlDelLdd16(LOGDISKID16 ldid)
+{
+    FIXME("(%d); - please report this!\n", ldid);
+    return SETUPX_DelLdd(ldid);
+}
+
+/***********************************************************************
+ * CtlFindLdd          (SETUPX.35)
+ *
+ * doesn't check pldd ptr validity: crash (W98SE)
+ *
+ * RETURN
+ *   ERR_VCP_LDDINVALID if pldd->cbSize != structsize
+ *   1 in all other cases ??
+ *
+ */
+RETERR16 WINAPI CtlFindLdd16(LPLOGDISKDESC pldd)
+{
+    LDD_LIST *pCurr, *pPrev = NULL;
+
+    TRACE("(%p)\n", pldd);
+
+    if (!std_LDDs_done)
+       SETUPX_CreateStandardLDDs();
+
+    if (pldd->cbSize != sizeof(LOGDISKDESC_S))
+        return ERR_VCP_LDDINVALID;
+
+    pCurr = pFirstLDD;
+    /* search until we find the appropriate LDD or hit the end */
+    while ((pCurr != NULL) && (pldd->ldid > pCurr->pldd->ldid))
+    {
+       pPrev = pCurr;
+       pCurr = pCurr->next;
+    }
+    if ( (pCurr == NULL) /* hit end of list */
+      || (pldd->ldid != pCurr->pldd->ldid) )
+       return ERR_VCP_LDDFIND; /* correct ? */
+
+    memcpy(pldd, pCurr->pldd, pldd->cbSize);
+    /* hmm, we probably ought to strcpy() the string ptrs here */
+
+    return 1; /* what is this ?? */
+}
+
+/***********************************************************************
+ * CtlSetLdd                   (SETUPX.33)
+ *
+ * Set an LDD entry.
+ *
+ * RETURN
+ *   ERR_VCP_LDDINVALID if pldd.cbSize != sizeof(LOGDISKDESC_S)
+ *
+ */
+RETERR16 WINAPI CtlSetLdd16(LPLOGDISKDESC pldd)
+{
+    LDD_LIST *pCurr, *pPrev = NULL;
+    LPLOGDISKDESC pCurrLDD;
+    HANDLE heap;
+    BOOL is_new = FALSE;
+
+    TRACE("(%p)\n", pldd);
+
+    if (!std_LDDs_done)
+       SETUPX_CreateStandardLDDs();
+
+    if (pldd->cbSize != sizeof(LOGDISKDESC_S))
+       return ERR_VCP_LDDINVALID;
+
+    heap = GetProcessHeap();
+    pCurr = pFirstLDD;
+    /* search until we find the appropriate LDD or hit the end */
+    while ((pCurr != NULL) && (pldd->ldid > pCurr->pldd->ldid))
+    {
+        pPrev = pCurr;
+        pCurr = pCurr->next;
+    }
+    if (!pCurr || pldd->ldid != pCurr->pldd->ldid)
+    {
+       is_new = TRUE;
+        pCurr = HeapAlloc(heap, 0, sizeof(LDD_LIST));
+        pCurr->pldd = HeapAlloc(heap, 0, sizeof(LOGDISKDESC_S));
+        pCurr->next = NULL;
+        pCurrLDD = pCurr->pldd;
+    }
+    else
+    {
+        pCurrLDD = pCurr->pldd;
+       if (pCurrLDD->pszPath)          HeapFree(heap, 0, pCurrLDD->pszPath);
+       if (pCurrLDD->pszVolLabel)      HeapFree(heap, 0, pCurrLDD->pszVolLabel);
+       if (pCurrLDD->pszDiskName)      HeapFree(heap, 0, pCurrLDD->pszDiskName);
+    }
+
+    memcpy(pCurrLDD, pldd, sizeof(LOGDISKDESC_S));
+
+    if (pldd->pszPath)
+    {
+        pCurrLDD->pszPath = HeapAlloc( heap, 0, strlen(pldd->pszPath)+1 );
+        strcpy( pCurrLDD->pszPath, pldd->pszPath );
+    }
+    if (pldd->pszVolLabel)
+    {
+        pCurrLDD->pszVolLabel = HeapAlloc( heap, 0, strlen(pldd->pszVolLabel)+1 );
+        strcpy( pCurrLDD->pszVolLabel, pldd->pszVolLabel );
+    }
+    if (pldd->pszDiskName)
+    {
+        pCurrLDD->pszDiskName = HeapAlloc( heap, 0, strlen(pldd->pszDiskName)+1 );
+        strcpy( pCurrLDD->pszDiskName, pldd->pszDiskName );
+    }
+
+    if (is_new) /* link into list */
+    {
+        if (pPrev)
+       {
+           pCurr->next = pPrev->next;
+            pPrev->next = pCurr;
+       }
+       if (!pFirstLDD)
+           pFirstLDD = pCurr;
+    }
+
+    return OK;
+}
+
+
+/***********************************************************************
+ * CtlAddLdd           (SETUPX.36)
+ *
+ * doesn't check pldd ptr validity: crash (W98SE)
+ *
+ */
+static LOGDISKID16 ldid_to_add = LDID_ASSIGN_START;
+RETERR16 WINAPI CtlAddLdd16(LPLOGDISKDESC pldd)
+{
+    pldd->ldid = ldid_to_add++;
+    return CtlSetLdd16(pldd);
+}
+
+/***********************************************************************
+ * CtlGetLdd           (SETUPX.34)
+ *
+ * doesn't check pldd ptr validity: crash (W98SE)
+ * What the !@#$%&*( is the difference between CtlFindLdd() and CtlGetLdd() ??
+ *
+ * RETURN
+ *   ERR_VCP_LDDINVALID if pldd->cbSize != structsize
+ *
+ */
+static RETERR16 SETUPX_GetLdd(LPLOGDISKDESC pldd)
+{
+    LDD_LIST *pCurr, *pPrev = NULL;
+
+    if (!std_LDDs_done)
+       SETUPX_CreateStandardLDDs();
+
+    if (pldd->cbSize != sizeof(LOGDISKDESC_S))
+        return ERR_VCP_LDDINVALID;
+
+    pCurr = pFirstLDD;
+    /* search until we find the appropriate LDD or hit the end */
+    while ((pCurr != NULL) && (pldd->ldid > pCurr->pldd->ldid))
+    {
+        pPrev = pCurr;
+        pCurr = pCurr->next;
+    }
+    if (pCurr == NULL) /* hit end of list */
+       return ERR_VCP_LDDFIND; /* correct ? */
+
+    memcpy(pldd, pCurr->pldd, pldd->cbSize);
+    /* hmm, we probably ought to strcpy() the string ptrs here */
+
+    return OK;
+}
+
+/**********************************************************************/
+
+RETERR16 WINAPI CtlGetLdd16(LPLOGDISKDESC pldd)
+{
+    FIXME("(%p); - please report this!\n", pldd);
+    return SETUPX_GetLdd(pldd);
+}
+
+/***********************************************************************
+ *             CtlGetLddPath           (SETUPX.38)
+ *
+ * Gets the path of an LDD.
+ * No crash if szPath == NULL.
+ * szPath has to be at least MAX_PATH_LEN bytes long.
+ * RETURN
+ *   ERR_VCP_LDDUNINIT if LDD for LDID not found.
+ */
+RETERR16 WINAPI CtlGetLddPath16(LOGDISKID16 ldid, LPSTR szPath)
+{
+    TRACE("(%d, %p);\n", ldid, szPath);
+
+    if (szPath)
+    {
+       LOGDISKDESC_S ldd;
+       INIT_LDD(ldd, ldid);
+       if (CtlFindLdd16(&ldd) == ERR_VCP_LDDFIND)
+           return ERR_VCP_LDDUNINIT;
+       SETUPX_GetLdd(&ldd);
+        strcpy(szPath, ldd.pszPath);
+       TRACE("ret '%s' for LDID %d\n", szPath, ldid);
+    }
+    return OK;
+}
+
+/***********************************************************************
+ *             CtlSetLddPath           (SETUPX.508)
+ *
+ * Sets the path of an LDD.
+ * Creates LDD for LDID if not existing yet.
+ */
+RETERR16 WINAPI CtlSetLddPath16(LOGDISKID16 ldid, LPSTR szPath)
+{
+    LOGDISKDESC_S ldd;
+    TRACE("(%d, '%s');\n", ldid, szPath);
+
+    SetupSetDirectoryIdA( 0, ldid, szPath );
+    INIT_LDD(ldd, ldid);
+    ldd.pszPath = szPath;
+    return CtlSetLdd16(&ldd);
+}
diff --git a/reactos/lib/setupapi/stubs.c b/reactos/lib/setupapi/stubs.c
new file mode 100644 (file)
index 0000000..252c1ac
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * SetupAPI stubs
+ *
+ * Copyright 2000 James Hatheway
+ *
+ * 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 <stdarg.h>
+
+#include "wine/debug.h"
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "winreg.h"
+#include "setupapi.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
+
+/***********************************************************************
+ *             TPWriteProfileString (SETUPX.62)
+ */
+BOOL WINAPI TPWriteProfileString16( LPCSTR section, LPCSTR entry, LPCSTR string )
+{
+    FIXME( "%s %s %s: stub\n", debugstr_a(section), debugstr_a(entry), debugstr_a(string) );
+    return TRUE;
+}
+
+
+/***********************************************************************
+ *             suErrorToIds  (SETUPX.61)
+ */
+DWORD WINAPI suErrorToIds16( WORD w1, WORD w2 )
+{
+    FIXME( "%x %x: stub\n", w1, w2 );
+    return 0;
+}
+
+/***********************************************************************
+ *             SetupDiOpenClassRegKeyExW  (SETUPAPI.@)
+ *
+ * WINAPI in description not given
+ */
+HKEY WINAPI SetupDiOpenClassRegKeyExW(const GUID* class, REGSAM access, DWORD flags, PCWSTR machine, PVOID reserved)
+{
+  FIXME("\n");
+  return INVALID_HANDLE_VALUE;
+}
+
+/***********************************************************************
+ *             SetupDiGetClassDescriptionExW  (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetClassDescriptionExW (const GUID* class, PWSTR desc, DWORD size, PDWORD required, PCWSTR machine, PVOID reserved)
+{
+  FIXME("\n");
+  return FALSE;
+}
+
+/***********************************************************************
+ *             SetupDiClassNameFromGuidExW  (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiClassNameFromGuidExW (const GUID* class, PWSTR desc, DWORD size, PDWORD required, PCWSTR machine, PVOID reserved)
+{
+  FIXME("\n");
+  return FALSE;
+}
+
+/***********************************************************************
+ *             SetupDiBuildClassInfoListExW  (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiBuildClassInfoListExW(DWORD flags, LPGUID list, DWORD size, PDWORD required,  LPCWSTR  machine, PVOID reserved)
+{
+  FIXME("\n");
+  return FALSE;
+}
+
+/***********************************************************************
+ *             SetupDiGetDeviceInfoListDetailA  (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetDeviceInfoListDetailA(HDEVINFO devinfo, PSP_DEVINFO_LIST_DETAIL_DATA_A devinfo_data )
+{
+  FIXME("\n");
+  return FALSE;
+}
+
+/***********************************************************************
+ *             SetupDiGetDeviceInfoListDetailW  (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiGetDeviceInfoListDetailW(HDEVINFO devinfo, PSP_DEVINFO_LIST_DETAIL_DATA_W devinfo_data )
+{
+  FIXME("\n");
+  return FALSE;
+}
+
+/***********************************************************************
+ *             SetupDiCreateDeviceInfoListA (SETUPAPI.@)
+ */
+HDEVINFO WINAPI SetupDiCreateDeviceInfoList(const GUID *class, HWND parend)
+{
+  FIXME("\n");
+  return FALSE;
+}
+
+/***********************************************************************
+ *             SetupDiCreateDeviceInfoListExW  (SETUPAPI.@)
+ */
+HDEVINFO WINAPI SetupDiCreateDeviceInfoListExW(const GUID *class, HWND parend , PCWSTR machine, PVOID reserved)
+{
+  FIXME("\n");
+  return FALSE;
+}
+
+/***********************************************************************
+ *               (SETUPAPI.@)
+ *
+ * NO WINAPI in description given
+ */
+HDEVINFO WINAPI SetupDiGetClassDevsExA(const GUID *class, PCSTR filter, HWND parent, DWORD flags, HDEVINFO deviceset, PCSTR machine, PVOID reserved)
+{
+  FIXME("filter %s machine %s\n",debugstr_a(filter),debugstr_a(machine));
+  return FALSE;
+}
+
+/***********************************************************************
+ *               (SETUPAPI.@)
+ *
+ * NO WINAPI in description given
+ */
+HDEVINFO WINAPI SetupDiGetClassDevsExW(const GUID *class, PCWSTR filter, HWND parent, DWORD flags, HDEVINFO deviceset, PCWSTR machine, PVOID reserved)
+{
+  FIXME("\n");
+  return FALSE;
+}
+
+/***********************************************************************
+ *             SetupDiClassGuidsFromNameExW  (SETUPAPI.@)
+ */
+BOOL WINAPI SetupDiClassGuidsFromNameExW(LPCWSTR class, LPGUID list, DWORD size, PDWORD required,  LPCWSTR  machine, PVOID reserved)
+{
+  FIXME("\n");
+  return FALSE;
+}
+
+/***********************************************************************
+ *             CM_Connect_MachineW  (SETUPAPI.@)
+ */
+DWORD WINAPI CM_Connect_MachineW(LPCWSTR name, void * machine)
+{
+#define  CR_SUCCESS       0x00000000
+#define  CR_ACCESS_DENIED 0x00000033
+  FIXME("\n");
+  return  CR_ACCESS_DENIED;
+}
+
+/***********************************************************************
+ *             CM_Disconnect_Machine  (SETUPAPI.@)
+ */
+DWORD WINAPI CM_Disconnect_Machine(DWORD handle)
+{
+  FIXME("\n");
+  return  CR_SUCCESS;
+
+}
+
+/***********************************************************************
+ *             SetupCopyOEMInfA  (SETUPAPI.@)
+ */
+BOOL WINAPI SetupCopyOEMInfA(PCSTR sourceinffile, PCSTR sourcemedialoc,
+                           DWORD mediatype, DWORD copystyle, PSTR destinfname,
+                           DWORD destnamesize, PDWORD required,
+                           PSTR *destinfnamecomponent)
+{
+  FIXME("stub: source %s location %s ...\n",sourceinffile, sourcemedialoc);
+  return FALSE;
+}
+
+/***********************************************************************
+ *             InstallHinfSection  (SETUPAPI.@)
+ */
+void WINAPI InstallHinfSection(HWND hwnd, HINSTANCE handle, LPCSTR cmdline, INT show)
+{
+    FIXME("stub, hwnd %p, handle %p, cmdline %s\n", hwnd, handle, debugstr_a(cmdline));
+}
diff --git a/reactos/lib/setupapi/virtcopy.c b/reactos/lib/setupapi/virtcopy.c
new file mode 100644 (file)
index 0000000..47c79dc
--- /dev/null
@@ -0,0 +1,789 @@
+/*
+ * SetupAPI virtual copy operations
+ *
+ * Copyright 2001 Andreas Mohr
+ *
+ * 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
+ *
+ * FIXME: we now rely on builtin setupapi.dll for dialog resources.
+ *        This is bad ! We ought to have 16bit resource handling working.
+ */
+
+#include <stdarg.h>
+#include <string.h>
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winreg.h"
+#include "wownt32.h"
+#include "wingdi.h"
+#include "winnls.h"
+#include "setupapi.h"
+#include "setupx16.h"
+#include "setupapi_private.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(setupapi);
+
+static FARPROC16 VCP_Proc = NULL;
+static LPARAM VCP_MsgRef = 0;
+
+static BOOL VCP_opened = FALSE;
+
+static VCPSTATUS vcp_status;
+
+static HINSTANCE SETUPAPI_hInstance;
+
+static WORD VCP_Callback( LPVOID obj, UINT16 msg, WPARAM16 wParam, LPARAM lParam, LPARAM lParamRef )
+{
+    WORD args[8];
+    DWORD ret = OK;
+    if (VCP_Proc)
+    {
+        args[7] = HIWORD(obj);
+        args[6] = LOWORD(obj);
+        args[5] = msg;
+        args[4] = wParam;
+        args[3] = HIWORD(lParam);
+        args[2] = LOWORD(lParam);
+        args[1] = HIWORD(lParamRef);
+        args[0] = LOWORD(lParamRef);
+        WOWCallback16Ex( (DWORD)VCP_Proc, WCB16_PASCAL, sizeof(args), args, &ret );
+    }
+    return (WORD)ret;
+}
+
+/****************************** VHSTR management ******************************/
+
+/*
+ * This is a totally braindead implementation for now;
+ * I don't care about speed at all ! Size and implementation time
+ * is much more important IMHO. I could have created some sophisticated
+ * tree structure, but... what the hell ! :-)
+ */
+typedef struct {
+    DWORD refcount;
+    LPCSTR pStr;
+} VHSTR_STRUCT;
+
+static VHSTR_STRUCT **vhstrlist = NULL;
+static VHSTR vhstr_alloc = 0;
+
+#define VALID_VHSTR(x)         ((x < vhstr_alloc) && (vhstrlist[x]) && (vhstrlist[x]->refcount))
+
+/***********************************************************************
+ *             vsmStringAdd (SETUPX.207)
+ */
+VHSTR WINAPI vsmStringAdd16(LPCSTR lpszName)
+{
+    VHSTR n;
+    VHSTR index = 0xffff;
+    HANDLE heap;
+
+    TRACE("add string '%s'\n", lpszName);
+    /* search whether string already inserted */
+    TRACE("searching for existing string...\n");
+    for (n = 0; n < vhstr_alloc; n++)
+    {
+       if ((vhstrlist[n]) && (vhstrlist[n]->refcount))
+       {
+               TRACE("checking item: %d\n", n);
+           if (!strcmp(vhstrlist[n]->pStr, lpszName))
+           {
+               TRACE("found\n");
+               vhstrlist[n]->refcount++;
+               return n;
+           }
+       }
+    }
+
+    /* hmm, not found yet, let's insert it */
+    TRACE("inserting item\n");
+    for (n = 0; n < vhstr_alloc; n++)
+    {
+       if ((!(vhstrlist[n])) || (!(vhstrlist[n]->refcount)))
+       {
+           index = n;
+           break;
+       }
+    }
+    heap = GetProcessHeap();
+    if (n == vhstr_alloc) /* hmm, no free index found yet */
+    {
+       index = vhstr_alloc;
+       vhstr_alloc += 20;
+
+       if (vhstrlist)
+           vhstrlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, vhstrlist,
+                                       sizeof(VHSTR_STRUCT *) * vhstr_alloc);
+       else
+           vhstrlist = HeapAlloc(heap, HEAP_ZERO_MEMORY,
+                                       sizeof(VHSTR_STRUCT *) * vhstr_alloc);
+    }
+    if (index == 0xffff)
+       return 0xffff; /* failure */
+    if (!vhstrlist[index])
+       vhstrlist[index] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VHSTR_STRUCT));
+    vhstrlist[index]->refcount = 1;
+    vhstrlist[index]->pStr = HeapAlloc(heap, 0, strlen(lpszName)+1);
+    strcpy((LPSTR)vhstrlist[index]->pStr, lpszName);
+    return index;
+}
+
+/***********************************************************************
+ *             vsmStringDelete (SETUPX.206)
+ */
+INT16 WINAPI vsmStringDelete16(VHSTR vhstr)
+{
+    if (VALID_VHSTR(vhstr))
+    {
+       vhstrlist[vhstr]->refcount--;
+       if (!vhstrlist[vhstr]->refcount)
+       {
+           HeapFree(GetProcessHeap(), 0, (LPSTR)vhstrlist[vhstr]->pStr);
+           vhstrlist[vhstr]->pStr = NULL;
+       }
+       return VCPN_OK;
+    }
+
+    /* string not found */
+    return VCPN_FAIL;
+}
+
+/*
+ * vsmStringFind() - not exported from a standard SETUPX.DLL, it seems
+ */
+VHSTR WINAPI vsmStringFind16(LPCSTR lpszName)
+{
+    WORD n;
+    for (n = 0; n < vhstr_alloc; n++)
+       if ((vhstrlist[n]) && (vhstrlist[n]->refcount) && (!strcmp(vhstrlist[n]->pStr, lpszName)))
+           return n;
+    return 0xffff;
+}
+
+/***********************************************************************
+ *             vsmGetStringName (SETUPX.205)
+ *
+ * Pretty correct, I guess
+ */
+INT16 WINAPI vsmGetStringName16(VHSTR vhstr, LPSTR lpszBuffer, int cbBuffer)
+{
+    if (VALID_VHSTR(vhstr))
+    {
+       int len = strlen(vhstrlist[vhstr]->pStr)+1;
+       if (cbBuffer >= len)
+       {
+           if (lpszBuffer)
+               strcpy(lpszBuffer, vhstrlist[vhstr]->pStr);
+           return len;
+       }
+    }
+    return VCPN_FAIL;
+}
+
+/***********************************************************************
+ *             vsmStringCompare (not exported from a standard SETUPX.DLL, it seems)
+ */
+INT16 WINAPI vsmStringCompare16(VHSTR vhstrA, VHSTR vhstrB)
+{
+    if ((!VALID_VHSTR(vhstrA)) || (!VALID_VHSTR(vhstrB)))
+       return VCPN_FAIL; /* correct ? */
+    return strcmp(vhstrlist[vhstrA]->pStr, vhstrlist[vhstrB]->pStr);
+}
+
+/***********************************************************************
+ *             vsmGetStringRawName (SETUPX.208)
+ */
+LPCSTR WINAPI vsmGetStringRawName16(VHSTR vhstr)
+{
+    return (VALID_VHSTR(vhstr)) ? vhstrlist[vhstr]->pStr : NULL;
+}
+
+
+/***************************** VIRTNODE management ****************************/
+static LPVIRTNODE *pvnlist = NULL;
+static DWORD vn_num = 0;
+static DWORD vn_last = 0;
+
+RETERR16 VCP_VirtnodeCreate(LPVCPFILESPEC vfsSrc, LPVCPFILESPEC vfsDst, WORD fl, LPARAM lParam, LPEXPANDVTBL lpExpandVtbl)
+{
+    HANDLE heap;
+    LPVIRTNODE lpvn;
+    RETERR16 cbres;
+
+    while (vn_last < vn_num)
+    {
+       if (pvnlist[vn_last] == NULL)
+           break;
+       vn_last++;
+    }
+    heap = GetProcessHeap();
+    if (vn_last == vn_num)
+    {
+       vn_num += 20;
+       if (pvnlist)
+           pvnlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, pvnlist,
+                               sizeof(LPVIRTNODE *) * vn_num);
+       else
+           pvnlist = HeapAlloc(heap, HEAP_ZERO_MEMORY, 
+                               sizeof(LPVIRTNODE *) * vn_num);
+    }
+    pvnlist[vn_last] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VIRTNODE));
+    lpvn = pvnlist[vn_last];
+    vn_last++;
+
+    lpvn->cbSize = sizeof(VIRTNODE);
+
+    if (vfsSrc)
+        memcpy(&lpvn->vfsSrc, vfsSrc, sizeof(VCPFILESPEC));
+
+    if (vfsDst)
+        memcpy(&lpvn->vfsDst, vfsDst, sizeof(VCPFILESPEC));
+
+    lpvn->fl = fl;
+    lpvn->lParam = lParam;
+    lpvn->lpExpandVtbl = lpExpandVtbl;
+
+    lpvn->vhstrDstFinalName = 0xffff; /* FIXME: what is this ? */
+
+    cbres = VCP_Callback(lpvn, VCPM_NODECREATE, 0, 0, VCP_MsgRef);
+    lpvn->fl |= VFNL_CREATED;
+    cbres = VCP_Callback(lpvn, VCPM_NODEACCEPT, 0, 0, VCP_MsgRef);
+
+    return OK;
+}
+
+BOOL VCP_VirtnodeDelete(LPVIRTNODE lpvnDel)
+{
+    DWORD n;
+    RETERR16 cbres;
+
+    for (n = 0; n < vn_last; n++)
+    {
+       if (pvnlist[n] == lpvnDel)
+       {
+           cbres = VCP_Callback(lpvnDel, VCPM_NODEDESTROY, 0, 0, VCP_MsgRef);
+           HeapFree(GetProcessHeap(), 0, lpvnDel);
+           pvnlist[n] = NULL;
+           return TRUE;
+       }
+    }
+    return FALSE;
+}
+
+/***********************************************************************
+ *             VcpOpen (SETUPX.200)
+ *
+ * Sets up a virtual copy operation.
+ * This means that functions such as GenInstall()
+ * create a VIRTNODE struct for every file to be touched in a .INF file
+ * instead of actually touching the file.
+ * The actual copy/move/rename gets started when VcpClose or
+ * VcpFlush is called; several different callbacks are made
+ * (copy, rename, open, close, version conflicts, ...) on every file copied.
+ */
+RETERR16 WINAPI VcpOpen16(VIFPROC vifproc, LPARAM lparamMsgRef)
+{
+    TRACE("(%p, %08lx)\n", vifproc, lparamMsgRef);
+    if (VCP_opened)
+       return ERR_VCP_BUSY;
+
+    VCP_Proc = (FARPROC16)vifproc;
+    VCP_MsgRef = lparamMsgRef;
+
+    /* load SETUPAPI needed for dialog resources etc. */
+    SETUPAPI_hInstance = LoadLibraryA("setupapi.dll");
+    if (!SETUPAPI_hInstance)
+    {
+       ERR("Could not load sibling setupapi.dll\n");
+       return ERR_VCP_NOMEM;
+    }
+    VCP_opened = TRUE;
+    return OK;
+}
+
+/***********************************************************************
+ *             VcpQueueCopy            [SETUPX.13]
+ *
+ * lpExpandVtbl seems to be deprecated.
+ * fl are the CNFL_xxx and VNFL_xxx flags.
+ * lParam are the VNLP_xxx flags.
+ */
+RETERR16 WINAPI VcpQueueCopy16(
+       LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName,
+       LPCSTR lpszSrcDir, LPCSTR lpszDstDir,
+       LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst,
+       LPEXPANDVTBL lpExpandVtbl,
+       WORD fl, LPARAM lParam
+)
+{
+    VCPFILESPEC vfsSrc, vfsDst;
+
+    if (!VCP_opened)
+       return ERR_VCP_NOTOPEN;
+
+    TRACE("srcdir: %s, srcfile: %s, dstdir: %s, dstfile: %s\n",
+      lpszSrcDir, lpszSrcFileName, lpszDstDir, lpszDstFileName);
+
+    TRACE("ldidSrc == %d, ldidDst == %d\n", ldidSrc, ldidDst);
+
+    vfsSrc.ldid = ldidSrc;
+    vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir);
+    vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName);
+
+    vfsDst.ldid = ldidDst;
+    vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
+    vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
+
+    return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, fl, lParam,
+                   lpExpandVtbl);
+}
+
+/***********************************************************************
+ *             VcpQueueDelete          [SETUPX.17]
+ *
+ * Is lParamRef the same as lParam in VcpQueueCopy ?
+ * Damn docu !! Err... which docu ?
+ */
+RETERR16 WINAPI VcpQueueDelete16(
+       LPCSTR lpszDstFileName,
+       LPCSTR lpszDstDir,
+       LOGDISKID16 ldidDst,
+       LPARAM lParamRef
+)
+{
+    VCPFILESPEC vfsDst;
+
+    if (!VCP_opened)
+       return ERR_VCP_NOTOPEN;
+
+    vfsDst.ldid = ldidDst;
+    vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
+    vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
+
+    return VCP_VirtnodeCreate(NULL, &vfsDst, VNFL_DELETE, lParamRef, 0);
+}
+
+/***********************************************************************
+ *             VcpQueueRename          [SETUPX.204]
+ *
+ */
+RETERR16 WINAPI VcpQueueRename16(
+       LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName,
+       LPCSTR lpszSrcDir, LPCSTR lpszDstDir,
+       LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst,
+       LPARAM lParam
+)
+{
+    VCPFILESPEC vfsSrc, vfsDst;
+
+    if (!VCP_opened)
+       return ERR_VCP_NOTOPEN;
+
+    vfsSrc.ldid = ldidSrc;
+    vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir);
+    vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName);
+
+    vfsDst.ldid = ldidDst;
+    vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
+    vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
+
+    return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, VNFL_RENAME, lParam,
+                   0);
+}
+
+/***********************************************************************
+ *             VcpEnumFiles (SETUPX.@)
+ */
+INT16 WINAPI VcpEnumFiles(VCPENUMPROC vep, LPARAM lParamRef)
+{
+    WORD n;
+
+    for (n = 0; n < vn_last; n++)
+       vep(pvnlist[n], lParamRef);
+
+    return 0; /* FIXME: return value ? */
+}
+
+/***********************************************************************
+ *             VcpExplain (SETUPX.411)
+ */
+LPCSTR WINAPI VcpExplain16(LPVIRTNODE lpVn, DWORD dwWhat)
+{
+    static char buffer[MAX_PATH]; /* FIXME: is this how it's done ? */
+    buffer[0] = '\0';
+    switch (dwWhat)
+    {
+       case VCPEX_SRC_FULL:
+       case VCPEX_DST_FULL:
+           {
+               LPVCPFILESPEC lpvfs =
+                   (dwWhat == VCPEX_SRC_FULL) ?  &lpVn->vfsSrc : &lpVn->vfsDst;
+
+                /* if we have an ldid, use it, otherwise use the string */
+                /* from the vhstrlist array */
+               if (lpvfs->ldid != 0xffff)
+                  CtlGetLddPath16(lpvfs->ldid, buffer);
+                else
+                  strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrDir));
+
+                strcat(buffer, "\\");
+                strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrFileName));
+           }
+           break;
+       default:
+           FIXME("%ld unimplemented !\n", dwWhat);
+           strcpy(buffer, "Unknown error");
+           break;
+    }
+    return buffer;
+}
+
+RETERR16 VCP_CheckPaths(void)
+{
+    DWORD n;
+    LPVIRTNODE lpvn;
+    RETERR16 cbres;
+
+    cbres = VCP_Callback(&vcp_status, VCPM_VSTATPATHCHECKSTART, 0, 0, VCP_MsgRef);
+    for (n = 0; n < vn_num; n++)
+    {
+       lpvn = pvnlist[n];
+       if (!lpvn) continue;
+        /* FIXME: check paths of all VIRTNODEs here ! */
+       cbres = VCP_Callback(&lpvn->vfsDst, VCPM_CHECKPATH, 0, (DWORD)lpvn, VCP_MsgRef);
+    }
+    cbres = VCP_Callback(&vcp_status, VCPM_VSTATPATHCHECKEND, 0, 0, VCP_MsgRef);
+    return OK;
+}
+
+RETERR16 VCP_CopyFiles(void)
+{
+    char fn_src[MAX_PATH], fn_dst[MAX_PATH];
+    RETERR16 res = OK, cbres;
+    DWORD n;
+    LPVIRTNODE lpvn;
+
+    cbres = VCP_Callback(&vcp_status, VCPM_VSTATCOPYSTART, 0, 0, VCP_MsgRef);
+    for (n = 0; n < vn_num; n++)
+    {
+       lpvn = pvnlist[n];
+       if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_COPY)) continue;
+       /* FIXME: need to send VCPM_VSTATNEWDISK notification sometimes */
+        strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL));
+        strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL));
+       /* FIXME: what is this VCPM_VSTATWRITE here for ?
+        * I guess it's to signal successful destination file creation */
+       cbres = VCP_Callback(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef);
+
+       /* FIXME: need to do the file copy in small chunks for notifications */
+       TRACE("copying '%s' to '%s'\n", fn_src, fn_dst);
+        /* perform the file copy */
+        if (!(CopyFileA(fn_src, fn_dst,
+              (lpvn->fl & VNLP_COPYIFEXISTS) ? FALSE : TRUE )))
+        {
+            ERR("error copying, src: %s -> dst: %s\n", fn_src, fn_dst);
+           res = ERR_VCP_IOFAIL;
+        }
+
+       vcp_status.prgFileRead.dwSoFar++;
+       cbres = VCP_Callback(&vcp_status, VCPM_VSTATREAD, 0, 0, VCP_MsgRef);
+       vcp_status.prgFileWrite.dwSoFar++;
+       cbres = VCP_Callback(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef);
+    }
+
+    cbres = VCP_Callback(&vcp_status, VCPM_VSTATCOPYEND, 0, 0, VCP_MsgRef);
+    return res;
+}
+
+/***********************************************************************
+ *             VcpFlush - internal (not exported), but documented
+ *
+ * VNFL_NOW is used for VcpFlush.
+ */
+RETERR16 VcpFlush16(WORD fl, LPCSTR lpszBackupDest)
+{
+    return OK;
+}
+
+/***********************************************************************
+ *             VcpClose (SETUPX.201)
+ *
+ * Does callbacks (-> vifproc) with VCPM_VSTATCLOSESTART,
+ * VCPM_VSTATCLOSEEND.
+ *
+ * fl gets VCPFL_xxx flags to indicate what to do with the
+ * VIRTNODEs (files to mess with) created by e.g. GenInstall()
+ */
+RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest)
+{
+    RETERR16 res = OK;
+    WORD cbres = VCPN_PROCEED;
+
+    TRACE("(%04x, '%s')\n", fl, lpszBackupDest);
+
+    /* FIXME: needs to sort virtnodes in case VCPFL_INSPECIFIEDORDER
+     * is not set. This is done by VCP_Callback(VCPM_NODECOMPARE) */
+
+    TRACE("#1\n");
+    memset(&vcp_status, 0, sizeof(VCPSTATUS));
+    /* yes, vcp_status.cbSize is 0 ! */
+    TRACE("#2\n");
+    cbres = VCP_Callback(&vcp_status, VCPM_VSTATCLOSESTART, 0, 0, VCP_MsgRef);
+    TRACE("#3\n");
+
+    res = VCP_CheckPaths();
+    TRACE("#4\n");
+    if (res != OK)
+       return res; /* is this ok ? */
+    VCP_CopyFiles();
+
+    TRACE("#5\n");
+    cbres = VCP_Callback(&vcp_status, VCPM_VSTATCLOSEEND, 0, 0, VCP_MsgRef);
+    TRACE("#6\n");
+    VCP_Proc = NULL;
+    FreeLibrary(SETUPAPI_hInstance);
+    VCP_opened = FALSE;
+    return OK;
+}
+
+RETERR16 VCP_RenameFiles(void)
+{
+    char fn_src[MAX_PATH], fn_dst[MAX_PATH];
+    RETERR16 res = OK, cbres;
+    DWORD n;
+    LPVIRTNODE lpvn;
+
+    cbres = VCP_Callback(&vcp_status, VCPM_VSTATRENAMESTART, 0, 0, VCP_MsgRef);
+    for (n = 0; n < vn_num; n++)
+    {
+       lpvn = pvnlist[n];
+       if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_RENAME)) continue;
+        strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL));
+        strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL));
+       cbres = VCP_Callback(&lpvn->vfsDst, VCPM_FILEOPENOUT, 0, (LPARAM)lpvn, VCP_MsgRef);
+        if (!(MoveFileExA(fn_src, fn_dst, MOVEFILE_REPLACE_EXISTING)))
+           res = ERR_VCP_IOFAIL;
+       else
+           VCP_VirtnodeDelete(lpvn);
+    }
+    cbres = VCP_Callback(&vcp_status, VCPM_VSTATRENAMEEND, 0, 0, VCP_MsgRef);
+    return res;
+}
+
+/***********************************************************************
+ *             vcpDefCallbackProc (SETUPX.202)
+ */
+RETERR16 WINAPI vcpDefCallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
+                                       LPARAM lParam, LPARAM lParamRef)
+{
+    static int count = 0;
+    if (count < 10)
+        FIXME("(%p, %04x, %04x, %08lx, %08lx) - what to do here ?\n",
+               lpvObj, uMsg, wParam, lParam, lParamRef);
+    count++;
+    return OK;
+}
+
+/********************* point-and-click stuff from here ***********************/
+
+static HWND hDlgCopy = 0;
+static HKEY hKeyFiles = 0, hKeyRename = 0, hKeyConflict = 0;
+static char BackupDir[12];
+
+static INT_PTR CALLBACK VCP_UI_FileCopyDlgProc(HWND hWndDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
+{
+    INT_PTR retval = FALSE;
+
+    if (iMsg == WM_INITDIALOG)
+    {
+        ShowWindow(hWndDlg, SW_SHOWNORMAL);
+        UpdateWindow(hWndDlg);
+       retval = TRUE;
+    }
+    return retval;
+}
+
+BOOL VCP_UI_GetDialogTemplate(LPCVOID *template32)
+{
+    HRSRC hResInfo;
+    HGLOBAL hDlgTmpl32;
+
+    if (!(hResInfo = FindResourceA(SETUPAPI_hInstance, MAKEINTRESOURCEA(COPYFILEDLGORD), (LPSTR)RT_DIALOG)))
+       return FALSE;
+    if (!(hDlgTmpl32 = LoadResource(SETUPAPI_hInstance, hResInfo )) ||
+        !(*template32 = LockResource( hDlgTmpl32 )))
+       return FALSE;
+    return TRUE;
+}
+
+static LRESULT WINAPI
+VCP_UI_FileCopyWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+    if (uMsg != WM_CREATE)
+        return DefWindowProcA (hwnd, uMsg, wParam, lParam);
+
+    switch (uMsg)
+    {
+       case WM_CREATE:
+           return 0;
+       default:
+           FIXME("%04x: unhandled.\n", uMsg);
+    }
+
+    return 0;
+}
+
+void VCP_UI_RegisterProgressClass(void)
+{
+    static BOOL registered = FALSE;
+    WNDCLASSA wndClass;
+
+    if (registered)
+       return;
+
+    registered = TRUE;
+    ZeroMemory (&wndClass, sizeof(WNDCLASSA));
+    wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
+    wndClass.lpfnWndProc   = (WNDPROC)VCP_UI_FileCopyWndProc;
+    wndClass.cbClsExtra    = 0;
+    wndClass.cbWndExtra    = 0;
+    wndClass.hCursor       = LoadCursorA (0, (LPSTR)IDC_ARROW);
+    wndClass.hbrBackground = NULL;
+    wndClass.lpszClassName = "setupx_progress";
+
+    RegisterClassA (&wndClass);
+}
+
+RETERR16 VCP_UI_NodeCompare(LPVIRTNODE vn1, LPVIRTNODE vn2)
+{
+    LPCSTR file1, file2;
+    file1 = vsmGetStringRawName16(vn1->vfsSrc.vhstrFileName);
+    file2 = vsmGetStringRawName16(vn2->vfsSrc.vhstrFileName);
+    return (RETERR16)strcmp(file1, file2);
+}
+
+RETERR16 VCP_UI_CopyStart(void)
+{
+    LPCVOID template32;
+    char buf[256]; /* plenty */
+    BOOL dirty;
+    DWORD len;
+
+    /* FIXME: should be registered at DLL startup instead */
+    VCP_UI_RegisterProgressClass();
+    if (!(VCP_UI_GetDialogTemplate(&template32)))
+       return VCPN_FAIL;
+
+    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;
+    strcat(buf, REGPART_RENAME);
+    if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyRename))
+       return VCPN_FAIL;
+    if (RegCreateKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT, &hKeyConflict))
+       return VCPN_FAIL;
+    len = 1;
+    if (!(RegQueryValueExA(hKeyConflict, "Dirty", NULL, 0, (LPBYTE)&dirty, &len)))
+    {
+       /* FIXME: what does SETUPX.DLL do in this case ? */
+       MESSAGE("Warning: another program using SETUPX is already running ! Failed.\n");
+       return VCPN_FAIL;
+    }
+    dirty = TRUE;
+    if (RegSetValueExA(hKeyConflict, "Dirty", 0, REG_BINARY, (LPBYTE)&dirty, 1))
+       return VCPN_FAIL;
+    len = 12;
+    if (!(RegQueryValueExA(hKeyConflict, "BackupDirectory", NULL, 0, BackupDir, &len)))
+       strcpy(BackupDir, "VCM");
+
+    /* create C:\WINDOWS\[BackupDir] and set registry key to it */
+    GetWindowsDirectoryA(buf, 256);
+    strcat(buf, "\\");
+    strcat(buf, BackupDir);
+    if (!(CreateDirectoryA(buf, NULL)))
+       return VCPN_FAIL;
+    if (RegSetValueExA(hKeyConflict, "BackupDirectory", 0, REG_SZ, (LPBYTE)buf, strlen(buf)+1))
+       return VCPN_FAIL;
+    RegCloseKey(hKeyConflict);
+
+    return VCPN_OK;
+}
+
+/***********************************************************************
+ *             vcpUICallbackProc (SETUPX.213)
+ */
+RETERR16 WINAPI vcpUICallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
+                                       LPARAM lParam, LPARAM lParamRef)
+{
+    static int count = 0;
+    RETERR16 res = VCPN_OK, cbres;
+
+    if (count < 5)
+        FIXME("(%p, %04x, %04x, %08lx, %08lx) - semi-stub\n",
+               lpvObj, uMsg, wParam, lParam, lParamRef);
+    count++;
+    switch (uMsg)
+    {
+       /* unused messages, it seems */
+       case VCPM_DISKPREPINFO:
+
+       case VCPM_FILENEEDED:
+
+       case VCPM_NODECREATE:
+       case VCPM_NODEACCEPT:
+
+       case VCPM_VSTATCLOSESTART:
+       case VCPM_VSTATPATHCHECKSTART:
+       case VCPM_VSTATPATHCHECKEND:
+
+       case VCPM_CHECKPATH:
+           break;
+
+       /* the real stuff */
+       case VCPM_NODECOMPARE:
+           res = VCP_UI_NodeCompare((LPVIRTNODE)lpvObj, (LPVIRTNODE)lParam);
+           break;
+       case VCPM_VSTATREAD:
+           break;
+       case VCPM_VSTATWRITE:
+           cbres = VCP_Callback(&vcp_status, VCPM_DISKPREPINFO, 0, 0, VCP_MsgRef);
+           break;
+       case VCPM_VSTATCLOSEEND:
+           RegCloseKey(hKeyFiles);
+           RegCloseKey(hKeyRename);
+           RegDeleteKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT);
+           break;
+       case VCPM_VSTATCOPYSTART:
+           res = VCP_UI_CopyStart();
+           break;
+       case VCPM_VSTATCOPYEND:
+           if (hDlgCopy) DestroyWindow(hDlgCopy);
+           break;
+       default:
+           FIXME("unhandled msg 0x%04x\n", uMsg);
+    }
+    return res;
+}