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