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