- implement some more virtcopy (VCP) stuff
- add some setupx resources
- implement VHSTR functionality
- large parts of VCP callback handling
- merge setupapi and setupx stuff, especially resource handling
  gets rid of setupx debug channel; setupapi is the only one that remains

diff --git a/dlls/setupapi/.cvsignore b/dlls/setupapi/.cvsignore
index c582135..8df8ae4 100644
--- a/dlls/setupapi/.cvsignore
+++ b/dlls/setupapi/.cvsignore
@@ -1,3 +1,5 @@
 Makefile
+setupapi.res
 setupapi.spec.c
 setupx.spec.c
+virtcopy.glue.c
diff --git a/dlls/setupapi/Makefile.in b/dlls/setupapi/Makefile.in
index f2524b8..a77c806 100644
--- a/dlls/setupapi/Makefile.in
+++ b/dlls/setupapi/Makefile.in
@@ -13,7 +13,14 @@
 	devinst.c \
 	infparse.c \
 	setupx_main.c \
-	stubs.c
+	stubs.c \
+	virtcopy.c
+
+GLUE = \
+	virtcopy.c
+
+RC_SRCS= \
+	setupapi.rc
 
 @MAKE_DLL_RULES@
 
diff --git a/dlls/setupapi/infparse.c b/dlls/setupapi/infparse.c
index 26b97b9..2582dff 100644
--- a/dlls/setupapi/infparse.c
+++ b/dlls/setupapi/infparse.c
@@ -16,9 +16,9 @@
 #include "heap.h"
 #include "wine/winbase16.h"
 #include "setupx16.h"
-#include "setupx_private.h"
+#include "setupapi_private.h"
 
-DEFAULT_DEBUG_CHANNEL(setupx);
+DEFAULT_DEBUG_CHANNEL(setupapi);
 
 WORD InfNumEntries = 0;
 INF_FILE *InfList = NULL;
diff --git a/dlls/setupapi/setupapi.rc b/dlls/setupapi/setupapi.rc
new file mode 100644
index 0000000..5bc79e2
--- /dev/null
+++ b/dlls/setupapi/setupapi.rc
@@ -0,0 +1,34 @@
+/*
+ * Top level resource file for SETUPX
+ *
+ */
+
+#include "windef.h"
+#include "winuser.h"
+#include "winnls.h"
+#include "setupapi_private.h"
+
+/*--------------------- FIXME --------------------------
+ *
+ * These must be seperated into the language files
+ * and translated. The language 0,0 is a hack to get it
+ * loaded properly for all languages by pretending that
+ * they are neutral.
+ * The menus are not yet properly implemented.
+ * Don't localize it yet. (js)
+ */
+
+LANGUAGE 0,0
+
+COPYFILEDLGORD DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 208, 105
+STYLE DS_MODALFRAME | DS_SETFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION
+CAPTION "Copying Files..."
+FONT 8, "MS Sans Serif"
+BEGIN
+	PUSHBUTTON "Cancel", IDCANCEL, 79, 84, 50, 14, WS_CHILD | WS_VISIBLE | WS_TABSTOP
+	LTEXT "Source:", -1, 7, 7, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP
+	LTEXT "", SOURCESTRORD, 7, 18, 194, 11, WS_CHILD | WS_VISIBLE | WS_GROUP
+	LTEXT "Destination:", -1, 7, 30, 77, 11, WS_CHILD | WS_VISIBLE | WS_GROUP
+	LTEXT "", DESTSTRORD, 7, 41, 194, 22, WS_CHILD | WS_VISIBLE | WS_GROUP
+	CONTROL "", PROGRESSORD, "setupx_progress", 7, 63, 194, 13, WS_CHILD | WS_VISIBLE | WS_TABSTOP 
+END
diff --git a/dlls/setupapi/setupapi.spec b/dlls/setupapi/setupapi.spec
index 7cc43cd..f030647 100644
--- a/dlls/setupapi/setupapi.spec
+++ b/dlls/setupapi/setupapi.spec
@@ -1,11 +1,13 @@
 name setupapi
 type win32
+rsrc setupapi.res
 
+import user32.dll
 import advapi32.dll
 import kernel32.dll
 import ntdll.dll
 
-debug_channels (setupapi setupx)
+debug_channels (setupapi)
 
 # almost all functions are commented out for now. Ordinals are from setupapi.dll 4.0
 
diff --git a/dlls/setupapi/setupapi_private.h b/dlls/setupapi/setupapi_private.h
new file mode 100644
index 0000000..e2400ce
--- /dev/null
+++ b/dlls/setupapi/setupapi_private.h
@@ -0,0 +1,39 @@
+#ifndef __SETUPAPI_PRIVATE_H
+#define __SETUPAPI_PRIVATE_H
+
+#include "wine/windef16.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"
+
+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)
+
+typedef struct {
+    HINF16 hInf;
+    HFILE hInfFile;
+    LPSTR lpInfFileName;
+} INF_FILE;
+
+extern INF_FILE *InfList;
+extern WORD InfNumEntries;
+
+extern LPCSTR IP_GetFileName(HINF16 hInf);
+
+#endif /* __SETUPAPI_PRIVATE_H */
diff --git a/dlls/setupapi/setupx.spec b/dlls/setupapi/setupx.spec
index b43bc44..8a84816 100644
--- a/dlls/setupapi/setupx.spec
+++ b/dlls/setupapi/setupx.spec
@@ -14,11 +14,11 @@
 10   stub     IpGetIntField #(word ptr word ptr)
 11   stub     IpFindNextLine #(word ptr)
 12   stub     IpGetFileName #(word ptr word)
-13   stub     VcpQueueCopy #(str str str str word word ptr word long)
+13   pascal16 VcpQueueCopy(str str str str word word ptr word long) VcpQueueCopy16
 14   stub     NOAUTORUNWNDPROC
 15   stub     __DEBUGMSG
 16   stub     __ASSERTMSG
-17   stub     VcpQueueDelete #(str str word long)
+17   pascal16 VcpQueueDelete(str str word long) VcpQueueDelete16
 18   stub     TpOpenFile #(str ptr word)
 19   stub     TpCloseFile #(word)
 20   stub     TpOpenSection #(word ptr str word)
@@ -62,6 +62,7 @@
 61   stub     suErrorToIds #(word word)
 62   stub     TPWriteProfileString #(str str str)
 63   stub     SURPLSETUP
+# does SUSTORELDIDPATH set the path of an LDID in the registry ?
 64   stub     SUSTORELDIDPATH
 65   stub     WILDCARDSTRCMPI
 101  pascal16 GenInstall(word str word) GenInstall16
@@ -122,20 +123,20 @@
 160  stub     SXUPDATEDS
 170  stub     SUSETMEM
 171  stub     WriteDMFBootData #(word ptr word)
-200  pascal   VcpOpen(ptr str) VcpOpen16
+200  pascal   VcpOpen(segptr ptr) VcpOpen16
 201  pascal   VcpClose(word str) VcpClose16
-202  stub     vcpDefCallbackProc #(ptr word word long long)
+202  pascal16 vcpDefCallbackProc(ptr word word long long) vcpDefCallbackProc16
 203  stub     vcpEnumFiles #(ptr long)
-204  stub     VcpQueueRename #(str str str str word word long)
-205  stub     vsmGetStringName #(word ptr word)
-206  stub     vsmStringDelete #(word)
-207  stub     vsmStringAdd #(str)
-208  stub     vsmGetStringRawName #(word)
+204  pascal16 VcpQueueRename(str str str str word word long) VcpQueueRename16
+205  pascal16 vsmGetStringName(word ptr word) vsmGetStringName16
+206  pascal16 vsmStringDelete(word) vsmStringDelete16
+207  pascal16 vsmStringAdd(str) vsmStringAdd16
+208  pascal   vsmGetStringRawName(word) vsmGetStringRawName16
 209  stub     IpSaveRestorePosition #(word word)
 210  pascal16 IpGetProfileString(word str str ptr word) IpGetProfileString16
 211  stub     IpOpenEx #(str ptr word)
 212  stub     IpOpenAppendEx #(str word word)
-213  stub     vcpUICallbackProc #(ptr word word long long)
+213  pascal16 vcpUICallbackProc(ptr word word long long) vcpUICallbackProc16
 214  stub     VcpAddMRUPath #(str)
 300  stub     DiBuildCompatDrvList #(ptr)
 301  stub     DiBuildClassDrvList #(ptr)
diff --git a/dlls/setupapi/setupx16.h b/dlls/setupapi/setupx16.h
index 12292fa..2094050 100644
--- a/dlls/setupapi/setupx16.h
+++ b/dlls/setupapi/setupx16.h
@@ -5,6 +5,7 @@
 
 typedef UINT16 HINF16;
 typedef UINT16 LOGDISKID16;
+typedef UINT16 VHSTR;
 
 #define LINE_LEN	256
 
@@ -18,7 +19,7 @@
 #define GEN_ERROR	(UINT16)400
 #define DI_ERROR	(UINT16)500
 
-enum _IP_ERR {
+enum {
 	ERR_IP_INVALID_FILENAME = IP_ERROR+1,
 	ERR_IP_ALLOC_ERR,
 	ERR_IP_INVALID_SECT_NAME,
@@ -37,7 +38,113 @@
 	ERR_IP_INVALID_INFTYPE
 };
 
-enum _ERR_VCP {
+/****** 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,
@@ -65,6 +172,205 @@
 	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);
+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 */
@@ -198,8 +504,6 @@
 } DEVICE_INFO16, *LPDEVICE_INFO16, **LPLPDEVICE_INFO16;
 
 
-typedef LRESULT CALLBACK (*VIFPROC)(LPVOID lpvObj, UINT uMsg, WPARAM wParam, LPARAM lParam, LPARAM lparamRef);
-
 extern void WINAPI GenFormStrWithoutPlaceHolders16(LPSTR,LPCSTR,HINF16);
 extern RETERR16 WINAPI IpOpen16(LPCSTR,HINF16 *);
 extern RETERR16 WINAPI IpClose16(HINF16);
@@ -208,6 +512,7 @@
 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);
 
 #endif /* __SETUPX16_H */
diff --git a/dlls/setupapi/setupx_main.c b/dlls/setupapi/setupx_main.c
index e0ef93a..dde4101 100644
--- a/dlls/setupapi/setupx_main.c
+++ b/dlls/setupapi/setupx_main.c
@@ -47,12 +47,12 @@
 #include "winreg.h"
 #include "wine/winuser16.h"
 #include "setupx16.h"
-#include "setupx_private.h"
+#include "setupapi_private.h"
 #include "winerror.h"
 #include "heap.h"
 #include "debugtools.h"
 
-DEFAULT_DEBUG_CHANNEL(setupx);
+DEFAULT_DEBUG_CHANNEL(setupapi);
 
 /***********************************************************************
  *		SURegOpenKey
@@ -292,7 +292,7 @@
     LPSTR *pSub;
     DWORD count;
     HINF16 hInf = 0;
-    RETERR16 res = OK;
+    RETERR16 res = OK, tmp;
     WORD wFlags;
     BOOL reboot = FALSE;
     HMODULE hMod;
@@ -310,6 +310,8 @@
 	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;
@@ -338,7 +340,12 @@
     
     res = OK;
 end:
-    IpClose16(hInf);
+    tmp = VcpClose16(VCPFL_ALL, NULL);
+    if (tmp != OK)
+	res = tmp;
+    tmp = IpClose16(hInf);
+    if (tmp != OK)
+	res = tmp;
     SETUPX_FreeSubStrings(pSub);
     if (reboot)
     {
@@ -502,7 +509,7 @@
  * LDID == Logical Device ID
  *
  * The whole LDD/LDID business might go into a separate file named
- * ldd.c or logdevice.c.
+ * 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.
@@ -1048,28 +1055,6 @@
     TRACE("ret '%s'\n", szDst);
 }
 
-/***********************************************************************
- *		VcpOpen
- *
- * No idea what to do here.
- */
-RETERR16 WINAPI VcpOpen16(VIFPROC vifproc, LPARAM lparamMsgRef)
-{
-    FIXME("(%p, %08lx), stub.\n", vifproc, lparamMsgRef);
-    return OK;
-}
-
-/***********************************************************************
- *		VcpClose
- *
- * Is fl related to VCPDISKINFO.fl ?
- */
-RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest)
-{
-    FIXME("(%04x, '%s'), stub.\n", fl, lpszBackupDest);
-    return OK;
-}
-
 /*
  * Copy all items in a CopyFiles entry over to the destination
  *
@@ -1077,26 +1062,29 @@
  */
 static BOOL SETUPX_CopyFiles(LPSTR *pSub, HINF16 hInf)
 {
-    BOOL res = FALSE;
+    BOOL bSingle = FALSE;
     unsigned int n;
     LPCSTR filename = IP_GetFileName(hInf);
     LPSTR pCopyEntry;
-    char pDestStr[MAX_PATH];
+    char pDstStr[MAX_PATH];
     LPSTR pSrcDir, pDstDir;
     LPSTR pFileEntries, p;
     WORD ldid;
     LOGDISKDESC_S ldd;
     LPSTR *pSubFile;
     LPSTR pSrcFile, pDstFile;
+    WORD flag;
 
     for (n=0; n < *(DWORD *)pSub; n++)
     {
 	pCopyEntry = *(pSub+1+n);
 	if (*pCopyEntry == '@')
 	{
-	    ERR("single file not handled yet !\n");
-	    continue;
+	    pCopyEntry++;
+	    bSingle = TRUE;
 	}
+	else
+	    bSingle = FALSE;
 
 	/* get source directory for that entry */
 	INIT_LDD(ldd, LDID_SRCPATH);
@@ -1105,20 +1093,35 @@
 	
         /* get destination directory for that entry */
 	if (!(GetPrivateProfileStringA("DestinationDirs", pCopyEntry, "",
-					pDestStr, sizeof(pDestStr), filename)))
-	    continue;
+					pDstStr, sizeof(pDstStr), filename)))
+	{
+	    /* hmm, not found; try the default entry */
+	    if (!(GetPrivateProfileStringA("DestinationDirs", "DefaultDestDir", "", pDstStr, sizeof(pDstStr), filename)))
+	    {
+		WARN("DefaultDestDir not found.\n");
+	        continue;
+	    }
+	}
 
 	/* translate destination dir if given as LDID */
-	ldid = atoi(pDestStr);
+	ldid = atoi(pDstStr);
 	if (ldid)
 	{
 	    if (!(SETUPX_IP_TranslateLDID(ldid, &pDstDir, hInf)))
 		continue;
 	}
 	else
-	    pDstDir = pDestStr;
+	    pDstDir = pDstStr;
 	
-	/* now that we have the destination dir, iterate over files to copy */
+	/* now that we have the destination dir, register file copying */
+
+	if (bSingle)
+	{
+	    VcpQueueCopy16(pCopyEntry, pCopyEntry, pSrcDir, pDstDir, LDID_SRCPATH, ldid ? ldid : 0xffff, 0, VFNL_COPY, 0);
+	    return TRUE;
+	}
+
+	/* entry wasn't a single file, so let's iterate over section */
 	pFileEntries = SETUPX_GetSectionEntries(filename, pCopyEntry);
         for (p=pFileEntries; *p; p +=strlen(p)+1)
 	{
@@ -1126,30 +1129,34 @@
 	    pSrcFile = *(pSubFile+1);
 	    pDstFile = (*(DWORD *)pSubFile > 1) ? *(pSubFile+2) : pSrcFile;
 	    TRACE("copying file '%s\\%s' to '%s\\%s'\n", pSrcDir, pSrcFile, pDstDir, pDstFile);
+	    flag = 0;
 	    if (*(DWORD *)pSubFile > 2)
 	    {
-		WORD flag;
 		if ((flag = atoi(*(pSubFile+3)))) /* ah, flag */
 		{
 		    if (flag & 0x2c)
 		    FIXME("VNLP_xxx flag %d not handled yet.\n", flag);
 		}
 		else
-		    FIXME("temp file name '%s' given. Need to register in wininit.ini !\n", *(pSubFile+3)); /* strong guess that this is VcpQueueCopy() */
+		{
+		    FIXME("temp file name '%s' given. Need to register in wininit.ini !\n", *(pSubFile+3));
+		    /* we probably need to set VIRTNODE.vhstrDstFinalName to
+		     * the final destination name, and the temp name is merely
+		     * the copy destination */
+		}
 	    }
+	    VcpQueueCopy16(pSrcFile, pDstFile, pSrcDir, pDstDir, LDID_SRCPATH, ldid ? ldid : 0xffff, 0, VFNL_COPY|flag, 0);
 	    SETUPX_FreeSubStrings(pSubFile);
-	    /* we don't copy ANYTHING yet ! (I'm too lazy and want to verify
-	     * this first before destroying whole partitions ;-) */
 	}
     }
 	    
-    return res;
+    return TRUE;
 }
 
 /***********************************************************************
  *		GenInstall
  *
- * general install function for .INF file sections
+ * generic installer function for .INF file sections
  *
  * This is not perfect - patch whenever you can !
  * 
diff --git a/dlls/setupapi/setupx_private.h b/dlls/setupapi/setupx_private.h
deleted file mode 100644
index dc73255..0000000
--- a/dlls/setupapi/setupx_private.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef __SETUPX_PRIVATE_H
-#define __SETUPX_PRIVATE_H
-
-#include "wine/windef16.h"
-
-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)
-
-typedef struct {
-    HINF16 hInf;
-    HFILE hInfFile;
-    LPSTR lpInfFileName;
-} INF_FILE;
-
-extern INF_FILE *InfList;
-extern WORD InfNumEntries;
-
-extern LPCSTR IP_GetFileName(HINF16 hInf);
-
-#endif /* __SETUPX_PRIVATE_H */
diff --git a/dlls/setupapi/virtcopy.c b/dlls/setupapi/virtcopy.c
new file mode 100644
index 0000000..3cd7459
--- /dev/null
+++ b/dlls/setupapi/virtcopy.c
@@ -0,0 +1,709 @@
+/*
+ * SetupAPI virtual copy operations
+ *
+ * FIXME: we now rely on builtin setupapi.dll for dialog resources.
+ *        This is bad ! We ought to have 16bit resource handling working.
+ */
+
+#include <string.h>
+#include "debugtools.h"
+#include "windef.h"
+#include "setupx16.h"
+#include "heap.h"
+#include "callback.h"
+#include "stackframe.h"
+#include "winreg.h"
+#include "setupapi_private.h"
+
+DEFAULT_DEBUG_CHANNEL(setupapi);
+
+/* ### start build ### */
+extern WORD CALLBACK VCP_CallTo16_word_lwwll(FARPROC16,LPVOID,UINT16,WPARAM,LPARAM,LPARAM);
+/* ### stop build ### */
+
+static FARPROC16 VCP_Proc = NULL;
+static LPARAM VCP_MsgRef = 0;
+
+#define VCP_CALLBACK(obj,msg,wParam,lParam,lParamRef) \
+	(VCP_Proc) ? \
+	VCP_CallTo16_word_lwwll(VCP_Proc, obj,msg,wParam,lParam,lParamRef) : OK;
+
+static BOOL VCP_opened = FALSE;
+
+static VCPSTATUS vcp_status;
+
+static HINSTANCE SETUPAPI_hInstance;
+
+/****************************** 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))
+
+VHSTR WINAPI vsmStringAdd16(LPCSTR lpszName)
+{
+    VHSTR n;
+    VHSTR index = 0xffff;
+    HANDLE heap;
+
+    TRACE("add string '%s'\n", lpszName);
+    /* search whether string already inserted */
+    for (n = 0; n < vhstr_alloc; n++)
+    {
+	if ((vhstrlist[n]) && (vhstrlist[n]->refcount))
+	{
+		TRACE("comp %d\n", n);
+	    if (!strcmp(vhstrlist[n]->pStr, lpszName))
+	    {
+		vhstrlist[n]->refcount++;
+		return n;
+	    }
+	}
+    }
+
+    /* hmm, not found yet, let's insert it */
+    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;
+	vhstrlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, vhstrlist,
+					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;
+}
+
+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()
+ * 
+ * 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);
+}
+
+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;
+        pvnlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, pvnlist,
+		    		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
+ *
+ * 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;
+
+    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,
+	LPEXPANDVTBL lpExpandVtbl,
+	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,
+		    lpExpandVtbl);
+}
+
+/***********************************************************************
+ *		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);
+}
+
+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 ? */
+}
+
+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 (lpvfs->ldid != 0xffff)
+		    CtlGetLddPath16(lpvfs->ldid, buffer);
+                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);
+#if DO_A_REAL_COPY
+        if (!(CopyFileA(fn_src, fn_dst, TRUE)))
+	    res = ERR_VCP_IOFAIL;
+#endif
+	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
+ *
+ * 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;
+}
+
+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 BOOL CALLBACK VCP_UI_FileCopyDlgProc(HWND hWndDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
+{
+    BOOL retval = FALSE;
+
+    if (iMsg == WM_INITDIALOG)
+    {
+        ShowWindow(hWndDlg, SW_SHOWNORMAL);
+        UpdateWindow(hWndDlg);
+	retval = TRUE;
+    }
+    return retval;
+}
+
+BOOL VCP_UI_GetDialogTemplate(LPCVOID *template32)
+{
+    HANDLE hResInfo, hDlgTmpl32;
+
+    if (!(hResInfo = FindResourceA(SETUPAPI_hInstance, MAKEINTRESOURCEA(COPYFILEDLGORD), RT_DIALOGA)))
+	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, IDC_ARROWA);
+    wndClass.hbrBackground = (HBRUSH)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;
+
+    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;
+}
+
+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:
+	    DestroyWindow(hDlgCopy);
+	    break;
+	default:
+	    FIXME("unhandled msg 0x%04x\n", uMsg);
+    }
+    return res;
+}