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