Sync with trunk r43000
[reactos.git] / reactos / base / setup / reactos / reactos.c
1 /*
2 * ReactOS applications
3 * Copyright (C) 2004-2008 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id$
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS GUI first stage setup application
23 * FILE: subsys/system/reactos/reactos.c
24 * PROGRAMMERS: Eric Kohl
25 * Matthias Kupfer
26 * Dmitry Chapyshev (dmitry@reactos.org)
27 */
28
29 #include <windows.h>
30 #include <commctrl.h>
31 #include <tchar.h>
32 #include <setupapi.h>
33 #include <initguid.h>
34 #include <devguid.h>
35 #include <wine/unicode.h>
36
37 #include "resource.h"
38
39 /* GLOBALS ******************************************************************/
40
41 HFONT hTitleFont;
42
43 typedef struct _LANG
44 {
45 TCHAR LangId[9];
46 TCHAR LangName[128];
47 } LANG, *PLANG;
48
49 typedef struct _KBLAYOUT
50 {
51 TCHAR LayoutId[9];
52 TCHAR LayoutName[128];
53 TCHAR DllName[128];
54 } KBLAYOUT, *PKBLAYOUT;
55
56
57 // generic entries with simple 1:1 mapping
58 typedef struct _GENENTRY
59 {
60 TCHAR Id[24];
61 TCHAR Value[128];
62 } GENENTRY, *PGENENTRY;
63
64 struct
65 {
66 // Settings
67 LONG DestDiskNumber; // physical disk
68 LONG DestPartNumber; // partition on disk
69 LONG DestPartSize; // if partition doesn't exist, size of partition
70 LONG FSType; // file system type on partition
71 LONG MBRInstallType; // install bootloader
72 LONG FormatPart; // type of format the partition
73 LONG SelectedLangId; // selected language (table index)
74 LONG SelectedKBLayout; // selected keyboard layout (table index)
75 TCHAR InstallDir[MAX_PATH]; // installation directory on hdd
76 LONG SelectedComputer; // selected computer type (table index)
77 LONG SelectedDisplay; // selected display type (table index)
78 LONG SelectedKeyboard; // selected keyboard type (table index)
79 BOOLEAN RepairUpdateFlag; // flag for update/repair an installed reactos
80 // txtsetup.sif data
81 LONG DefaultLang; // default language (table index)
82 PLANG pLanguages;
83 LONG LangCount;
84 LONG DefaultKBLayout; // default keyboard layout (table index)
85 PKBLAYOUT pKbLayouts;
86 LONG KbLayoutCount;
87 PGENENTRY pComputers;
88 LONG CompCount;
89 PGENENTRY pDisplays;
90 LONG DispCount;
91 PGENENTRY pKeyboards;
92 LONG KeybCount;
93 } SetupData;
94
95 typedef struct _IMGINFO
96 {
97 HBITMAP hBitmap;
98 INT cxSource;
99 INT cySource;
100 } IMGINFO, *PIMGINFO;
101
102 TCHAR abort_msg[512], abort_title[64];
103 HINSTANCE hInstance;
104 BOOL isUnattend;
105
106 /* FUNCTIONS ****************************************************************/
107
108 static VOID
109 CenterWindow(HWND hWnd)
110 {
111 HWND hWndParent;
112 RECT rcParent;
113 RECT rcWindow;
114
115 hWndParent = GetParent(hWnd);
116 if (hWndParent == NULL)
117 hWndParent = GetDesktopWindow();
118
119 GetWindowRect(hWndParent, &rcParent);
120 GetWindowRect(hWnd, &rcWindow);
121
122 SetWindowPos(hWnd,
123 HWND_TOP,
124 ((rcParent.right - rcParent.left) - (rcWindow.right - rcWindow.left)) / 2,
125 ((rcParent.bottom - rcParent.top) - (rcWindow.bottom - rcWindow.top)) / 2,
126 0,
127 0,
128 SWP_NOSIZE);
129 }
130
131 static HFONT
132 CreateTitleFont(VOID)
133 {
134 NONCLIENTMETRICS ncm;
135 LOGFONT LogFont;
136 HDC hdc;
137 INT FontSize;
138 HFONT hFont;
139
140 ncm.cbSize = sizeof(NONCLIENTMETRICS);
141 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
142
143 LogFont = ncm.lfMessageFont;
144 LogFont.lfWeight = FW_BOLD;
145 _tcscpy(LogFont.lfFaceName, _T("MS Shell Dlg"));
146
147 hdc = GetDC(NULL);
148 FontSize = 12;
149 LogFont.lfHeight = 0 - GetDeviceCaps (hdc, LOGPIXELSY) * FontSize / 72;
150 hFont = CreateFontIndirect(&LogFont);
151 ReleaseDC(NULL, hdc);
152
153 return hFont;
154 }
155
156 static VOID
157 InitImageInfo(PIMGINFO ImgInfo)
158 {
159 BITMAP bitmap;
160
161 ZeroMemory(ImgInfo, sizeof(*ImgInfo));
162
163 ImgInfo->hBitmap = LoadImage(hInstance,
164 MAKEINTRESOURCE(IDB_ROSLOGO),
165 IMAGE_BITMAP,
166 0,
167 0,
168 LR_DEFAULTCOLOR);
169
170 if (ImgInfo->hBitmap != NULL)
171 {
172 GetObject(ImgInfo->hBitmap, sizeof(BITMAP), &bitmap);
173
174 ImgInfo->cxSource = bitmap.bmWidth;
175 ImgInfo->cySource = bitmap.bmHeight;
176 }
177 }
178
179 static INT_PTR CALLBACK
180 StartDlgProc(HWND hwndDlg,
181 UINT uMsg,
182 WPARAM wParam,
183 LPARAM lParam)
184 {
185 switch (uMsg)
186 {
187 case WM_INITDIALOG:
188 {
189 HWND hwndControl;
190 DWORD dwStyle;
191
192 hwndControl = GetParent(hwndDlg);
193
194 /* Center the wizard window */
195 CenterWindow (hwndControl);
196
197 dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE);
198 SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
199
200 /* Hide and disable the 'Cancel' button at the moment,
201 * we use this button to cancel the setup process
202 * like F3 in usetup
203 */
204 hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL);
205 ShowWindow (hwndControl, SW_HIDE);
206 EnableWindow (hwndControl, FALSE);
207
208 /* Set title font */
209 SendDlgItemMessage(hwndDlg,
210 IDC_STARTTITLE,
211 WM_SETFONT,
212 (WPARAM)hTitleFont,
213 (LPARAM)TRUE);
214 }
215 break;
216
217 case WM_NOTIFY:
218 {
219 LPNMHDR lpnm = (LPNMHDR)lParam;
220
221 switch (lpnm->code)
222 {
223 case PSN_SETACTIVE:
224 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
225 break;
226 default:
227 break;
228 }
229 }
230 break;
231
232 default:
233 break;
234
235 }
236
237 return FALSE;
238 }
239
240 static INT_PTR CALLBACK
241 LangSelDlgProc(HWND hwndDlg,
242 UINT uMsg,
243 WPARAM wParam,
244 LPARAM lParam)
245 {
246 PIMGINFO pImgInfo;
247 LONG i;
248 LRESULT tindex;
249 HWND hList;
250
251 pImgInfo = (PIMGINFO)GetWindowLongPtr(hwndDlg, DWLP_USER);
252
253 switch (uMsg)
254 {
255 case WM_INITDIALOG:
256 {
257 HWND hwndControl;
258 DWORD dwStyle;
259
260 hwndControl = GetParent(hwndDlg);
261
262 dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE);
263 SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
264
265 hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL);
266 ShowWindow (hwndControl, SW_SHOW);
267 EnableWindow (hwndControl, TRUE);
268
269 pImgInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IMGINFO));
270 if (pImgInfo == NULL)
271 {
272 EndDialog(hwndDlg, 0);
273 return FALSE;
274 }
275
276 SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)pImgInfo);
277
278 InitImageInfo(pImgInfo);
279
280 /* Set title font */
281 /*SendDlgItemMessage(hwndDlg,
282 IDC_STARTTITLE,
283 WM_SETFONT,
284 (WPARAM)hTitleFont,
285 (LPARAM)TRUE);*/
286
287 hList = GetDlgItem(hwndDlg, IDC_LANGUAGES);
288
289 for (i=0; i < SetupData.LangCount; i++)
290 {
291 tindex = SendMessage(hList, CB_ADDSTRING, (WPARAM) 0, (LPARAM) SetupData.pLanguages[i].LangName);
292 SendMessage(hList, CB_SETITEMDATA, tindex, i);
293 if (SetupData.DefaultLang == i)
294 SendMessage(hList, CB_SETCURSEL, (WPARAM) tindex,(LPARAM) 0);
295 }
296
297 hList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT);
298
299 for (i=0; i < SetupData.KbLayoutCount; i++)
300 {
301 tindex = SendMessage(hList, CB_ADDSTRING, (WPARAM) 0, (LPARAM)SetupData.pKbLayouts[i].LayoutName);
302 SendMessage(hList, CB_SETITEMDATA, tindex, i);
303 if (SetupData.DefaultKBLayout == i)
304 SendMessage(hList,CB_SETCURSEL,(WPARAM)tindex,(LPARAM)0);
305 }
306 }
307 break;
308
309 case WM_DRAWITEM:
310 {
311 LPDRAWITEMSTRUCT lpDrawItem;
312 lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
313
314 if (lpDrawItem->CtlID == IDB_ROSLOGO)
315 {
316 HDC hdcMem;
317 LONG left;
318
319 /* position image in centre of dialog */
320 left = (lpDrawItem->rcItem.right - pImgInfo->cxSource) / 2;
321
322 hdcMem = CreateCompatibleDC(lpDrawItem->hDC);
323 if (hdcMem != NULL)
324 {
325 SelectObject(hdcMem, pImgInfo->hBitmap);
326 BitBlt(lpDrawItem->hDC,
327 left,
328 lpDrawItem->rcItem.top,
329 lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
330 lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
331 hdcMem,
332 0,
333 0,
334 SRCCOPY);
335 DeleteDC(hdcMem);
336 }
337 }
338 return TRUE;
339 }
340
341 case WM_NOTIFY:
342 {
343 LPNMHDR lpnm = (LPNMHDR)lParam;
344
345 switch (lpnm->code)
346 {
347 case PSN_SETACTIVE:
348 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK);
349 break;
350
351 case PSN_QUERYCANCEL:
352 SetWindowLongPtr(hwndDlg,
353 DWL_MSGRESULT,
354 MessageBox(GetParent(hwndDlg),
355 abort_msg,
356 abort_title,
357 MB_YESNO | MB_ICONQUESTION) != IDYES);
358 return TRUE;
359
360 case PSN_WIZNEXT: // set the selected data
361 {
362 hList =GetDlgItem(hwndDlg, IDC_LANGUAGES);
363 tindex = SendMessage(hList,CB_GETCURSEL, (WPARAM)0, (LPARAM)0);
364
365 if (tindex != CB_ERR)
366 {
367 WORD LangID;
368 SetupData.SelectedLangId = SendMessage(hList, CB_GETITEMDATA, (WPARAM) tindex, (LPARAM) 0);
369 LangID = _tcstol(SetupData.pLanguages[SetupData.SelectedLangId].LangId, NULL, 16);
370 SetThreadLocale(MAKELCID(LangID, SORT_DEFAULT));
371 // FIXME: need to reload all resource to force
372 // the new language setting
373 }
374
375 hList = GetDlgItem(hwndDlg, IDC_KEYLAYOUT);
376 tindex = SendMessage(hList,CB_GETCURSEL, (WPARAM)0, (LPARAM)0);
377 if (tindex != CB_ERR)
378 {
379 SetupData.SelectedKBLayout = SendMessage(hList, CB_GETITEMDATA, (WPARAM) tindex, (LPARAM) 0);
380 }
381 return TRUE;
382 }
383
384 default:
385 break;
386 }
387 }
388 break;
389
390 default:
391 break;
392
393 }
394 return FALSE;
395 }
396
397 static INT_PTR CALLBACK
398 TypeDlgProc(HWND hwndDlg,
399 UINT uMsg,
400 WPARAM wParam,
401 LPARAM lParam)
402 {
403 switch (uMsg)
404 {
405 case WM_INITDIALOG:
406 {
407 HWND hwndControl;
408 DWORD dwStyle;
409
410 hwndControl = GetParent(hwndDlg);
411
412 dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE);
413 SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
414
415 CheckDlgButton(hwndDlg, IDC_INSTALL, BST_CHECKED);
416
417 /* Set title font */
418 /*SendDlgItemMessage(hwndDlg,
419 IDC_STARTTITLE,
420 WM_SETFONT,
421 (WPARAM)hTitleFont,
422 (LPARAM)TRUE);*/
423 }
424 break;
425
426 case WM_NOTIFY:
427 {
428 LPNMHDR lpnm = (LPNMHDR)lParam;
429
430 switch (lpnm->code)
431 {
432 case PSN_SETACTIVE:
433 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK);
434 break;
435
436 case PSN_QUERYCANCEL:
437 SetWindowLongPtr(hwndDlg,
438 DWL_MSGRESULT,
439 MessageBox(GetParent(hwndDlg),
440 abort_msg,
441 abort_title,
442 MB_YESNO | MB_ICONQUESTION) != IDYES);
443 return TRUE;
444
445 case PSN_WIZNEXT: // set the selected data
446 SetupData.RepairUpdateFlag = !(SendMessage(GetDlgItem(hwndDlg, IDC_INSTALL),
447 BM_GETCHECK,
448 (WPARAM) 0,
449 (LPARAM) 0) == BST_CHECKED);
450 return TRUE;
451
452 default:
453 break;
454 }
455 }
456 break;
457
458 default:
459 break;
460
461 }
462 return FALSE;
463 }
464
465 static INT_PTR CALLBACK
466 DeviceDlgProc(HWND hwndDlg,
467 UINT uMsg,
468 WPARAM wParam,
469 LPARAM lParam)
470 {
471 LONG i;
472 LRESULT tindex;
473 HWND hList;
474
475 switch (uMsg)
476 {
477 case WM_INITDIALOG:
478 {
479 HWND hwndControl;
480 DWORD dwStyle;
481
482 hwndControl = GetParent(hwndDlg);
483
484 dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE);
485 SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
486
487 /* Set title font */
488 /*SendDlgItemMessage(hwndDlg,
489 IDC_STARTTITLE,
490 WM_SETFONT,
491 (WPARAM)hTitleFont,
492 (LPARAM)TRUE);*/
493
494 hList = GetDlgItem(hwndDlg, IDC_COMPUTER);
495
496 for (i=0; i < SetupData.CompCount; i++)
497 {
498 tindex = SendMessage(hList, CB_ADDSTRING, (WPARAM) 0, (LPARAM) SetupData.pComputers[i].Value);
499 SendMessage(hList, CB_SETITEMDATA, tindex, i);
500 }
501 SendMessage(hList, CB_SETCURSEL, 0, 0); // set first as default
502
503 hList = GetDlgItem(hwndDlg, IDC_DISPLAY);
504
505 for (i=0; i < SetupData.DispCount; i++)
506 {
507 tindex = SendMessage(hList, CB_ADDSTRING, (WPARAM) 0, (LPARAM) SetupData.pDisplays[i].Value);
508 SendMessage(hList, CB_SETITEMDATA, tindex, i);
509 }
510 SendMessage(hList, CB_SETCURSEL, 0, 0); // set first as default
511
512 hList = GetDlgItem(hwndDlg, IDC_KEYBOARD);
513
514 for (i=0; i < SetupData.KeybCount; i++)
515 {
516 tindex = SendMessage(hList,CB_ADDSTRING,(WPARAM)0,(LPARAM)SetupData.pKeyboards[i].Value);
517 SendMessage(hList,CB_SETITEMDATA,tindex,i);
518 }
519 SendMessage(hList,CB_SETCURSEL,0,0); // set first as default
520 }
521 break;
522
523 case WM_NOTIFY:
524 {
525 LPNMHDR lpnm = (LPNMHDR)lParam;
526
527 switch (lpnm->code)
528 {
529 case PSN_SETACTIVE:
530 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK);
531 break;
532
533 case PSN_QUERYCANCEL:
534 SetWindowLongPtr(hwndDlg,
535 DWL_MSGRESULT,
536 MessageBox(GetParent(hwndDlg),
537 abort_msg,
538 abort_title,
539 MB_YESNO | MB_ICONQUESTION) != IDYES);
540 return TRUE;
541
542 case PSN_WIZNEXT: // set the selected data
543 {
544 hList = GetDlgItem(hwndDlg, IDC_COMPUTER);
545
546 tindex = SendMessage(hList, CB_GETCURSEL, (WPARAM) 0, (LPARAM) 0);
547 if (tindex != CB_ERR)
548 {
549 SetupData.SelectedComputer = SendMessage(hList,
550 CB_GETITEMDATA,
551 (WPARAM) tindex,
552 (LPARAM) 0);
553 }
554
555 hList = GetDlgItem(hwndDlg, IDC_DISPLAY);
556
557 tindex = SendMessage(hList, CB_GETCURSEL, (WPARAM) 0, (LPARAM) 0);
558 if (tindex != CB_ERR)
559 {
560 SetupData.SelectedDisplay = SendMessage(hList,
561 CB_GETITEMDATA,
562 (WPARAM) tindex,
563 (LPARAM) 0);
564 }
565
566 hList =GetDlgItem(hwndDlg, IDC_KEYBOARD);
567
568 tindex = SendMessage(hList, CB_GETCURSEL, (WPARAM) 0, (LPARAM) 0);
569 if (tindex != CB_ERR)
570 {
571 SetupData.SelectedKeyboard = SendMessage(hList,
572 CB_GETITEMDATA,
573 (WPARAM) tindex,
574 (LPARAM) 0);
575 }
576 return TRUE;
577 }
578
579 default:
580 break;
581 }
582 }
583 break;
584
585 default:
586 break;
587
588 }
589 return FALSE;
590 }
591
592 static INT_PTR CALLBACK
593 MoreOptDlgProc(HWND hwndDlg,
594 UINT uMsg,
595 WPARAM wParam,
596 LPARAM lParam)
597 {
598 switch (uMsg)
599 {
600 case WM_INITDIALOG:
601 {
602 CheckDlgButton(hwndDlg, IDC_INSTFREELDR, BST_CHECKED);
603 SendMessage(GetDlgItem(hwndDlg, IDC_PATH),
604 WM_SETTEXT,
605 (WPARAM) 0,
606 (LPARAM) SetupData.InstallDir);
607 }
608 break;
609
610 case WM_COMMAND:
611 {
612 switch(LOWORD(wParam))
613 {
614 case IDOK:
615 {
616 SendMessage(GetDlgItem(hwndDlg, IDC_PATH),
617 WM_GETTEXT,
618 (WPARAM) sizeof(SetupData.InstallDir) / sizeof(TCHAR),
619 (LPARAM) SetupData.InstallDir);
620
621 EndDialog(hwndDlg, IDOK);
622 return TRUE;
623 }
624
625 case IDCANCEL:
626 {
627 EndDialog(hwndDlg, IDCANCEL);
628 return TRUE;
629 }
630 }
631 }
632 }
633
634 return FALSE;
635 }
636
637 static INT_PTR CALLBACK
638 PartitionDlgProc(HWND hwndDlg,
639 UINT uMsg,
640 WPARAM wParam,
641 LPARAM lParam)
642 {
643 switch (uMsg)
644 {
645 case WM_INITDIALOG:
646 break;
647 case WM_COMMAND:
648 {
649 switch(LOWORD(wParam))
650 {
651 case IDOK:
652 EndDialog(hwndDlg, IDOK);
653 return TRUE;
654 case IDCANCEL:
655 EndDialog(hwndDlg, IDCANCEL);
656 return TRUE;
657 }
658 }
659 }
660 return FALSE;
661 }
662
663 static INT_PTR CALLBACK
664 DriveDlgProc(HWND hwndDlg,
665 UINT uMsg,
666 WPARAM wParam,
667 LPARAM lParam)
668 {
669 #if 0
670 HDEVINFO h;
671 HWND hList;
672 SP_DEVINFO_DATA DevInfoData;
673 DWORD i;
674 #endif
675 switch (uMsg)
676 {
677 case WM_INITDIALOG:
678 {
679 HWND hwndControl;
680 DWORD dwStyle;
681
682 hwndControl = GetParent(hwndDlg);
683
684 dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE);
685 SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
686
687 /* Set title font */
688 /*SendDlgItemMessage(hwndDlg,
689 IDC_STARTTITLE,
690 WM_SETFONT,
691 (WPARAM)hTitleFont,
692 (LPARAM)TRUE);*/
693 #if 0
694 h = SetupDiGetClassDevs(&GUID_DEVCLASS_DISKDRIVE, NULL, NULL, DIGCF_PRESENT);
695 if (h != INVALID_HANDLE_VALUE)
696 {
697 hList =GetDlgItem(hwndDlg, IDC_PARTITION);
698 DevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
699 for (i=0; SetupDiEnumDeviceInfo(h, i, &DevInfoData); i++)
700 {
701 DWORD DataT;
702 LPTSTR buffer = NULL;
703 DWORD buffersize = 0;
704
705 while (!SetupDiGetDeviceRegistryProperty(h,
706 &DevInfoData,
707 SPDRP_DEVICEDESC,
708 &DataT,
709 (PBYTE)buffer,
710 buffersize,
711 &buffersize))
712 {
713 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
714 {
715 if (buffer) LocalFree(buffer);
716 buffer = LocalAlloc(LPTR, buffersize * 2);
717 }
718 else
719 break;
720 }
721 if (buffer)
722 {
723 SendMessage(hList, LB_ADDSTRING, (WPARAM) 0, (LPARAM) buffer);
724 LocalFree(buffer);
725 }
726 }
727 SetupDiDestroyDeviceInfoList(h);
728 }
729 #endif
730 }
731 break;
732
733 case WM_COMMAND:
734 {
735 switch(LOWORD(wParam))
736 {
737 case IDC_PARTMOREOPTS:
738 DialogBox(hInstance,
739 MAKEINTRESOURCE(IDD_BOOTOPTIONS),
740 hwndDlg,
741 (DLGPROC) MoreOptDlgProc);
742 break;
743 case IDC_PARTCREATE:
744 DialogBox(hInstance,
745 MAKEINTRESOURCE(IDD_PARTITION),
746 hwndDlg,
747 (DLGPROC) PartitionDlgProc);
748 break;
749 case IDC_PARTDELETE:
750 break;
751 }
752 break;
753 }
754
755 case WM_NOTIFY:
756 {
757 LPNMHDR lpnm = (LPNMHDR)lParam;
758
759 switch (lpnm->code)
760 {
761 case PSN_SETACTIVE:
762 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK);
763 break;
764
765 case PSN_QUERYCANCEL:
766 SetWindowLongPtr(hwndDlg,
767 DWL_MSGRESULT,
768 MessageBox(GetParent(hwndDlg),
769 abort_msg,
770 abort_title,
771 MB_YESNO | MB_ICONQUESTION) != IDYES);
772 return TRUE;
773
774 default:
775 break;
776 }
777 }
778 break;
779
780 default:
781 break;
782
783 }
784
785 return FALSE;
786 }
787
788 static INT_PTR CALLBACK
789 SummaryDlgProc(HWND hwndDlg,
790 UINT uMsg,
791 WPARAM wParam,
792 LPARAM lParam)
793 {
794 switch (uMsg)
795 {
796 case WM_INITDIALOG:
797 {
798 HWND hwndControl;
799 DWORD dwStyle;
800
801 hwndControl = GetParent(hwndDlg);
802
803 dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE);
804 SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
805
806 hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL);
807 ShowWindow(hwndControl, SW_HIDE);
808 EnableWindow(hwndControl, FALSE);
809
810 /* Set title font */
811 /*SendDlgItemMessage(hwndDlg,
812 IDC_STARTTITLE,
813 WM_SETFONT,
814 (WPARAM)hTitleFont,
815 (LPARAM)TRUE);*/
816 }
817 break;
818
819 case WM_NOTIFY:
820 {
821 LPNMHDR lpnm = (LPNMHDR)lParam;
822
823 switch (lpnm->code)
824 {
825 case PSN_SETACTIVE:
826 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK);
827 break;
828
829 default:
830 break;
831 }
832 }
833 break;
834
835 default:
836 break;
837 }
838
839 return FALSE;
840 }
841
842 static INT_PTR CALLBACK
843 ProcessDlgProc(HWND hwndDlg,
844 UINT uMsg,
845 WPARAM wParam,
846 LPARAM lParam)
847 {
848 switch (uMsg)
849 {
850 case WM_INITDIALOG:
851 {
852 HWND hwndControl;
853 DWORD dwStyle;
854
855 hwndControl = GetParent(hwndDlg);
856
857 dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE);
858 SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
859
860 hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL);
861 ShowWindow(hwndControl, SW_HIDE);
862 EnableWindow(hwndControl, FALSE);
863
864 /* Set title font */
865 /*SendDlgItemMessage(hwndDlg,
866 IDC_STARTTITLE,
867 WM_SETFONT,
868 (WPARAM)hTitleFont,
869 (LPARAM)TRUE);*/
870 }
871 break;
872
873 case WM_NOTIFY:
874 {
875 LPNMHDR lpnm = (LPNMHDR)lParam;
876
877 switch (lpnm->code)
878 {
879 case PSN_SETACTIVE:
880 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT);
881 // disable all buttons during installation process
882 // PropSheet_SetWizButtons(GetParent(hwndDlg), 0 );
883 break;
884
885 default:
886 break;
887 }
888 }
889 break;
890
891 default:
892 break;
893
894 }
895
896 return FALSE;
897 }
898
899 static INT_PTR CALLBACK
900 RestartDlgProc(HWND hwndDlg,
901 UINT uMsg,
902 WPARAM wParam,
903 LPARAM lParam)
904 {
905 switch (uMsg)
906 {
907 case WM_INITDIALOG:
908 {
909 HWND hwndControl;
910 DWORD dwStyle;
911
912 hwndControl = GetParent(hwndDlg);
913
914 dwStyle = GetWindowLongPtr(hwndControl, GWL_STYLE);
915 SetWindowLongPtr(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU);
916
917 /* Set title font */
918 /*SendDlgItemMessage(hwndDlg,
919 IDC_STARTTITLE,
920 WM_SETFONT,
921 (WPARAM)hTitleFont,
922 (LPARAM)TRUE);*/
923 }
924 break;
925
926 case WM_TIMER:
927 {
928 INT Position;
929 HWND hWndProgress;
930
931 hWndProgress = GetDlgItem(hwndDlg, IDC_RESTART_PROGRESS);
932 Position = SendMessage(hWndProgress, PBM_GETPOS, 0, 0);
933 if (Position == 300)
934 {
935 KillTimer(hwndDlg, 1);
936 PropSheet_PressButton(GetParent(hwndDlg), PSBTN_FINISH);
937 }
938 else
939 {
940 SendMessage(hWndProgress, PBM_SETPOS, Position + 1, 0);
941 }
942 return TRUE;
943 }
944
945 case WM_DESTROY:
946 return TRUE;
947
948 case WM_NOTIFY:
949 {
950 LPNMHDR lpnm = (LPNMHDR)lParam;
951
952 switch (lpnm->code)
953 {
954 case PSN_SETACTIVE: // Only "Finish" for closing the App
955 {
956 PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_FINISH);
957 SendDlgItemMessage(hwndDlg, IDC_RESTART_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, 300));
958 SendDlgItemMessage(hwndDlg, IDC_RESTART_PROGRESS, PBM_SETPOS, 0, 0);
959 SetTimer(hwndDlg, 1, 50, NULL);
960 }
961 break;
962
963 default:
964 break;
965 }
966 }
967 break;
968
969 default:
970 break;
971
972 }
973
974 return FALSE;
975 }
976
977 void LoadSetupData()
978 {
979 WCHAR szPath[MAX_PATH];
980 TCHAR tmp[10];
981 WCHAR *ch;
982 HINF hTxtsetupSif;
983 INFCONTEXT InfContext;
984 //TCHAR szValue[MAX_PATH];
985 DWORD LineLength;
986 LONG Count;
987
988 GetModuleFileNameW(NULL,szPath,MAX_PATH);
989 ch = strrchrW(szPath,L'\\');
990 if (ch != NULL)
991 *ch = L'\0';
992
993 wcscat(szPath, L"\\txtsetup.sif");
994 hTxtsetupSif = SetupOpenInfFileW(szPath, NULL, INF_STYLE_OLDNT, NULL);
995 if (hTxtsetupSif != INVALID_HANDLE_VALUE)
996 {
997 // get language list
998 Count = SetupGetLineCount(hTxtsetupSif, _T("Language"));
999 if (Count > 0)
1000 {
1001 SetupData.pLanguages = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LANG) * Count);
1002 if (SetupData.pLanguages != NULL)
1003 {
1004 SetupData.LangCount = Count;
1005 Count = 0;
1006 if (SetupFindFirstLine(hTxtsetupSif, _T("Language"), NULL, &InfContext))
1007 {
1008 do
1009 {
1010 SetupGetStringField(&InfContext,
1011 0,
1012 SetupData.pLanguages[Count].LangId,
1013 sizeof(SetupData.pLanguages[Count].LangId) / sizeof(TCHAR),
1014 &LineLength);
1015
1016 SetupGetStringField(&InfContext,
1017 1,
1018 SetupData.pLanguages[Count].LangName,
1019 sizeof(SetupData.pLanguages[Count].LangName) / sizeof(TCHAR),
1020 &LineLength);
1021 ++Count;
1022 }
1023 while (SetupFindNextLine(&InfContext, &InfContext) && Count < SetupData.LangCount);
1024 }
1025 }
1026 }
1027
1028 // get keyboard layout list
1029 Count = SetupGetLineCount(hTxtsetupSif, _T("KeyboardLayout"));
1030 if (Count > 0)
1031 {
1032 SetupData.pKbLayouts = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(KBLAYOUT) * Count);
1033 if (SetupData.pKbLayouts != NULL)
1034 {
1035 SetupData.KbLayoutCount = Count;
1036 Count = 0;
1037 if (SetupFindFirstLine(hTxtsetupSif, _T("KeyboardLayout"), NULL, &InfContext))
1038 {
1039 do
1040 {
1041 SetupGetStringField(&InfContext,
1042 0,
1043 SetupData.pKbLayouts[Count].LayoutId,
1044 sizeof(SetupData.pKbLayouts[Count].LayoutId) / sizeof(TCHAR),
1045 &LineLength);
1046
1047 SetupGetStringField(&InfContext,
1048 1,
1049 SetupData.pKbLayouts[Count].LayoutName,
1050 sizeof(SetupData.pKbLayouts[Count].LayoutName) / sizeof(TCHAR),
1051 &LineLength);
1052 ++Count;
1053 }
1054 while (SetupFindNextLine(&InfContext, &InfContext) && Count < SetupData.LangCount);
1055 }
1056 }
1057 }
1058
1059 // get default for keyboard and language
1060 SetupData.DefaultKBLayout = -1;
1061 SetupData.DefaultLang = -1;
1062
1063 // TODO: get defaults from underlaying running system
1064 if (SetupFindFirstLine(hTxtsetupSif, _T("NLS"), _T("DefaultLayout"), &InfContext))
1065 {
1066 SetupGetStringField(&InfContext, 1, tmp, sizeof(tmp) / sizeof(TCHAR), &LineLength);
1067 for (Count = 0; Count < SetupData.KbLayoutCount; Count++)
1068 if (_tcscmp(tmp, SetupData.pKbLayouts[Count].LayoutId) == 0)
1069 {
1070 SetupData.DefaultKBLayout = Count;
1071 break;
1072 }
1073 }
1074
1075 if (SetupFindFirstLine(hTxtsetupSif, _T("NLS"), _T("DefaultLanguage"), &InfContext))
1076 {
1077 SetupGetStringField(&InfContext, 1, tmp, sizeof(tmp) / sizeof(TCHAR), &LineLength);
1078 for (Count = 0; Count < SetupData.LangCount; Count++)
1079 if (_tcscmp(tmp, SetupData.pLanguages[Count].LangId) == 0)
1080 {
1081 SetupData.DefaultLang = Count;
1082 break;
1083 }
1084 }
1085
1086 // get computers list
1087 Count = SetupGetLineCount(hTxtsetupSif, _T("Computer"));
1088 if (Count > 0)
1089 {
1090 SetupData.pComputers = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(GENENTRY) * Count);
1091 if (SetupData.pComputers != NULL)
1092 {
1093 SetupData.CompCount = Count;
1094 Count = 0;
1095 if (SetupFindFirstLine(hTxtsetupSif, _T("Computer"), NULL, &InfContext))
1096 {
1097 do
1098 {
1099 SetupGetStringField(&InfContext,
1100 0,
1101 SetupData.pComputers[Count].Id,
1102 sizeof(SetupData.pComputers[Count].Id) / sizeof(TCHAR),
1103 &LineLength);
1104
1105 SetupGetStringField(&InfContext,
1106 1,
1107 SetupData.pComputers[Count].Value,
1108 sizeof(SetupData.pComputers[Count].Value) / sizeof(TCHAR),
1109 &LineLength);
1110 ++Count;
1111 }
1112 while (SetupFindNextLine(&InfContext, &InfContext) && Count < SetupData.CompCount);
1113 }
1114 }
1115 }
1116
1117 // get display list
1118 Count = SetupGetLineCount(hTxtsetupSif, _T("Display"));
1119 if (Count > 0)
1120 {
1121 SetupData.pDisplays = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(GENENTRY) * Count);
1122 if (SetupData.pDisplays != NULL)
1123 {
1124 SetupData.DispCount = Count;
1125 Count = 0;
1126
1127 if (SetupFindFirstLine(hTxtsetupSif, _T("Display"), NULL, &InfContext))
1128 {
1129 do
1130 {
1131 SetupGetStringField(&InfContext,
1132 0,
1133 SetupData.pDisplays[Count].Id,
1134 sizeof(SetupData.pDisplays[Count].Id) / sizeof(TCHAR),
1135 &LineLength);
1136
1137 SetupGetStringField(&InfContext,
1138 1,
1139 SetupData.pDisplays[Count].Value,
1140 sizeof(SetupData.pDisplays[Count].Value) / sizeof(TCHAR),
1141 &LineLength);
1142 ++Count;
1143 }
1144 while (SetupFindNextLine(&InfContext, &InfContext) && Count < SetupData.DispCount);
1145 }
1146 }
1147 }
1148
1149 // get keyboard list
1150 Count = SetupGetLineCount(hTxtsetupSif, _T("Keyboard"));
1151 if (Count > 0)
1152 {
1153 SetupData.pKeyboards = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(GENENTRY) * Count);
1154 if (SetupData.pKeyboards != NULL)
1155 {
1156 SetupData.KeybCount = Count;
1157 Count = 0;
1158
1159 if (SetupFindFirstLine(hTxtsetupSif, _T("Keyboard"), NULL, &InfContext))
1160 {
1161 do
1162 {
1163 SetupGetStringField(&InfContext,
1164 0,
1165 SetupData.pKeyboards[Count].Id,
1166 sizeof(SetupData.pKeyboards[Count].Id) / sizeof(TCHAR),
1167 &LineLength);
1168
1169 SetupGetStringField(&InfContext,
1170 1,
1171 SetupData.pKeyboards[Count].Value,
1172 sizeof(SetupData.pKeyboards[Count].Value) / sizeof(TCHAR),
1173 &LineLength);
1174 ++Count;
1175 }
1176 while (SetupFindNextLine(&InfContext, &InfContext) && Count < SetupData.KeybCount);
1177 }
1178 }
1179 }
1180
1181 // get install directory
1182 if (SetupFindFirstLine(hTxtsetupSif, _T("SetupData"), _T("DefaultPath"), &InfContext))
1183 {
1184 SetupGetStringField(&InfContext,
1185 1,
1186 SetupData.InstallDir,
1187 sizeof(SetupData.InstallDir) / sizeof(TCHAR),
1188 &LineLength);
1189 }
1190 SetupCloseInfFile(hTxtsetupSif);
1191 }
1192 }
1193
1194 BOOL isUnattendSetup()
1195 {
1196 WCHAR szPath[MAX_PATH];
1197 WCHAR *ch;
1198 HINF hUnattendedInf;
1199 INFCONTEXT InfContext;
1200 TCHAR szValue[MAX_PATH];
1201 DWORD LineLength;
1202 //HKEY hKey;
1203 BOOL result = 0;
1204
1205 GetModuleFileNameW(NULL, szPath, MAX_PATH);
1206 ch = strrchrW(szPath, L'\\');
1207 if (ch != NULL)
1208 *ch = L'\0';
1209
1210 wcscat(szPath, L"\\unattend.inf");
1211 hUnattendedInf = SetupOpenInfFileW(szPath, NULL, INF_STYLE_OLDNT, NULL);
1212
1213 if (hUnattendedInf != INVALID_HANDLE_VALUE)
1214 {
1215 if (SetupFindFirstLine(hUnattendedInf, _T("Unattend"), _T("UnattendSetupEnabled"),&InfContext))
1216 {
1217 if (SetupGetStringField(&InfContext,
1218 1,
1219 szValue,
1220 sizeof(szValue) / sizeof(TCHAR),
1221 &LineLength) && (_tcsicmp(szValue, _T("yes")) == 0))
1222 {
1223 result = 1; // unattendSetup enabled
1224 // read values and store in SetupData
1225 }
1226 }
1227 SetupCloseInfFile(hUnattendedInf);
1228 }
1229
1230 return result;
1231 }
1232
1233 int WINAPI
1234 _tWinMain(HINSTANCE hInst,
1235 HINSTANCE hPrevInstance,
1236 LPTSTR lpszCmdLine,
1237 int nCmdShow)
1238 {
1239 PROPSHEETHEADER psh;
1240 HPROPSHEETPAGE ahpsp[8];
1241 PROPSHEETPAGE psp = {0};
1242 UINT nPages = 0;
1243 hInstance = hInst;
1244 isUnattend = isUnattendSetup();
1245
1246 if (!isUnattend)
1247 {
1248 LoadString(hInst,IDS_ABORTSETUP, abort_msg, sizeof(abort_msg)/sizeof(TCHAR));
1249 LoadString(hInst,IDS_ABORTSETUP2, abort_title,sizeof(abort_title)/sizeof(TCHAR));
1250
1251 LoadSetupData();
1252
1253 /* Create the Start page, until setup is working */
1254 psp.dwSize = sizeof(PROPSHEETPAGE);
1255 psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER;
1256 psp.hInstance = hInst;
1257 psp.lParam = 0;
1258 psp.pfnDlgProc = StartDlgProc;
1259 psp.pszTemplate = MAKEINTRESOURCE(IDD_STARTPAGE);
1260 ahpsp[nPages++] = CreatePropertySheetPage(&psp);
1261
1262 /* Create language selection page */
1263 psp.dwSize = sizeof(PROPSHEETPAGE);
1264 psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
1265 psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_LANGTITLE);
1266 psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_LANGSUBTITLE);
1267 psp.hInstance = hInst;
1268 psp.lParam = 0;
1269 psp.pfnDlgProc = LangSelDlgProc;
1270 psp.pszTemplate = MAKEINTRESOURCE(IDD_LANGSELPAGE);
1271 ahpsp[nPages++] = CreatePropertySheetPage(&psp);
1272
1273 /* Create install type selection page */
1274 psp.dwSize = sizeof(PROPSHEETPAGE);
1275 psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
1276 psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_TYPETITLE);
1277 psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_TYPESUBTITLE);
1278 psp.hInstance = hInst;
1279 psp.lParam = 0;
1280 psp.pfnDlgProc = TypeDlgProc;
1281 psp.pszTemplate = MAKEINTRESOURCE(IDD_TYPEPAGE);
1282 ahpsp[nPages++] = CreatePropertySheetPage(&psp);
1283
1284 /* Create device settings page */
1285 psp.dwSize = sizeof(PROPSHEETPAGE);
1286 psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
1287 psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_DEVICETITLE);
1288 psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_DEVICESUBTITLE);
1289 psp.hInstance = hInst;
1290 psp.lParam = 0;
1291 psp.pfnDlgProc = DeviceDlgProc;
1292 psp.pszTemplate = MAKEINTRESOURCE(IDD_DEVICEPAGE);
1293 ahpsp[nPages++] = CreatePropertySheetPage(&psp);
1294
1295 /* Create install device settings page / boot method / install directory */
1296 psp.dwSize = sizeof(PROPSHEETPAGE);
1297 psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
1298 psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_DRIVETITLE);
1299 psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_DRIVESUBTITLE);
1300 psp.hInstance = hInst;
1301 psp.lParam = 0;
1302 psp.pfnDlgProc = DriveDlgProc;
1303 psp.pszTemplate = MAKEINTRESOURCE(IDD_DRIVEPAGE);
1304 ahpsp[nPages++] = CreatePropertySheetPage(&psp);
1305
1306 /* Create summary page */
1307 psp.dwSize = sizeof(PROPSHEETPAGE);
1308 psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
1309 psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_SUMMARYTITLE);
1310 psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_SUMMARYSUBTITLE);
1311 psp.hInstance = hInst;
1312 psp.lParam = 0;
1313 psp.pfnDlgProc = SummaryDlgProc;
1314 psp.pszTemplate = MAKEINTRESOURCE(IDD_SUMMARYPAGE);
1315 ahpsp[nPages++] = CreatePropertySheetPage(&psp);
1316 }
1317
1318 /* Create installation progress page */
1319 psp.dwSize = sizeof(PROPSHEETPAGE);
1320 psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
1321 psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_PROCESSTITLE);
1322 psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_PROCESSSUBTITLE);
1323 psp.hInstance = hInst;
1324 psp.lParam = 0;
1325 psp.pfnDlgProc = ProcessDlgProc;
1326 psp.pszTemplate = MAKEINTRESOURCE(IDD_PROCESSPAGE);
1327 ahpsp[nPages++] = CreatePropertySheetPage(&psp);
1328
1329 if (!isUnattend)
1330 {
1331 /* Create finish to reboot page */
1332 psp.dwSize = sizeof(PROPSHEETPAGE);
1333 psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE;
1334 psp.pszHeaderTitle = MAKEINTRESOURCE(IDS_RESTARTTITLE);
1335 psp.pszHeaderSubTitle = MAKEINTRESOURCE(IDS_RESTARTSUBTITLE);
1336 psp.hInstance = hInst;
1337 psp.lParam = 0;
1338 psp.pfnDlgProc = RestartDlgProc;
1339 psp.pszTemplate = MAKEINTRESOURCE(IDD_RESTARTPAGE);
1340 ahpsp[nPages++] = CreatePropertySheetPage(&psp);
1341 }
1342
1343 /* Create the property sheet */
1344 psh.dwSize = sizeof(PROPSHEETHEADER);
1345 psh.dwFlags = PSH_WIZARD97 | PSH_WATERMARK | PSH_HEADER;
1346 psh.hInstance = hInst;
1347 psh.hwndParent = NULL;
1348 psh.nPages = nPages;
1349 psh.nStartPage = 0;
1350 psh.phpage = ahpsp;
1351 psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK);
1352 psh.pszbmHeader = MAKEINTRESOURCE(IDB_HEADER);
1353
1354 /* Create title font */
1355 hTitleFont = CreateTitleFont();
1356
1357 /* Display the wizard */
1358 PropertySheet(&psh);
1359
1360 DeleteObject(hTitleFont);
1361
1362 return 0;
1363 }
1364
1365 /* EOF */