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