remove installation directory in batch file, too
[swftools.git] / installer / installer.c
1 /* installer.c
2
3    Part of the rfx installer (Main program).
4    
5    Copyright (c) 2004-2008 Matthias Kramm <kramm@quiss.org> 
6  
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
20
21 #include <windows.h>
22 #include <commctrl.h>
23 #include <commdlg.h>
24 #include <shlobj.h>
25 #include <prsht.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include "installer.h"
29 #ifndef DEINSTALL
30 #include "archive.h"
31 #endif
32 #include "utils.h"
33
34 static int config_forAllUsers = 0;
35 static int config_createLinks = 0;
36 static int config_createStartmenu = 1;
37 static int config_createDesktop = 1;
38 static int config_deleteextra = 1;
39
40 static char path_startmenu[MAX_PATH] = "\0";
41 static char path_desktop[MAX_PATH] = "\0";
42 static char path_programfiles[MAX_PATH] = "\0";
43
44 static char pathBuf[MAX_PATH];
45 static int do_abort = 0;
46
47 static char* pdf2swf_path;
48
49 static char registry_path[1024];
50
51 static char*install_path = "c:\\swftools\\";
52 #define SOFTWARE_DOMAIN "quiss.org"
53 #define SOFTWARE_NAME "SWFTools"
54 #define INSTALLER_NAME "SWFTools Installer"
55
56 static HBITMAP logo = 0;
57
58 static HINSTANCE me;
59
60 #define USER_SETMESSAGE 0x7f01
61
62 #ifndef DEINSTALL
63 extern char*crndata;
64 extern int crndata_len;
65 extern int crn_decompressed_size;
66 extern char*license_text;
67
68 #include "background.c"
69 #endif
70
71 typedef struct _filelist
72 {
73     const char*filename;
74     struct _filelist*next;
75     char type;
76 } filelist_t;
77
78 static filelist_t* registerFile(filelist_t*next, const char*filename, char type)
79 {
80     filelist_t*file = malloc(sizeof(filelist_t));
81     file->filename = strdup(filename);
82     file->type = type;
83     file->next = next;
84     return file;
85 }
86
87 static filelist_t* readFileList(char*filename)
88 {
89     FILE*fi = fopen(filename, "rb");
90     if(!fi) {
91         return 0;
92     }
93     fseek(fi, 0, SEEK_END);
94     int len = ftell(fi);
95     fseek(fi, 0, SEEK_SET);
96     char*data = malloc(len+1);
97     fread(data, len, 1, fi);
98     fclose(fi);
99     int t=0;
100     char*line = data;
101     filelist_t*list = 0,*lpos=0;;
102     while(t<len) {
103         if(data[t]=='\r' || data[t]=='\n') {
104             data[t++] = 0;
105             if(strchr("DFI", line[0]) && line[1]==' ' && line[2]!=' ') {
106                 filelist_t*f = malloc(sizeof(filelist_t));
107                 f->type=line[0];
108                 f->filename=strdup(line+2);
109                 f->next = 0;
110                 if(!list) {
111                     list = lpos = f;
112                 } else {
113                     lpos->next = f;
114                     lpos = f;
115                 }
116             } else {
117                 // skip line- this usually only happens if somebody tampered
118                 // with the file
119             }
120             while(t<len && (data[t]=='\0' || data[t]=='\r' || data[t]=='\n'))
121                 t++;
122             line = &data[t];
123         } else {
124             t++;
125         }
126     }
127     return list;
128 }
129
130 static void writeFileList(filelist_t*file, const char*filename)
131 {
132     FILE*fi = fopen(filename, "wb");
133     fprintf(fi, "[%s installed files]\r\n", SOFTWARE_NAME);
134     if(!fi) {
135         char buf[1024];
136         sprintf(buf, "Couldn't write file %s", filename);
137         MessageBox(0, buf, INSTALLER_NAME, MB_OK|MB_ICONERROR);
138         do_abort=1;
139         exit(1);
140     }
141     while(file) {
142         fprintf(fi, "%c %s\r\n", file->type, file->filename);
143         file = file->next;
144     }
145     fclose(fi);
146 }
147
148 static filelist_t*installedFiles = 0;
149
150 static void addFile(const char*filename)
151 {
152     installedFiles = registerFile(installedFiles, filename, 'F');
153 }
154 static void addDir(const char*filename)
155 {
156     installedFiles = registerFile(installedFiles, filename, 'D');
157 }
158
159 static void handleTemplateFile(const char*filename)
160 {
161     FILE*fi = fopen(filename, "rb");
162     fseek(fi, 0, SEEK_END);
163     int len = ftell(fi);
164     fseek(fi, 0, SEEK_SET);
165     char*file = malloc(len+1);
166     fread(file, len, 1, fi);
167     fclose(fi);
168     int l = strlen(install_path);
169     fi = fopen(filename, "wb");
170     char*pos = file;
171     char*lastpos = file;
172     while(1) {
173         pos = strstr(pos, "%%PATH%%");
174         if(!pos) {
175             pos = &file[len];
176             break;
177         }
178         if(pos!=lastpos)
179             fwrite(lastpos, pos-lastpos, 1, fi);
180         fwrite(install_path, l, 1, fi);
181         pos+=8; // length of "%%PATH%%"
182         lastpos = pos;
183     }
184     fwrite(lastpos, pos-lastpos, 1, fi);
185     fclose(fi);
186     free(file);
187 }
188
189 static int setRegistryEntry(char*key,char*value)
190 {
191     HKEY hkey1;
192     HKEY hkey2;
193     int ret1 = -1, ret2= -1;
194     ret1 = RegCreateKey(HKEY_CURRENT_USER, key, &hkey1);
195     if(config_forAllUsers) {
196         ret2 = RegCreateKey(HKEY_LOCAL_MACHINE, key, &hkey2);
197     }
198
199     if(ret1 && ret2) {
200         fprintf(stderr, "registry: CreateKey %s failed\n", key);
201         return 0;
202     }
203     if(ret1) {
204         installedFiles = registerFile(installedFiles, key, 'k');
205     }
206     if(ret2) {
207         installedFiles = registerFile(installedFiles, key, 'K');
208     }
209
210     if(!ret1)
211         ret1 = RegSetValue(hkey1, NULL, REG_SZ, value, strlen(value)+1);
212     if(!ret2)
213         ret2 = RegSetValue(hkey2, NULL, REG_SZ, value, strlen(value)+1);
214     if(ret1 && ret2) {
215         fprintf(stderr, "registry: SetValue %s failed\n", key);
216         return 0;
217     }
218     return 1;
219 }
220
221
222 static char* getRegistryEntry(char*path)
223 {
224     int res = 0;
225     HKEY key;
226     long rc;
227     long size = 0;
228     DWORD type;
229     char*buf;
230     rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0, KEY_ALL_ACCESS/* KEY_READ*/, &key);
231     if (rc != ERROR_SUCCESS) {
232         fprintf(stderr, "RegOpenKeyEx failed\n");
233         return 0;
234     }
235     rc = RegQueryValueEx(key, NULL, 0, 0, 0, (LPDWORD)&size) ;
236     if(rc != ERROR_SUCCESS) {
237         fprintf(stderr, "RegQueryValueEx(1) failed: %d\n", rc);
238         return 0;
239     }
240     buf = (char*)malloc(size+1);
241     rc = RegQueryValueEx(key, NULL, 0, &type, (BYTE*)buf, (LPDWORD)&size);
242     if(rc != ERROR_SUCCESS) {
243         fprintf(stderr, "RegQueryValueEx(2) failed: %d\n", rc);
244         return 0;
245     }
246     if(type == REG_SZ || type == REG_EXPAND_SZ) {
247         while(size && buf[size-1] == '\0')
248           --size;
249         buf[size] = 0;
250         /* TODO: convert */
251         return buf;
252     } else if(type == REG_BINARY) {
253         return buf;
254     }
255 }
256
257 void processMessages()
258 {
259     MSG msg;
260     while(PeekMessage(&msg,NULL,0,0,0))
261     {
262         GetMessage(&msg, NULL, 0, 0);
263         TranslateMessage(&msg);
264         DispatchMessage(&msg);
265     }
266 }
267
268 int addRegistryEntries(char*install_dir)
269 {
270     int ret;
271     ret = setRegistryEntry(registry_path, install_dir);
272     if(!ret) return 0;
273     return 1;
274 }
275
276 int CreateShortcut(char*path, char*description, char*filename, char*arguments, int iconindex, char*iconpath, char*workdir)
277 {
278     printf("Creating %s -> %s\n", filename, path);
279     WCHAR wszFilename[MAX_PATH];
280     IShellLink *ps1 = NULL;
281     IPersistFile *pPf = NULL;
282     HRESULT hr;
283     hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLink, (void*)&ps1);
284     if(FAILED(hr)) return 0;
285     hr = ps1->lpVtbl->QueryInterface(ps1, &IID_IPersistFile, (void **)&pPf);
286     if(FAILED(hr)) return 0;
287     hr = ps1->lpVtbl->SetPath(ps1, path);
288     if(FAILED(hr)) return 0;
289     hr = ps1->lpVtbl->SetDescription(ps1, description);
290     
291     if (arguments) {
292         hr = ps1->lpVtbl->SetArguments(ps1, arguments);
293         if(FAILED(hr)) return 0;
294     }
295     if (iconpath) {
296         hr = ps1->lpVtbl->SetIconLocation(ps1, iconpath, iconindex);
297         if (FAILED(hr)) return 0;
298     }
299     if (workdir) {
300         hr = ps1->lpVtbl->SetWorkingDirectory(ps1, workdir);
301         if (FAILED(hr)) return 0;
302     }
303     MultiByteToWideChar(CP_ACP, 0, filename, -1, wszFilename, MAX_PATH);
304     hr = pPf->lpVtbl->Save(pPf, wszFilename, TRUE);
305     if(FAILED(hr)) {
306         return 0;
307     }
308     pPf->lpVtbl->Release(pPf);
309     ps1->lpVtbl->Release(ps1);
310     addFile(filename);
311     return 1;
312 }
313
314 static int CreateURL(const char*url, const char*path)
315 {
316     FILE*fi = fopen(path, "wb");
317     if(!fi)
318         return 0;
319     fprintf(fi, "[InternetShortcut]\r\n");
320     fprintf(fi, "URL=http://localhost:8081/\r\n");
321     fclose(fi);
322     addFile(path);
323     return 1;
324 }
325
326
327 BOOL CALLBACK PropertySheetFuncCommon(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, int buttons)
328 {
329     LPNMHDR lpnm;
330         
331     HWND dialog = GetParent(hwnd);
332
333     if(message == WM_INITDIALOG) {
334         if(logo)
335             SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)logo);
336
337         RECT rc;
338         GetWindowRect(dialog, &rc);
339         int width = rc.right - rc.left;
340         int height = rc.bottom - rc.top;
341         MoveWindow(dialog, (GetSystemMetrics(SM_CXSCREEN) - width)/2, (GetSystemMetrics(SM_CYSCREEN) - height)/2, width, height, FALSE);
342         return FALSE;
343      }
344
345     if(message == WM_NOTIFY && (((LPNMHDR)lParam)->code == PSN_SETACTIVE)) {
346         PropSheet_SetWizButtons(dialog, buttons);
347         return FALSE;
348     }
349     return FALSE;
350 }
351
352 #ifndef DEINSTALL
353 BOOL CALLBACK PropertySheetFunc1(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
354     if(message == WM_INITDIALOG) {
355         SetDlgItemText(hwnd, IDC_LICENSE, license_text);
356     }
357     return PropertySheetFuncCommon(hwnd, message, wParam, lParam, PSWIZB_NEXT);
358 }
359 BOOL CALLBACK PropertySheetFunc2(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
360     if(message == WM_INITDIALOG) {
361         SetDlgItemText(hwnd, IDC_INSTALL_PATH, install_path);
362
363         config_forAllUsers = 0;
364         SendDlgItemMessage(hwnd, IDC_ALLUSERS, BM_SETCHECK, config_forAllUsers, 0);
365         SendDlgItemMessage(hwnd, IDC_CURRENTUSER, BM_SETCHECK, config_forAllUsers^1, 0);
366     }
367     if(message == WM_COMMAND) {
368         if((wParam&0xffff) == IDC_BROWSE) {
369             BROWSEINFOA browse;
370             memset(&browse, 0, sizeof(browse));
371             browse.ulFlags = BIF_EDITBOX | BIF_NEWDIALOGSTYLE | BIF_USENEWUI;// | BIF_RETURNONLYFSDIRS; //BIF_VALIDATE
372             browse.pszDisplayName = (CHAR*)malloc(MAX_PATH);
373             memset(browse.pszDisplayName, 0, MAX_PATH);
374             browse.lpszTitle = "Select installation directory";
375             browse.pidlRoot = SHBrowseForFolder(&browse);
376             if(browse.pszDisplayName) {
377                 if(SHGetPathFromIDList(browse.pidlRoot, browse.pszDisplayName)) {
378                     install_path = browse.pszDisplayName;
379                     int l = strlen(install_path);
380                     while(l && install_path[l-1]=='\\') {
381                         install_path[--l]=0;
382                     }   
383                 }
384             }
385             SendDlgItemMessage(hwnd, IDC_INSTALL_PATH, WM_SETTEXT, 0, (LPARAM)install_path);
386             return 0;
387
388         }
389         else if((wParam&0xffff) == IDC_ALLUSERS) {
390             config_forAllUsers = 1;
391             SendDlgItemMessage(hwnd, IDC_ALLUSERS, BM_SETCHECK, config_forAllUsers, 0);
392             SendDlgItemMessage(hwnd, IDC_CURRENTUSER, BM_SETCHECK, config_forAllUsers^1, 0);
393         }
394         else if((wParam&0xffff) == IDC_CURRENTUSER) {
395             config_forAllUsers = 0;
396             SendDlgItemMessage(hwnd, IDC_ALLUSERS, BM_SETCHECK, config_forAllUsers, 0);
397             SendDlgItemMessage(hwnd, IDC_CURRENTUSER, BM_SETCHECK, config_forAllUsers^1, 0);
398         }
399         else if((wParam&0xffff) == IDC_INSTALL_PATH) {
400             SendDlgItemMessage(hwnd, IDC_INSTALL_PATH, WM_GETTEXT, sizeof(pathBuf), (LPARAM)&(pathBuf[0]));
401             if(pathBuf[0]) {
402                 install_path = pathBuf;
403                 int l = strlen(install_path);
404                 while(l && install_path[l-1]=='\\') {
405                     install_path[--l]=0;
406                 }   
407             }
408             return 0;
409         }
410     }
411     return PropertySheetFuncCommon(hwnd, message, wParam, lParam, PSWIZB_BACK|PSWIZB_NEXT);
412 }
413 HWND statuswnd;
414 static int progress_pos = 0;
415
416 void PropertyArchiveError(char*text)
417 {
418     while(1) {
419         int ret = MessageBox(0, text, "Error", MB_RETRYCANCEL|MB_ICONERROR);
420         if(ret==IDRETRY) continue;
421         else break;
422     }
423 }
424
425 void PropertyArchive_NewFile(char*filename)
426 {
427     addFile(filename);
428     processMessages();
429 }
430 void PropertyArchive_NewDirectory(char*filename)
431 {
432     addDir(filename);
433     processMessages();
434 }
435
436 static int lastlen = 0;
437 void PropertyArchiveStatus(int pos, int len)
438 {
439     if(len!=lastlen) {
440         SendDlgItemMessage(statuswnd, IDC_PROGRESS, PBM_SETRANGE, 0, (LPARAM)MAKELONG(0,len));
441         lastlen = len;
442     }
443     SendDlgItemMessage(statuswnd, IDC_PROGRESS, PBM_SETPOS, pos, 0);
444     processMessages();
445     Sleep(30);
446 }
447 void PropertyArchiveMessage(char*text)
448 {
449     if(text && text[0]=='[') {
450         return;
451     }
452     SetDlgItemText(statuswnd, IDC_INFO, strdup(text));
453     processMessages();
454 }
455
456 void print_space(char*dest, char*msg, ULONGLONG size)
457 {
458     if(size < 1024)
459         sprintf(dest, "%s%d Bytes", msg, size);
460     else if(size < 1048576l)
461         sprintf(dest, "%s%.2f Kb", msg, size/1024.0);
462     else if(size < 1073741824l)
463         sprintf(dest, "%s%.2f Mb", msg, size/1048576.0);
464     else if(size < 1099511627776ll)
465         sprintf(dest, "%s%.2f Gb", msg, size/1073741824.0);
466     else
467         sprintf(dest, "%s%.2f Tb", msg, size/1125899906842624.0);
468 }
469
470 BOOL CALLBACK PropertySheetFunc3(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
471     HWND dialog = GetParent(hwnd);
472     if(message == WM_INITDIALOG) {
473         SetDlgItemText(hwnd, IDC_INFO, "Ready to install");
474
475         char buf[256];
476         print_space(buf, "Space required: ", crn_decompressed_size);
477         SetDlgItemText(hwnd, IDC_SPACE1, buf);
478         ULARGE_INTEGER available,total,totalfree;
479         available.QuadPart=0;
480         total.QuadPart=0;
481         totalfree.QuadPart=0;
482         if(GetDiskFreeSpaceEx(install_path, &available, &total, &totalfree)) {
483             print_space(buf, "Space available: ", available.QuadPart);
484         } else {
485             sprintf(buf, "Space available: [Error %d]", GetLastError());
486             if(GetLastError() == ERROR_FILE_NOT_FOUND && install_path[0] && install_path[1]==':') {
487                 /* installation directory does not yet exist */
488                 char path[3]={'c',':',0};
489                 path[0] = install_path[0];
490                 if(GetDiskFreeSpaceEx(path, &available, &total, &totalfree)) {
491                     print_space(buf, "Space available: ", available.QuadPart);
492                 }
493             } 
494         }
495         SetDlgItemText(hwnd, IDC_SPACE2, buf);
496     }
497     if(message == WM_NOTIFY && (((LPNMHDR)lParam)->code == PSN_WIZNEXT)) {
498         SetDlgItemText(hwnd, IDC_SPACE1, "");
499         SetDlgItemText(hwnd, IDC_SPACE2, "");
500         PropSheet_SetWizButtons(dialog, 0);
501         SendMessage(dialog, PSM_CANCELTOCLOSE, 0, 0); //makes wine display a warning
502         SetDlgItemText(hwnd, IDC_TITLE, "Installing files...");
503         statuswnd = hwnd;
504         status_t status;
505         status.status = PropertyArchiveStatus;
506         status.message = PropertyArchiveMessage;
507         status.error = PropertyArchiveError;
508         status.new_file = PropertyArchive_NewFile;
509         status.new_directory = PropertyArchive_NewDirectory;
510         int success = unpack_archive(crndata, crndata_len, install_path, &status);
511         memset(&status, 0, sizeof(status_t));
512         if(!success) {
513             MessageBox(0, "Couldn't extract all installation files", INSTALLER_NAME, MB_OK|MB_ICONERROR);
514             do_abort=1;
515             exit(1);
516         }
517         return 0;
518     }
519     return PropertySheetFuncCommon(hwnd, message, wParam, lParam, PSWIZB_BACK|PSWIZB_NEXT);
520 }
521
522 static HRESULT (WINAPI *f_SHGetSpecialFolderPath)(HWND hwnd, LPTSTR lpszPath, int nFolder, BOOL fCreate);
523
524 BOOL CALLBACK PropertySheetFunc4(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
525     if(message == WM_INITDIALOG) {
526         pdf2swf_path = concatPaths(install_path, "pdf2swf_gui.exe");
527         FILE*fi = fopen(pdf2swf_path, "rb");
528         if(fi) {
529             printf("a GUI program exists, creating desktop/startmenu links\n");
530             config_createLinks = 1;
531             fclose(fi);
532         }
533         if(!config_createLinks) {
534             SendDlgItemMessage(hwnd, IDC_STARTMENU, SW_HIDE, 0, 0);
535             SendDlgItemMessage(hwnd, IDC_DESKTOP, SW_HIDE, 0, 0);
536         }
537
538         SendDlgItemMessage(hwnd, IDC_STARTMENU, BM_SETCHECK, config_createStartmenu, 0);
539         SendDlgItemMessage(hwnd, IDC_DESKTOP, BM_SETCHECK, config_createDesktop, 0);
540     }
541     if(message == WM_COMMAND) {
542         if((wParam&0xffff) == IDC_STARTMENU) {
543             config_createStartmenu = SendDlgItemMessage(hwnd, IDC_STARTMENU, BM_GETCHECK, 0, 0);
544             config_createStartmenu^=1;
545             SendDlgItemMessage(hwnd, IDC_STARTMENU, BM_SETCHECK, config_createStartmenu, 0);
546             return 0;
547         }
548         if((wParam&0xffff) == IDC_DESKTOP) {
549             config_createDesktop = SendDlgItemMessage(hwnd, IDC_DESKTOP, BM_GETCHECK, 0, 0);
550             config_createDesktop^=1;
551             SendDlgItemMessage(hwnd, IDC_DESKTOP, BM_SETCHECK, config_createDesktop, 0);
552             return 0;
553         }
554     }
555
556     if(message == WM_NOTIFY && (((LPNMHDR)lParam)->code == PSN_WIZFINISH)) {
557         if(!addRegistryEntries(install_path)) {
558             MessageBox(0, "Couldn't create Registry Entries", INSTALLER_NAME, MB_OK|MB_ICONERROR);
559             return 1;
560         }
561
562         char mypath[MAX_PATH];
563         path_startmenu[0] = 0;
564         path_desktop[0] = 0;
565         if(config_forAllUsers) {
566             f_SHGetSpecialFolderPath(NULL, path_desktop, CSIDL_COMMON_DESKTOPDIRECTORY, 0);
567             f_SHGetSpecialFolderPath(NULL, path_startmenu, CSIDL_COMMON_PROGRAMS, 0);
568         }
569         /* get local program/desktop directory- this is both for forAllUsers=0 as well
570            as a fallback if the above didn't return any paths */
571         if(!path_startmenu[0]) {
572             f_SHGetSpecialFolderPath(NULL, path_startmenu, CSIDL_PROGRAMS, 0);
573         }
574         if(!path_desktop[0]) {
575             f_SHGetSpecialFolderPath(NULL, path_desktop, CSIDL_DESKTOPDIRECTORY, 0);
576         }
577         
578         char*uninstall_path = concatPaths(install_path, "uninstall.exe");
579
580         if(config_createLinks) {
581             if(config_createDesktop && path_desktop[0]) {
582                 char* linkName = concatPaths(path_desktop, "pdf2swf.lnk");
583                 printf("Creating desktop link %s -> %s\n", linkName, pdf2swf_path);
584                 if(!CreateShortcut(pdf2swf_path, "pdf2swf", linkName, 0, 0, 0, install_path)) {
585                     MessageBox(0, "Couldn't create desktop shortcut", INSTALLER_NAME, MB_OK);
586                     return 1;
587                 }
588             }
589             if(config_createStartmenu && path_startmenu[0]) {
590                 char* group = concatPaths(path_startmenu, "pdf2swf");
591                 CreateDirectory(group, 0);
592                 char* linkName = concatPaths(group, "pdf2swf.lnk");
593                 if(!CreateShortcut(pdf2swf_path, "pdf2swf", concatPaths(group, "pdf2swf.lnk"), 0, 0, 0, install_path) ||
594                    !CreateShortcut(uninstall_path, "uninstall", concatPaths(group, "uninstall.lnk"), 0, 0, 0, install_path)) {
595                     MessageBox(0, "Couldn't create start menu entry", INSTALLER_NAME, MB_OK);
596                     return 1;
597                 }
598             }
599         } else {
600             printf("not creating desktop/startmenu links\n");
601         }
602
603         char*uninstall_ini = concatPaths(install_path, "uninstall.ini");
604         addFile(uninstall_ini);
605         writeFileList(installedFiles, uninstall_ini);
606     }
607     return PropertySheetFuncCommon(hwnd, message, wParam, lParam, PSWIZB_FINISH);
608 }
609 #endif
610
611 #ifdef DEINSTALL
612
613 void findfiles(char*path, int*pos, char*data, int len, char del)
614 {
615     WIN32_FIND_DATA findFileData;
616     HANDLE hFind = FindFirstFile(concatPaths(path, "*"), &findFileData);
617     if(hFind == INVALID_HANDLE_VALUE)
618         return;
619     do {
620         if(findFileData.cFileName[0] == '.' &&
621            (findFileData.cFileName[0] == '.' || findFileData.cFileName == '\0'))
622             continue;
623         char*f = concatPaths(path, findFileData.cFileName);
624         if(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
625             findfiles(f, pos, data, len, del);
626             if(del) {
627                 RemoveDirectory(f);
628             }
629         } else {
630             int l = strlen(f);
631
632             /* don't list the uninstaller as file- it's going to be removed *after*
633                everything else is done */
634             char*uninstaller="uninstall.exe";
635             int ll = strlen(uninstaller);
636             if(l>=ll) {
637                 if(!strcasecmp(&f[l-ll],uninstaller)) {
638                     continue;
639                 }
640             }
641
642             if(data) {
643                 if(*pos+l <= len) {
644                     memcpy(&data[*pos], f, l);(*pos)+=l;
645                     data[(*pos)++] = '\r';
646                     data[(*pos)++] = '\n';
647                     data[(*pos)] = 0;
648                 }
649             } else {
650                 (*pos) += l+2;
651             }
652             if(del) {
653                 DeleteFile(f);
654             }
655         }
656     } while(FindNextFile(hFind, &findFileData));
657     FindClose(hFind);
658 }
659
660 static char*extrafiles = 0;
661
662 BOOL CALLBACK PropertySheetFunc5(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
663     HWND dialog = GetParent(hwnd);
664     if(message == WM_INITDIALOG) {
665         SetDlgItemText(hwnd, IDC_INFO, "Ready to deinstall");
666     }
667     if(message == WM_NOTIFY && (((LPNMHDR)lParam)->code == PSN_WIZNEXT)) {
668
669         filelist_t* list = readFileList("uninstall.ini");
670         if(!list) {
671             list = readFileList(concatPaths(install_path, "uninstall.ini"));
672             if(!list) {
673                 //Don't abort. If there's still something there, it'll be catched by the "extra files"
674                 //functionality later
675                 //MessageBox(0, "Couldn't determine installed files list- did you run uninstall twice?", INSTALLER_NAME, MB_OK);
676                 //exit(-1);
677             }
678         }
679         filelist_t* l = list;
680         int num = 0;
681         while(l) {num++;l=l->next;}
682
683         PropSheet_SetWizButtons(dialog, 0);
684         SendMessage(dialog, PSM_CANCELTOCLOSE, 0, 0);
685         SetDlgItemText(hwnd, IDC_TITLE, "Uninstalling files...");
686         SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, (LPARAM)MAKELONG(0,num));
687         num = 0;
688         l = list;
689         while(l) {
690             SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, num, 0);
691             if(l->type=='F')
692                 DeleteFile(l->filename);
693             else if(l->type=='D')
694                 RemoveDirectory(l->filename);
695             else if(l->type=='I') 
696                 /* skip- we will remove ourselves later */;
697             num++;l = l->next;
698         }
699
700         int len = 0;
701         findfiles(install_path, &len, 0, 0, 0);
702         if(len) {
703             extrafiles = malloc(len);
704             int pos = 0;
705             findfiles(install_path, &pos, extrafiles, len, 0);
706         } else {
707             PropSheet_RemovePage(dialog, 1, 0);
708         }
709         return 0;
710     }
711     return PropertySheetFuncCommon(hwnd, message, wParam, lParam, PSWIZB_BACK|PSWIZB_NEXT);
712 }
713
714 BOOL CALLBACK PropertySheetFunc6(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
715     if(message == WM_INITDIALOG) {
716         SendDlgItemMessage(hwnd, IDC_DELETEEXTRA, BM_SETCHECK, config_deleteextra, 0);
717         if(extrafiles) {
718             SetDlgItemText(hwnd, IDC_FILELIST, extrafiles);
719         }
720     }
721     if(message == WM_COMMAND) {
722         if((wParam&0xffff) == IDC_DELETEEXTRA) {
723             config_deleteextra = SendDlgItemMessage(hwnd, IDC_DELETEEXTRA, BM_GETCHECK, 0, 0);
724             config_deleteextra ^=1;
725             SendDlgItemMessage(hwnd, IDC_DELETEEXTRA, BM_SETCHECK, config_deleteextra, 0);
726             return 0;
727         }
728     }
729     if(message == WM_NOTIFY && (((LPNMHDR)lParam)->code == PSN_WIZNEXT)) {
730         if(config_deleteextra) {
731             int pos = 0;
732             findfiles(install_path, &pos, 0, 0, 1);
733         }
734     }
735     return PropertySheetFuncCommon(hwnd, message, wParam, lParam, PSWIZB_NEXT);
736 }
737
738 BOOL CALLBACK PropertySheetFunc7(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
739     if(message == WM_INITDIALOG) {
740         // ...
741     }
742     return PropertySheetFuncCommon(hwnd, message, wParam, lParam, PSWIZB_FINISH);
743 }
744 #endif
745
746 #ifndef PSP_HIDEHEADER
747 #define PSP_HIDEHEADER  2048
748 #endif
749
750 typedef struct _wizardpage {
751     DLGPROC function;
752     int resource;
753 } wizardpage_t;
754
755 void runPropertySheet(HWND parent)
756 {
757     PROPSHEETHEADER sheet;
758
759     wizardpage_t wpage[5] = {
760 #ifndef DEINSTALL
761         {PropertySheetFunc1, IDD_LICENSE},
762         {PropertySheetFunc2, IDD_INSTALLDIR},
763         {PropertySheetFunc3, IDD_PROGRESS},
764         {PropertySheetFunc4, IDD_FINISH},
765 #else
766         {PropertySheetFunc5, IDD_SURE},
767         {PropertySheetFunc6, IDD_EXTRAFILES},
768         {PropertySheetFunc7, IDD_DEINSTALLED},
769 #endif
770     };
771     int num = sizeof(wpage)/sizeof(wpage[0]);
772     HPROPSHEETPAGE pages[num];
773     int t;
774     for(t=0;t<num;t++) {
775         PROPSHEETPAGE page;
776         memset(&page, 0, sizeof(PROPSHEETPAGE));
777         page.dwSize = sizeof(PROPSHEETPAGE);
778         page.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
779         page.hInstance = me;
780         page.pfnDlgProc = wpage[t].function;
781         page.pszTemplate = MAKEINTRESOURCE(wpage[t].resource);
782         pages[t] = CreatePropertySheetPage(&page);
783     }
784
785     memset(&sheet, 0, sizeof(PROPSHEETHEADER));
786     sheet.dwSize = sizeof(PROPSHEETHEADER);
787     sheet.hInstance = me;
788     sheet.hwndParent = parent;
789     sheet.phpage = pages;
790     sheet.dwFlags = PSH_WIZARD;
791     sheet.nPages = num;
792     PropertySheet(&sheet);
793 }
794
795 #ifdef DEINSTALL
796
797 static void remove_self()
798 {
799     char exename[MAX_PATH];
800     char batname[MAX_PATH];
801     FILE *fp;
802
803     GetModuleFileName(NULL, exename, sizeof(exename));
804     sprintf(batname, "%s.bat", exename);
805     fp = fopen(batname, "w");
806     fprintf(fp, ":Repeat\n");
807     fprintf(fp, "del \"%s\"\n", exename);
808     fprintf(fp, "if exist \"%s\" goto Repeat\n", exename);
809     fprintf(fp, "del \"%s\"\n", batname);
810     fprintf(fp, "rmdir \"%s\"\n", install_path);
811     fclose(fp);
812
813     STARTUPINFO si;
814     PROCESS_INFORMATION pi;
815     memset(&si, 0, sizeof(si));
816     si.dwFlags = STARTF_USESHOWWINDOW;
817     si.wShowWindow = SW_HIDE;
818     si.cb = sizeof(si);
819     if (CreateProcess(NULL, batname, NULL, NULL, FALSE,
820                       CREATE_SUSPENDED | IDLE_PRIORITY_CLASS,
821                       NULL, "\\", &si, &pi)) {
822             SetThreadPriority(pi.hThread, THREAD_PRIORITY_IDLE);
823             SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
824             SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
825             CloseHandle(pi.hProcess);
826             ResumeThread(pi.hThread);
827             CloseHandle(pi.hThread);
828     }
829
830 }
831
832 int WINAPI WinMain(HINSTANCE _me,HINSTANCE hPrevInst,LPSTR lpszArgs, int nWinMode)
833 {
834     me = _me;
835     sprintf(registry_path, "Software\\%s\\%s\\InstallPath", SOFTWARE_DOMAIN, SOFTWARE_NAME);
836
837     HWND background = 0;
838     logo = LoadBitmap(me, "LOGO");
839
840     install_path = getRegistryEntry(registry_path);
841     if(!install_path || !install_path[0]) {
842         MessageBox(0, "Couldn't find software installation directory- did you run the deinstallation twice?", INSTALLER_NAME, MB_OK);
843         return 1;
844     }
845
846     CoInitialize(0);
847     InitCommonControls();
848
849     runPropertySheet(background);
850
851     remove_self();
852     return 0;
853 }
854 #else
855 int WINAPI WinMain(HINSTANCE _me,HINSTANCE hPrevInst,LPSTR lpszArgs, int nWinMode)
856 {
857     me = _me;
858     WNDCLASSEX wcl_background;
859     wcl_background.hInstance    = me;
860     wcl_background.lpfnWndProc  = WindowFunc;
861     wcl_background.lpszClassName= INSTALLER_NAME;
862     wcl_background.style        = CS_HREDRAW | CS_VREDRAW;
863     wcl_background.hIcon        = LoadIcon(NULL, IDI_APPLICATION);
864     wcl_background.hIconSm      = LoadIcon(NULL, IDI_APPLICATION);
865     wcl_background.hCursor      = LoadCursor(NULL, IDC_ARROW);
866     wcl_background.lpszMenuName = NULL; //no menu
867     wcl_background.cbClsExtra   = 0;
868     wcl_background.cbWndExtra   = 0;
869     wcl_background.hbrBackground= CreateSolidBrush(RGB(0, 0, 128));
870     wcl_background.cbSize       = sizeof(WNDCLASSEX);
871
872     sprintf(registry_path, "Software\\%s\\%s\\InstallPath", SOFTWARE_DOMAIN, SOFTWARE_NAME);
873
874     HINSTANCE shell32 = LoadLibrary("shell32.dll");
875     if(!shell32) {
876         MessageBox(0, "Could not load shell32.dll", INSTALLER_NAME, MB_OK);
877         return 1;
878     }
879     f_SHGetSpecialFolderPath = (HRESULT (WINAPI*)(HWND,LPTSTR,int,BOOL))GetProcAddress(shell32, "SHGetSpecialFolderPathA");
880     if(!f_SHGetSpecialFolderPath) {
881         MessageBox(0, "Could not load shell32.dll", INSTALLER_NAME, MB_OK);
882         return 1;
883     }
884         
885     HRESULT coret = CoInitialize(NULL);
886     if(FAILED(coret)) {
887         MessageBox(0, "Could not initialize COM interface", INSTALLER_NAME, MB_OK);
888         return 1;
889     }
890
891     path_programfiles[0] = 0;
892     f_SHGetSpecialFolderPath(NULL, path_programfiles, CSIDL_PROGRAM_FILES, 0);
893
894     if(!RegisterClassEx(&wcl_background)) {
895         MessageBox(0, "Could not register window background class", INSTALLER_NAME, MB_OK);
896         return 1;
897     }
898
899     HWND background = CreateWindow(wcl_background.lpszClassName, INSTALLER_NAME,
900                          0, 0, 0, 
901                          GetSystemMetrics(SM_CXFULLSCREEN),
902                          GetSystemMetrics(SM_CYFULLSCREEN),
903                          NULL, NULL, me, 
904                          (void*)"background");
905     
906     if(!background) {
907         MessageBox(0, "Could not create installation background window", INSTALLER_NAME, MB_OK);
908         return 1;
909     }
910
911     ShowWindow(background, SW_SHOWMAXIMIZED);
912     SetForegroundWindow(background);
913     UpdateWindow(background);
914
915     logo = LoadBitmap(me, "LOGO");
916
917     install_path = getRegistryEntry(registry_path);
918     if(!install_path || !install_path[0]) {
919         if(path_programfiles[0]) {
920             install_path = concatPaths(path_programfiles, SOFTWARE_NAME);
921         } else {
922             install_path = concatPaths("c:\\", SOFTWARE_NAME);
923         }
924     }
925
926     InitCommonControls();
927
928     runPropertySheet(background);
929     return 0;
930 }
931 #endif