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