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