Fixed some issues found by winapi_check.
[wine] / dlls / setupapi / virtcopy.c
1 /*
2  * SetupAPI virtual copy operations
3  *
4  * FIXME: we now rely on builtin setupapi.dll for dialog resources.
5  *        This is bad ! We ought to have 16bit resource handling working.
6  */
7
8 #include <string.h>
9 #include "debugtools.h"
10 #include "windef.h"
11 #include "setupx16.h"
12 #include "heap.h"
13 #include "callback.h"
14 #include "stackframe.h"
15 #include "winreg.h"
16 #include "setupapi_private.h"
17
18 DEFAULT_DEBUG_CHANNEL(setupapi);
19
20 /* ### start build ### */
21 extern WORD CALLBACK VCP_CallTo16_word_lwwll(FARPROC16,LPVOID,UINT16,WPARAM,LPARAM,LPARAM);
22 /* ### stop build ### */
23
24 static FARPROC16 VCP_Proc = NULL;
25 static LPARAM VCP_MsgRef = 0;
26
27 #define VCP_CALLBACK(obj,msg,wParam,lParam,lParamRef) \
28         (VCP_Proc) ? \
29         VCP_CallTo16_word_lwwll(VCP_Proc, obj,msg,wParam,lParam,lParamRef) : OK;
30
31 static BOOL VCP_opened = FALSE;
32
33 static VCPSTATUS vcp_status;
34
35 static HINSTANCE SETUPAPI_hInstance;
36
37 /****************************** VHSTR management ******************************/
38
39 /*
40  * This is a totally braindead implementation for now;
41  * I don't care about speed at all ! Size and implementation time
42  * is much more important IMHO. I could have created some sophisticated
43  * tree structure, but... what the hell ! :-)
44  */
45 typedef struct {
46     DWORD refcount;
47     LPCSTR pStr;
48 } VHSTR_STRUCT;
49
50 static VHSTR_STRUCT **vhstrlist = NULL;
51 static VHSTR vhstr_alloc = 0;
52
53 #define VALID_VHSTR(x)          ((x < vhstr_alloc) && (vhstrlist[x]) && (vhstrlist[x]->refcount))
54
55 /***********************************************************************
56  *              vsmStringAdd (SETUPX.207)
57  */
58 VHSTR WINAPI vsmStringAdd16(LPCSTR lpszName)
59 {
60     VHSTR n;
61     VHSTR index = 0xffff;
62     HANDLE heap;
63
64     TRACE("add string '%s'\n", lpszName);
65     /* search whether string already inserted */
66     for (n = 0; n < vhstr_alloc; n++)
67     {
68         if ((vhstrlist[n]) && (vhstrlist[n]->refcount))
69         {
70                 TRACE("comp %d\n", n);
71             if (!strcmp(vhstrlist[n]->pStr, lpszName))
72             {
73                 vhstrlist[n]->refcount++;
74                 return n;
75             }
76         }
77     }
78
79     /* hmm, not found yet, let's insert it */
80     for (n = 0; n < vhstr_alloc; n++)
81     {
82         if ((!(vhstrlist[n])) || (!(vhstrlist[n]->refcount)))
83         {
84             index = n;
85             break;
86         }
87     }
88     heap = GetProcessHeap();
89     if (n == vhstr_alloc) /* hmm, no free index found yet */
90     {
91         index = vhstr_alloc;
92         vhstr_alloc += 20;
93         vhstrlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, vhstrlist,
94                                         sizeof(VHSTR_STRUCT *) * vhstr_alloc);
95     }
96     if (index == 0xffff)
97         return 0xffff; /* failure */
98     if (!vhstrlist[index])
99         vhstrlist[index] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VHSTR_STRUCT));
100     vhstrlist[index]->refcount = 1;
101     vhstrlist[index]->pStr = HeapAlloc(heap, 0, strlen(lpszName)+1);
102     strcpy((LPSTR)vhstrlist[index]->pStr, lpszName);
103     return index;
104 }
105
106 /***********************************************************************
107  *              vsmStringDelete (SETUPX.206)
108  */
109 INT16 WINAPI vsmStringDelete16(VHSTR vhstr)
110 {
111     if (VALID_VHSTR(vhstr))
112     {
113         vhstrlist[vhstr]->refcount--;
114         if (!vhstrlist[vhstr]->refcount)
115         {
116             HeapFree(GetProcessHeap(), 0, (LPSTR)vhstrlist[vhstr]->pStr);
117             vhstrlist[vhstr]->pStr = NULL;
118         }
119         return VCPN_OK;
120     }
121
122     /* string not found */
123     return VCPN_FAIL;
124 }
125
126 /*
127  * vsmStringFind() - not exported from a standard SETUPX.DLL, it seems
128  */
129 VHSTR WINAPI vsmStringFind16(LPCSTR lpszName)
130 {
131     WORD n;
132     for (n = 0; n < vhstr_alloc; n++)
133         if ((vhstrlist[n]) && (vhstrlist[n]->refcount) && (!strcmp(vhstrlist[n]->pStr, lpszName)))
134             return n;
135     return 0xffff;
136 }
137
138 /***********************************************************************
139  *              vsmGetStringName (SETUPX.205)
140  * 
141  * Pretty correct, I guess
142  */
143 INT16 WINAPI vsmGetStringName16(VHSTR vhstr, LPSTR lpszBuffer, int cbBuffer)
144 {
145     if (VALID_VHSTR(vhstr))
146     {
147         int len = strlen(vhstrlist[vhstr]->pStr)+1;
148         if (cbBuffer >= len)
149         {
150             if (lpszBuffer)
151                 strcpy(lpszBuffer, vhstrlist[vhstr]->pStr);
152             return len;
153         }
154     }
155     return VCPN_FAIL;
156 }
157
158 /***********************************************************************
159  *              vsmStringCompare (not exported from a standard SETUPX.DLL, it seems)
160  */
161 INT16 WINAPI vsmStringCompare16(VHSTR vhstrA, VHSTR vhstrB)
162 {
163     if ((!VALID_VHSTR(vhstrA)) || (!VALID_VHSTR(vhstrB)))
164         return VCPN_FAIL; /* correct ? */
165     return strcmp(vhstrlist[vhstrA]->pStr, vhstrlist[vhstrB]->pStr);
166 }
167
168 /***********************************************************************
169  *              vsmGetStringRawName (SETUPX.208)
170  */
171 LPCSTR WINAPI vsmGetStringRawName16(VHSTR vhstr)
172 {
173     return (VALID_VHSTR(vhstr)) ? vhstrlist[vhstr]->pStr : NULL;
174 }
175
176
177 /***************************** VIRTNODE management ****************************/
178 static LPVIRTNODE *pvnlist = NULL;
179 static DWORD vn_num = 0;
180 static DWORD vn_last = 0;
181
182 RETERR16 VCP_VirtnodeCreate(LPVCPFILESPEC vfsSrc, LPVCPFILESPEC vfsDst, WORD fl, LPARAM lParam, LPEXPANDVTBL lpExpandVtbl)
183 {
184     HANDLE heap;
185     LPVIRTNODE lpvn;
186     RETERR16 cbres;
187
188     while (vn_last < vn_num)
189     {
190         if (pvnlist[vn_last] == NULL)
191             break;      
192         vn_last++;
193     }
194     heap = GetProcessHeap();
195     if (vn_last == vn_num)
196     {
197         vn_num += 20;
198         pvnlist = HeapReAlloc(heap, HEAP_ZERO_MEMORY, pvnlist,
199                                 sizeof(LPVIRTNODE *) * vn_num);
200     }
201     pvnlist[vn_last] = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(VIRTNODE));
202     lpvn = pvnlist[vn_last];
203     vn_last++;
204     
205     lpvn->cbSize = sizeof(VIRTNODE);
206
207     if (vfsSrc)
208         memcpy(&lpvn->vfsSrc, vfsSrc, sizeof(VCPFILESPEC));
209
210     if (vfsDst)
211         memcpy(&lpvn->vfsDst, vfsDst, sizeof(VCPFILESPEC));
212
213     lpvn->fl = fl;
214     lpvn->lParam = lParam;
215     lpvn->lpExpandVtbl = lpExpandVtbl;
216
217     lpvn->vhstrDstFinalName = 0xffff; /* FIXME: what is this ? */
218
219     cbres = VCP_CALLBACK(lpvn, VCPM_NODECREATE, 0, 0, VCP_MsgRef);
220     lpvn->fl |= VFNL_CREATED;
221     cbres = VCP_CALLBACK(lpvn, VCPM_NODEACCEPT, 0, 0, VCP_MsgRef);
222
223     return OK;
224 }
225
226 BOOL VCP_VirtnodeDelete(LPVIRTNODE lpvnDel)
227 {
228     DWORD n;
229     RETERR16 cbres;
230
231     for (n = 0; n < vn_last; n++)
232     {
233         if (pvnlist[n] == lpvnDel)
234         {
235             cbres = VCP_CALLBACK(lpvnDel, VCPM_NODEDESTROY, 0, 0, VCP_MsgRef);
236             HeapFree(GetProcessHeap(), 0, lpvnDel);
237             pvnlist[n] = NULL;
238             return TRUE;
239         }
240     }
241     return FALSE;
242 }
243
244 /***********************************************************************
245  *              VcpOpen (SETUPX.200)
246  *
247  * Sets up a virtual copy operation.
248  * This means that functions such as GenInstall()
249  * create a VIRTNODE struct for every file to be touched in a .INF file
250  * instead of actually touching the file.
251  * The actual copy/move/rename gets started when VcpClose or
252  * VcpFlush is called; several different callbacks are made
253  * (copy, rename, open, close, version conflicts, ...) on every file copied.
254  */
255 RETERR16 WINAPI VcpOpen16(VIFPROC vifproc, LPARAM lparamMsgRef)
256 {
257     TRACE("(%p, %08lx)\n", vifproc, lparamMsgRef);
258     if (VCP_opened)
259         return ERR_VCP_BUSY;
260
261     VCP_Proc = (FARPROC16)vifproc;
262     VCP_MsgRef = lparamMsgRef;
263
264     /* load SETUPAPI needed for dialog resources etc. */
265     SETUPAPI_hInstance = LoadLibraryA("setupapi.dll");
266     if (!SETUPAPI_hInstance)
267     {
268         ERR("Could not load sibling setupapi.dll\n");
269         return ERR_VCP_NOMEM;
270     }
271     VCP_opened = TRUE;
272     return OK;
273 }
274
275 /***********************************************************************
276  *              VcpQueueCopy            [SETUPX.13]
277  *              
278  * lpExpandVtbl seems to be deprecated.
279  * fl are the CNFL_xxx and VNFL_xxx flags.
280  * lParam are the VNLP_xxx flags.
281  */
282 RETERR16 WINAPI VcpQueueCopy16(
283         LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName,
284         LPCSTR lpszSrcDir, LPCSTR lpszDstDir,
285         LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst,
286         LPEXPANDVTBL lpExpandVtbl,
287         WORD fl, LPARAM lParam
288 )
289 {
290     VCPFILESPEC vfsSrc, vfsDst;
291
292     if (!VCP_opened)
293         return ERR_VCP_NOTOPEN;
294
295     vfsSrc.ldid = ldidSrc;
296     vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir);
297     vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName);
298
299     vfsDst.ldid = ldidDst;
300     vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
301     vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
302
303     return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, fl, lParam,
304                     lpExpandVtbl);
305 }
306
307 /***********************************************************************
308  *              VcpQueueDelete          [SETUPX.17]
309  *
310  * Is lParamRef the same as lParam in VcpQueueCopy ?
311  * Damn docu !! Err... which docu ?
312  */
313 RETERR16 WINAPI VcpQueueDelete16(
314         LPCSTR lpszDstFileName,
315         LPCSTR lpszDstDir,
316         LOGDISKID16 ldidDst,
317         LPEXPANDVTBL lpExpandVtbl,
318         LPARAM lParamRef
319 )
320 {
321     VCPFILESPEC vfsDst;
322
323     if (!VCP_opened)
324         return ERR_VCP_NOTOPEN;
325
326     vfsDst.ldid = ldidDst;
327     vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
328     vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
329
330     return VCP_VirtnodeCreate(NULL, &vfsDst, VNFL_DELETE, lParamRef,
331                     lpExpandVtbl);
332 }
333
334 /***********************************************************************
335  *              VcpQueueRename          [SETUPX.204]
336  *              
337  */
338 RETERR16 WINAPI VcpQueueRename16(
339         LPCSTR lpszSrcFileName, LPCSTR lpszDstFileName,
340         LPCSTR lpszSrcDir, LPCSTR lpszDstDir,
341         LOGDISKID16 ldidSrc, LOGDISKID16 ldidDst,
342         LPARAM lParam
343 )
344 {
345     VCPFILESPEC vfsSrc, vfsDst;
346
347     if (!VCP_opened)
348         return ERR_VCP_NOTOPEN;
349
350     vfsSrc.ldid = ldidSrc;
351     vfsSrc.vhstrDir = vsmStringAdd16(lpszSrcDir);
352     vfsSrc.vhstrFileName = vsmStringAdd16(lpszSrcFileName);
353
354     vfsDst.ldid = ldidDst;
355     vfsDst.vhstrDir = vsmStringAdd16(lpszDstDir);
356     vfsDst.vhstrFileName = vsmStringAdd16(lpszDstFileName);
357
358     return VCP_VirtnodeCreate(&vfsSrc, &vfsDst, VNFL_RENAME, lParam,
359                     0);
360 }
361
362 /***********************************************************************
363  *              VcpEnumFiles (SETUPX.@)
364  */
365 INT16 WINAPI VcpEnumFiles(VCPENUMPROC vep, LPARAM lParamRef)
366 {
367     WORD n;
368
369     for (n = 0; n < vn_last; n++)
370         vep(pvnlist[n], lParamRef);
371
372     return 0; /* FIXME: return value ? */
373 }
374
375 /***********************************************************************
376  *              VcpExplain (SETUPX.?)
377  */
378 LPCSTR WINAPI VcpExplain16(LPVIRTNODE lpVn, DWORD dwWhat)
379 {
380     static char buffer[MAX_PATH]; /* FIXME: is this how it's done ? */
381     buffer[0] = '\0';
382     switch (dwWhat)
383     {
384         case VCPEX_SRC_FULL:
385         case VCPEX_DST_FULL:
386             {
387                 LPVCPFILESPEC lpvfs =
388                     (dwWhat == VCPEX_SRC_FULL) ?  &lpVn->vfsSrc : &lpVn->vfsDst;
389
390                 if (lpvfs->ldid != 0xffff)
391                     CtlGetLddPath16(lpvfs->ldid, buffer);
392                 strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrDir));
393                 strcat(buffer, "\\");
394                 strcat(buffer, vsmGetStringRawName16(lpvfs->vhstrFileName));
395             }
396             break;
397         default:
398             FIXME("%ld unimplemented !\n", dwWhat);
399             strcpy(buffer, "Unknown error");
400             break;
401     }
402     return buffer;
403 }
404
405 RETERR16 VCP_CheckPaths(void)
406 {
407     DWORD n;
408     LPVIRTNODE lpvn;
409     RETERR16 cbres;
410
411     cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATPATHCHECKSTART, 0, 0, VCP_MsgRef);
412     for (n = 0; n < vn_num; n++)
413     {
414         lpvn = pvnlist[n];
415         if (!lpvn) continue;
416         /* FIXME: check paths of all VIRTNODEs here ! */
417         cbres = VCP_CALLBACK(&lpvn->vfsDst, VCPM_CHECKPATH, 0, (DWORD)lpvn, VCP_MsgRef);
418     }
419     cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATPATHCHECKEND, 0, 0, VCP_MsgRef);
420     return OK;
421 }
422
423 RETERR16 VCP_CopyFiles(void)
424 {
425     char fn_src[MAX_PATH], fn_dst[MAX_PATH];
426     RETERR16 res = OK, cbres;
427     DWORD n;
428     LPVIRTNODE lpvn;
429
430     cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCOPYSTART, 0, 0, VCP_MsgRef);
431     for (n = 0; n < vn_num; n++)
432     {
433         lpvn = pvnlist[n];
434         if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_COPY)) continue;
435         /* FIXME: need to send VCPM_VSTATNEWDISK notification sometimes */
436         strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL));
437         strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL));
438         /* FIXME: what is this VCPM_VSTATWRITE here for ?
439          * I guess it's to signal successful destination file creation */
440         cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef);
441         /* FIXME: need to do the file copy in small chunks for notifications */
442         TRACE("copying '%s' to '%s'\n", fn_src, fn_dst);
443 #if DO_A_REAL_COPY
444         if (!(CopyFileA(fn_src, fn_dst, TRUE)))
445             res = ERR_VCP_IOFAIL;
446 #endif
447         vcp_status.prgFileRead.dwSoFar++;
448         cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATREAD, 0, 0, VCP_MsgRef);
449         vcp_status.prgFileWrite.dwSoFar++;
450         cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATWRITE, 0, 0, VCP_MsgRef);
451     }
452     
453     cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCOPYEND, 0, 0, VCP_MsgRef);
454     return res;
455 }
456
457 /***********************************************************************
458  *              VcpFlush - internal (not exported), but documented
459  *
460  * VNFL_NOW is used for VcpFlush.
461  */
462 RETERR16 VcpFlush16(WORD fl, LPCSTR lpszBackupDest)
463 {
464     return OK;
465 }
466
467 /***********************************************************************
468  *              VcpClose (SETUPX.201)
469  *
470  * Does callbacks (-> vifproc) with VCPM_VSTATCLOSESTART,
471  * VCPM_VSTATCLOSEEND.
472  *
473  * fl gets VCPFL_xxx flags to indicate what to do with the
474  * VIRTNODEs (files to mess with) created by e.g. GenInstall()
475  */
476 RETERR16 WINAPI VcpClose16(WORD fl, LPCSTR lpszBackupDest)
477 {
478     RETERR16 res = OK;
479     WORD cbres = VCPN_PROCEED;
480
481     TRACE("(%04x, '%s')\n", fl, lpszBackupDest);
482
483     /* FIXME: needs to sort virtnodes in case VCPFL_INSPECIFIEDORDER
484      * is not set. This is done by VCP_CALLBACK(VCPM_NODECOMPARE) */
485     
486     TRACE("#1\n");
487     memset(&vcp_status, 0, sizeof(VCPSTATUS));
488     /* yes, vcp_status.cbSize is 0 ! */
489     TRACE("#2\n");
490     cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCLOSESTART, 0, 0, VCP_MsgRef);
491     TRACE("#3\n");
492
493     res = VCP_CheckPaths();
494     TRACE("#4\n");
495     if (res != OK)
496         return res; /* is this ok ? */
497     VCP_CopyFiles();
498     
499     TRACE("#5\n");
500     cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATCLOSEEND, 0, 0, VCP_MsgRef);
501     TRACE("#6\n");
502     VCP_Proc = NULL;
503     FreeLibrary(SETUPAPI_hInstance);
504     VCP_opened = FALSE;
505     return OK;
506 }
507
508 RETERR16 VCP_RenameFiles(void)
509 {
510     char fn_src[MAX_PATH], fn_dst[MAX_PATH];
511     RETERR16 res = OK, cbres;
512     DWORD n;
513     LPVIRTNODE lpvn;
514
515     cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATRENAMESTART, 0, 0, VCP_MsgRef);
516     for (n = 0; n < vn_num; n++)
517     {
518         lpvn = pvnlist[n];
519         if ((!lpvn) || ((lpvn->fl & VNFL_NODE_TYPE) != VNFL_RENAME)) continue;
520         strcpy(fn_src, VcpExplain16(lpvn, VCPEX_SRC_FULL));
521         strcpy(fn_dst, VcpExplain16(lpvn, VCPEX_DST_FULL));
522         cbres = VCP_CALLBACK(&lpvn->vfsDst, VCPM_FILEOPENOUT, 0, (LPARAM)lpvn, VCP_MsgRef);
523         if (!(MoveFileExA(fn_src, fn_dst, MOVEFILE_REPLACE_EXISTING)))
524             res = ERR_VCP_IOFAIL;
525         else
526             VCP_VirtnodeDelete(lpvn);
527     }
528     cbres = VCP_CALLBACK(&vcp_status, VCPM_VSTATRENAMEEND, 0, 0, VCP_MsgRef);
529     return res;
530 }
531
532 /***********************************************************************
533  *              vcpDefCallbackProc (SETUPX.202)
534  */
535 RETERR16 WINAPI vcpDefCallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
536                                         LPARAM lParam, LPARAM lParamRef)
537 {
538     static int count = 0;
539     if (count < 10)
540         FIXME("(%p, %04x, %04x, %08lx, %08lx) - what to do here ?\n",
541                 lpvObj, uMsg, wParam, lParam, lParamRef);
542     count++;
543     return OK;
544 }
545
546 /********************* point-and-click stuff from here ***********************/
547
548 static HWND hDlgCopy = 0;
549 static HKEY hKeyFiles = 0, hKeyRename = 0, hKeyConflict = 0;
550 static char BackupDir[12];
551
552 static BOOL CALLBACK VCP_UI_FileCopyDlgProc(HWND hWndDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
553 {
554     BOOL retval = FALSE;
555
556     if (iMsg == WM_INITDIALOG)
557     {
558         ShowWindow(hWndDlg, SW_SHOWNORMAL);
559         UpdateWindow(hWndDlg);
560         retval = TRUE;
561     }
562     return retval;
563 }
564
565 BOOL VCP_UI_GetDialogTemplate(LPCVOID *template32)
566 {
567     HANDLE hResInfo, hDlgTmpl32;
568
569     if (!(hResInfo = FindResourceA(SETUPAPI_hInstance, MAKEINTRESOURCEA(COPYFILEDLGORD), RT_DIALOGA)))
570         return FALSE;
571     if (!(hDlgTmpl32 = LoadResource(SETUPAPI_hInstance, hResInfo )) ||
572         !(*template32 = LockResource( hDlgTmpl32 )))
573         return FALSE;
574     return TRUE;
575 }
576
577 static LRESULT WINAPI
578 VCP_UI_FileCopyWndProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
579 {
580     if (uMsg != WM_CREATE)
581         return DefWindowProcA (hwnd, uMsg, wParam, lParam);
582
583     switch (uMsg)
584     {
585         case WM_CREATE:
586             return 0;
587         default:
588             FIXME("%04x: unhandled.\n", uMsg);
589     }
590
591     return 0;
592 }
593
594 void VCP_UI_RegisterProgressClass(void)
595 {
596     static BOOL registered = FALSE;
597     WNDCLASSA wndClass;
598
599     if (registered)
600         return;
601
602     registered = TRUE;
603     ZeroMemory (&wndClass, sizeof(WNDCLASSA));
604     wndClass.style         = CS_GLOBALCLASS | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
605     wndClass.lpfnWndProc   = (WNDPROC)VCP_UI_FileCopyWndProc;
606     wndClass.cbClsExtra    = 0;
607     wndClass.cbWndExtra    = 0;
608     wndClass.hCursor       = LoadCursorA (0, IDC_ARROWA);
609     wndClass.hbrBackground = (HBRUSH)NULL;
610     wndClass.lpszClassName = "setupx_progress";
611   
612     RegisterClassA (&wndClass);
613 }
614
615 RETERR16 VCP_UI_NodeCompare(LPVIRTNODE vn1, LPVIRTNODE vn2)
616 {
617     LPCSTR file1, file2;
618     file1 = vsmGetStringRawName16(vn1->vfsSrc.vhstrFileName);
619     file2 = vsmGetStringRawName16(vn2->vfsSrc.vhstrFileName);
620     return (RETERR16)strcmp(file1, file2);
621 }
622
623 RETERR16 VCP_UI_CopyStart(void)
624 {
625     LPCVOID template32;
626     char buf[256]; /* plenty */
627     BOOL dirty;
628     DWORD len;
629
630     /* FIXME: should be registered at DLL startup instead */
631     VCP_UI_RegisterProgressClass();
632     if (!(VCP_UI_GetDialogTemplate(&template32)))
633         return VCPN_FAIL;
634
635     hDlgCopy = CreateDialogIndirectParamA(SETUPAPI_hInstance, template32, 0,
636                                                 VCP_UI_FileCopyDlgProc, 0);
637     if (!hDlgCopy)
638         return VCPN_FAIL;
639     SetDlgItemTextA(hDlgCopy, SOURCESTRORD, "Scanning ...");
640     SetDlgItemTextA(hDlgCopy, DESTSTRORD, "NOT_IMPLEMENTED_YET");
641     strcpy(buf, REG_INSTALLEDFILES);
642     if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyFiles))
643         return VCPN_FAIL;
644     strcat(buf, REGPART_RENAME);
645     if (RegCreateKeyA(HKEY_LOCAL_MACHINE, buf, &hKeyRename))
646         return VCPN_FAIL;
647     if (RegCreateKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT, &hKeyConflict))
648         return VCPN_FAIL;
649     len = 1;
650     if (!(RegQueryValueExA(hKeyConflict, "Dirty", NULL, 0, (LPBYTE)&dirty, &len)))
651     {
652         /* FIXME: what does SETUPX.DLL do in this case ? */
653         MESSAGE("Warning: another program using SETUPX is already running ! Failed.\n");
654         return VCPN_FAIL;
655     }
656     dirty = TRUE;
657     if (RegSetValueExA(hKeyConflict, "Dirty", 0, REG_BINARY, (LPBYTE)&dirty, 1))
658         return VCPN_FAIL;
659     len = 12;
660     if (!(RegQueryValueExA(hKeyConflict, "BackupDirectory", NULL, 0, BackupDir, &len)))
661         strcpy(BackupDir, "VCM");
662
663     /* create C:\WINDOWS\[BackupDir] and set registry key to it */
664     GetWindowsDirectoryA(buf, 256);
665     strcat(buf, "\\");
666     strcat(buf, BackupDir);
667     if (!(CreateDirectoryA(buf, NULL)))
668         return VCPN_FAIL;
669     if (RegSetValueExA(hKeyConflict, "BackupDirectory", 0, REG_SZ, (LPBYTE)buf, strlen(buf)+1))
670         return VCPN_FAIL;
671     RegCloseKey(hKeyConflict);
672     
673     return VCPN_OK;
674 }
675
676 /***********************************************************************
677  *              vcpUICallbackProc (SETUPX.213)
678  */
679 RETERR16 WINAPI vcpUICallbackProc16(LPVOID lpvObj, UINT16 uMsg, WPARAM wParam,
680                                         LPARAM lParam, LPARAM lParamRef)
681 {
682     static int count = 0;
683     RETERR16 res = VCPN_OK, cbres;
684
685     if (count < 5)
686         FIXME("(%p, %04x, %04x, %08lx, %08lx) - semi-stub\n",
687                 lpvObj, uMsg, wParam, lParam, lParamRef);
688     count++;
689     switch (uMsg)
690     {
691         /* unused messages, it seems */
692         case VCPM_DISKPREPINFO:
693
694         case VCPM_FILENEEDED:
695
696         case VCPM_NODECREATE:
697         case VCPM_NODEACCEPT:
698
699         case VCPM_VSTATCLOSESTART:
700         case VCPM_VSTATPATHCHECKSTART:
701         case VCPM_VSTATPATHCHECKEND:
702
703         case VCPM_CHECKPATH:
704             break;
705
706         /* the real stuff */
707         case VCPM_NODECOMPARE:
708             res = VCP_UI_NodeCompare((LPVIRTNODE)lpvObj, (LPVIRTNODE)lParam);
709             break;
710         case VCPM_VSTATREAD:
711             break;
712         case VCPM_VSTATWRITE:
713             cbres = VCP_CALLBACK(&vcp_status, VCPM_DISKPREPINFO, 0, 0, VCP_MsgRef);
714             break;
715         case VCPM_VSTATCLOSEEND:
716             RegCloseKey(hKeyFiles);
717             RegCloseKey(hKeyRename);
718             RegDeleteKeyA(HKEY_LOCAL_MACHINE, REG_VERSIONCONFLICT);
719             break;
720         case VCPM_VSTATCOPYSTART:
721             res = VCP_UI_CopyStart();
722             break;
723         case VCPM_VSTATCOPYEND:
724             DestroyWindow(hDlgCopy);
725             break;
726         default:
727             FIXME("unhandled msg 0x%04x\n", uMsg);
728     }
729     return res;
730 }