xref: /reactos/base/setup/usetup/usetup.c (revision 8da0f868)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2002, 2003, 2004 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 along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /*
20  * COPYRIGHT:       See COPYING in the top level directory
21  * PROJECT:         ReactOS text-mode setup
22  * FILE:            base/setup/usetup/usetup.c
23  * PURPOSE:         Text-mode setup
24  * PROGRAMMERS:     Casper S. Hornstrup (chorns@users.sourceforge.net)
25  *                  Hervé Poussineau (hpoussin@reactos.org)
26  */
27 
28 #include <usetup.h>
29 #include <math.h>
30 #include <ntstrsafe.h>
31 
32 #include "bootsup.h"
33 #include "chkdsk.h"
34 #include "cmdcons.h"
35 #include "devinst.h"
36 #include "format.h"
37 
38 #define NDEBUG
39 #include <debug.h>
40 
41 
42 /* GLOBALS & LOCALS *********************************************************/
43 
44 HANDLE ProcessHeap;
45 BOOLEAN IsUnattendedSetup = FALSE;
46 
47 static USETUP_DATA USetupData;
48 
49 /* The partition where to perform the installation */
50 static PPARTENTRY InstallPartition = NULL;
51 /*
52  * The system partition we will actually use. It can be different from
53  * PartitionList->SystemPartition in case we don't support it, or we install
54  * on a removable disk.
55  * We may indeed not support the original system partition in case we do not
56  * have write support on it. Please note that this situation is partly a HACK
57  * and MUST NEVER happen on architectures where real system partitions are
58  * mandatory (because then they are formatted in FAT FS and we support write
59  * operation on them).
60  */
61 static PPARTENTRY SystemPartition = NULL;
62 
63 
64 /* OTHER Stuff *****/
65 
66 PCWSTR SelectedLanguageId;
67 static WCHAR DefaultLanguage[20];   // Copy of string inside LanguageList
68 static WCHAR DefaultKBLayout[20];   // Copy of string inside KeyboardList
69 
70 static BOOLEAN RepairUpdateFlag = FALSE;
71 
72 /* Global partition list on the system */
73 static PPARTLIST PartitionList = NULL;
74 
75 /* Currently selected partition entry in the list */
76 static PPARTENTRY CurrentPartition = NULL;
77 
78 /* List of supported file systems for the partition to be formatted */
79 static PFILE_SYSTEM_LIST FileSystemList = NULL;
80 
81 /* Machine state for the formatter */
82 static PPARTENTRY TempPartition = NULL;
83 static FORMATMACHINESTATE FormatState = Start;
84 
85 /*****************************************************/
86 
87 static PNTOS_INSTALLATION CurrentInstallation = NULL;
88 static PGENERIC_LIST NtOsInstallsList = NULL;
89 
90 #ifdef __REACTOS__ /* HACK */
91 
92 /* FONT SUBSTITUTION WORKAROUND *************************************************/
93 
94 /* For font file check */
95 FONTSUBSTSETTINGS s_SubstSettings = { FALSE };
96 
97 static void
98 DoWatchDestFileName(LPCWSTR FileName)
99 {
100     if (FileName[0] == 'm' || FileName[0] == 'M')
101     {
102         if (wcsicmp(FileName, L"mingliu.ttc") == 0)
103         {
104             DPRINT("mingliu.ttc found\n");
105             s_SubstSettings.bFoundFontMINGLIU = TRUE;
106         }
107         else if (wcsicmp(FileName, L"msgothic.ttc") == 0)
108         {
109             DPRINT("msgothic.ttc found\n");
110             s_SubstSettings.bFoundFontMSGOTHIC = TRUE;
111         }
112         else if (wcsicmp(FileName, L"msmincho.ttc") == 0)
113         {
114             DPRINT("msmincho.ttc found\n");
115             s_SubstSettings.bFoundFontMSMINCHO = TRUE;
116         }
117         else if (wcsicmp(FileName, L"mssong.ttf") == 0)
118         {
119             DPRINT("mssong.ttf found\n");
120             s_SubstSettings.bFoundFontMSSONG = TRUE;
121         }
122     }
123     else
124     {
125         if (wcsicmp(FileName, L"simsun.ttc") == 0)
126         {
127             DPRINT("simsun.ttc found\n");
128             s_SubstSettings.bFoundFontSIMSUN = TRUE;
129         }
130         else if (wcsicmp(FileName, L"gulim.ttc") == 0)
131         {
132             DPRINT("gulim.ttc found\n");
133             s_SubstSettings.bFoundFontGULIM = TRUE;
134         }
135         else if (wcsicmp(FileName, L"batang.ttc") == 0)
136         {
137             DPRINT("batang.ttc found\n");
138             s_SubstSettings.bFoundFontBATANG = TRUE;
139         }
140     }
141 }
142 #endif  /* HACK */
143 
144 /* FUNCTIONS ****************************************************************/
145 
146 static VOID
147 PrintString(IN PCSTR fmt,...)
148 {
149     CHAR buffer[512];
150     va_list ap;
151     UNICODE_STRING UnicodeString;
152     ANSI_STRING AnsiString;
153 
154     va_start(ap, fmt);
155     vsprintf(buffer, fmt, ap);
156     va_end(ap);
157 
158     RtlInitAnsiString(&AnsiString, buffer);
159     RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, TRUE);
160     NtDisplayString(&UnicodeString);
161     RtlFreeUnicodeString(&UnicodeString);
162 }
163 
164 
165 static VOID
166 DrawBox(IN SHORT xLeft,
167         IN SHORT yTop,
168         IN SHORT Width,
169         IN SHORT Height)
170 {
171     COORD coPos;
172     DWORD Written;
173 
174     /* Draw upper left corner */
175     coPos.X = xLeft;
176     coPos.Y = yTop;
177     FillConsoleOutputCharacterA(StdOutput,
178                                 0xDA, // '+',
179                                 1,
180                                 coPos,
181                                 &Written);
182 
183     /* Draw upper edge */
184     coPos.X = xLeft + 1;
185     coPos.Y = yTop;
186     FillConsoleOutputCharacterA(StdOutput,
187                                 0xC4, // '-',
188                                 Width - 2,
189                                 coPos,
190                                 &Written);
191 
192     /* Draw upper right corner */
193     coPos.X = xLeft + Width - 1;
194     coPos.Y = yTop;
195     FillConsoleOutputCharacterA(StdOutput,
196                                 0xBF, // '+',
197                                 1,
198                                 coPos,
199                                 &Written);
200 
201     /* Draw right edge, inner space and left edge */
202     for (coPos.Y = yTop + 1; coPos.Y < yTop + Height - 1; coPos.Y++)
203     {
204         coPos.X = xLeft;
205         FillConsoleOutputCharacterA(StdOutput,
206                                     0xB3, // '|',
207                                     1,
208                                     coPos,
209                                     &Written);
210 
211         coPos.X = xLeft + 1;
212         FillConsoleOutputCharacterA(StdOutput,
213                                     ' ',
214                                     Width - 2,
215                                     coPos,
216                                     &Written);
217 
218         coPos.X = xLeft + Width - 1;
219         FillConsoleOutputCharacterA(StdOutput,
220                                     0xB3, // '|',
221                                     1,
222                                     coPos,
223                                     &Written);
224     }
225 
226     /* Draw lower left corner */
227     coPos.X = xLeft;
228     coPos.Y = yTop + Height - 1;
229     FillConsoleOutputCharacterA(StdOutput,
230                                 0xC0, // '+',
231                                 1,
232                                 coPos,
233                                 &Written);
234 
235     /* Draw lower edge */
236     coPos.X = xLeft + 1;
237     coPos.Y = yTop + Height - 1;
238     FillConsoleOutputCharacterA(StdOutput,
239                                 0xC4, // '-',
240                                 Width - 2,
241                                 coPos,
242                                 &Written);
243 
244     /* Draw lower right corner */
245     coPos.X = xLeft + Width - 1;
246     coPos.Y = yTop + Height - 1;
247     FillConsoleOutputCharacterA(StdOutput,
248                                 0xD9, // '+',
249                                 1,
250                                 coPos,
251                                 &Written);
252 }
253 
254 
255 VOID
256 PopupError(PCCH Text,
257            PCCH Status,
258            PINPUT_RECORD Ir,
259            ULONG WaitEvent)
260 {
261     SHORT yTop;
262     SHORT xLeft;
263     COORD coPos;
264     DWORD Written;
265     ULONG Length;
266     ULONG MaxLength;
267     ULONG Lines;
268     PCHAR p;
269     PCCH pnext;
270     BOOLEAN LastLine;
271     SHORT Width;
272     SHORT Height;
273 
274     /* Count text lines and longest line */
275     MaxLength = 0;
276     Lines = 0;
277     pnext = Text;
278 
279     while (TRUE)
280     {
281         p = strchr(pnext, '\n');
282 
283         if (p == NULL)
284         {
285             Length = strlen(pnext);
286             LastLine = TRUE;
287         }
288         else
289         {
290             Length = (ULONG)(p - pnext);
291             LastLine = FALSE;
292         }
293 
294         Lines++;
295 
296         if (Length > MaxLength)
297             MaxLength = Length;
298 
299         if (LastLine)
300             break;
301 
302         pnext = p + 1;
303     }
304 
305     /* Check length of status line */
306     if (Status != NULL)
307     {
308         Length = strlen(Status);
309 
310         if (Length > MaxLength)
311             MaxLength = Length;
312     }
313 
314     Width = MaxLength + 4;
315     Height = Lines + 2;
316 
317     if (Status != NULL)
318         Height += 2;
319 
320     yTop = (yScreen - Height) / 2;
321     xLeft = (xScreen - Width) / 2;
322 
323 
324     /* Set screen attributes */
325     coPos.X = xLeft;
326     for (coPos.Y = yTop; coPos.Y < yTop + Height; coPos.Y++)
327     {
328         FillConsoleOutputAttribute(StdOutput,
329                                    FOREGROUND_RED | BACKGROUND_WHITE,
330                                    Width,
331                                    coPos,
332                                    &Written);
333     }
334 
335     DrawBox(xLeft, yTop, Width, Height);
336 
337     /* Print message text */
338     coPos.Y = yTop + 1;
339     pnext = Text;
340     while (TRUE)
341     {
342         p = strchr(pnext, '\n');
343 
344         if (p == NULL)
345         {
346             Length = strlen(pnext);
347             LastLine = TRUE;
348         }
349         else
350         {
351             Length = (ULONG)(p - pnext);
352             LastLine = FALSE;
353         }
354 
355         if (Length != 0)
356         {
357             coPos.X = xLeft + 2;
358             WriteConsoleOutputCharacterA(StdOutput,
359                                          pnext,
360                                          Length,
361                                          coPos,
362                                          &Written);
363         }
364 
365         if (LastLine)
366             break;
367 
368         coPos.Y++;
369         pnext = p + 1;
370     }
371 
372     /* Print separator line and status text */
373     if (Status != NULL)
374     {
375         coPos.Y = yTop + Height - 3;
376         coPos.X = xLeft;
377         FillConsoleOutputCharacterA(StdOutput,
378                                     0xC3, // '+',
379                                     1,
380                                     coPos,
381                                     &Written);
382 
383         coPos.X = xLeft + 1;
384         FillConsoleOutputCharacterA(StdOutput,
385                                     0xC4, // '-',
386                                     Width - 2,
387                                     coPos,
388                                     &Written);
389 
390         coPos.X = xLeft + Width - 1;
391         FillConsoleOutputCharacterA(StdOutput,
392                                     0xB4, // '+',
393                                     1,
394                                     coPos,
395                                     &Written);
396 
397         coPos.Y++;
398         coPos.X = xLeft + 2;
399         WriteConsoleOutputCharacterA(StdOutput,
400                                      Status,
401                                      min(strlen(Status), (SIZE_T)Width - 4),
402                                      coPos,
403                                      &Written);
404     }
405 
406     if (WaitEvent == POPUP_WAIT_NONE)
407         return;
408 
409     while (TRUE)
410     {
411         CONSOLE_ConInKey(Ir);
412 
413         if (WaitEvent == POPUP_WAIT_ANY_KEY ||
414             Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)
415         {
416             return;
417         }
418     }
419 }
420 
421 
422 /** See also usetup:partlist.c!PrintDiskData() **/
423 VOID
424 PrettifySize1(
425     IN OUT PULONGLONG Size,
426     OUT PCSTR* Unit)
427 {
428     ULONGLONG DiskSize = *Size;
429 
430 #if 0
431     if (DiskSize >= 10 * GB) /* 10 GB */
432     {
433         DiskSize = DiskSize / GB;
434         *Unit = MUIGetString(STRING_GB);
435     }
436     else
437 #endif
438     {
439         DiskSize = DiskSize / MB;
440         if (DiskSize == 0)
441             DiskSize = 1;
442 
443         *Unit = MUIGetString(STRING_MB);
444     }
445 
446     *Size = DiskSize;
447 }
448 
449 /** See also usetup:partlist.c!PrintPartitionData() **/
450 VOID
451 PrettifySize2(
452     IN OUT PULONGLONG Size,
453     OUT PCSTR* Unit)
454 {
455     ULONGLONG PartSize = *Size;
456 
457 #if 0
458     if (PartSize >= 10 * GB) /* 10 GB */
459     {
460         PartSize = PartSize / GB;
461         *Unit = MUIGetString(STRING_GB);
462     }
463     else
464 #endif
465     if (PartSize >= 10 * MB) /* 10 MB */
466     {
467         PartSize = PartSize / MB;
468         *Unit = MUIGetString(STRING_MB);
469     }
470     else
471     {
472         PartSize = PartSize / KB;
473         *Unit = MUIGetString(STRING_KB);
474     }
475 
476     // if (PartSize == 0)
477         // PartSize = 1;
478     *Size = PartSize;
479 }
480 
481 
482 /*
483  * Confirm quit setup
484  * RETURNS
485  *   TRUE: Quit setup.
486  *   FALSE: Don't quit setup.
487  */
488 static BOOL
489 ConfirmQuit(PINPUT_RECORD Ir)
490 {
491     BOOL Result = FALSE;
492     MUIDisplayError(ERROR_NOT_INSTALLED, NULL, POPUP_WAIT_NONE);
493 
494     while (TRUE)
495     {
496         CONSOLE_ConInKey(Ir);
497 
498         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
499             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
500         {
501             Result = TRUE;
502             break;
503         }
504         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
505         {
506             Result = FALSE;
507             break;
508         }
509     }
510 
511     return Result;
512 }
513 
514 
515 static VOID
516 UpdateKBLayout(VOID)
517 {
518     PGENERIC_LIST_ENTRY ListEntry;
519     PCWSTR pszNewLayout;
520 
521     pszNewLayout = MUIDefaultKeyboardLayout(SelectedLanguageId);
522 
523     if (USetupData.LayoutList == NULL)
524     {
525         USetupData.LayoutList = CreateKeyboardLayoutList(USetupData.SetupInf, SelectedLanguageId, DefaultKBLayout);
526         if (USetupData.LayoutList == NULL)
527         {
528             /* FIXME: Handle error! */
529             return;
530         }
531     }
532 
533     /* Search for default layout (if provided) */
534     if (pszNewLayout != NULL)
535     {
536         for (ListEntry = GetFirstListEntry(USetupData.LayoutList); ListEntry;
537              ListEntry = GetNextListEntry(ListEntry))
538         {
539             if (!wcscmp(pszNewLayout, ((PGENENTRY)GetListEntryData(ListEntry))->Id))
540             {
541                 SetCurrentListEntry(USetupData.LayoutList, ListEntry);
542                 break;
543             }
544         }
545     }
546 }
547 
548 
549 static NTSTATUS
550 NTAPI
551 GetSettingDescription(
552     IN PGENERIC_LIST_ENTRY Entry,
553     OUT PSTR Buffer,
554     IN SIZE_T cchBufferSize)
555 {
556     return RtlStringCchPrintfA(Buffer, cchBufferSize, "%S",
557                                ((PGENENTRY)GetListEntryData(Entry))->Value);
558 }
559 
560 static NTSTATUS
561 NTAPI
562 GetNTOSInstallationName(
563     IN PGENERIC_LIST_ENTRY Entry,
564     OUT PSTR Buffer,
565     IN SIZE_T cchBufferSize)
566 {
567     PNTOS_INSTALLATION NtOsInstall = (PNTOS_INSTALLATION)GetListEntryData(Entry);
568     PPARTENTRY PartEntry = NtOsInstall->PartEntry;
569 
570     if (PartEntry && PartEntry->DriveLetter)
571     {
572         /* We have retrieved a partition that is mounted */
573         return RtlStringCchPrintfA(Buffer, cchBufferSize,
574                                    "%C:%S  \"%S\"",
575                                    PartEntry->DriveLetter,
576                                    NtOsInstall->PathComponent,
577                                    NtOsInstall->InstallationName);
578     }
579     else
580     {
581         /* We failed somewhere, just show the NT path */
582         return RtlStringCchPrintfA(Buffer, cchBufferSize,
583                                    "%wZ  \"%S\"",
584                                    &NtOsInstall->SystemNtPath,
585                                    NtOsInstall->InstallationName);
586     }
587 }
588 
589 
590 /*
591  * Displays the LanguagePage.
592  *
593  * Next pages: WelcomePage, QuitPage
594  *
595  * SIDEEFFECTS
596  *  Init SelectedLanguageId
597  *  Init USetupData.LanguageId
598  *
599  * RETURNS
600  *   Number of the next page.
601  */
602 static PAGE_NUMBER
603 LanguagePage(PINPUT_RECORD Ir)
604 {
605     GENERIC_LIST_UI ListUi;
606     PCWSTR NewLanguageId;
607     BOOL RefreshPage = FALSE;
608 
609     /* Initialize the computer settings list */
610     if (USetupData.LanguageList == NULL)
611     {
612         USetupData.LanguageList = CreateLanguageList(USetupData.SetupInf, DefaultLanguage);
613         if (USetupData.LanguageList == NULL)
614         {
615            PopupError("Setup failed to initialize available translations", NULL, NULL, POPUP_WAIT_NONE);
616            return WELCOME_PAGE;
617         }
618     }
619 
620     SelectedLanguageId = DefaultLanguage;
621     USetupData.LanguageId = 0;
622 
623     /* Load the font */
624     SetConsoleCodePage();
625     UpdateKBLayout();
626 
627     /*
628      * If there is no language or just a single one in the list,
629      * skip the language selection process altogether.
630      */
631     if (GetNumberOfListEntries(USetupData.LanguageList) <= 1)
632     {
633         USetupData.LanguageId = (LANGID)(wcstol(SelectedLanguageId, NULL, 16) & 0xFFFF);
634         return WELCOME_PAGE;
635     }
636 
637     InitGenericListUi(&ListUi, USetupData.LanguageList, GetSettingDescription);
638     DrawGenericList(&ListUi,
639                     2, 18,
640                     xScreen - 3,
641                     yScreen - 3);
642 
643     ScrollToPositionGenericList(&ListUi, GetDefaultLanguageIndex());
644 
645     MUIDisplayPage(LANGUAGE_PAGE);
646 
647     while (TRUE)
648     {
649         CONSOLE_ConInKey(Ir);
650 
651         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
652             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DOWN))  /* DOWN */
653         {
654             ScrollDownGenericList(&ListUi);
655             RefreshPage = TRUE;
656         }
657         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
658                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_UP))  /* UP */
659         {
660             ScrollUpGenericList(&ListUi);
661             RefreshPage = TRUE;
662         }
663         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
664             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_NEXT))  /* PAGE DOWN */
665         {
666             ScrollPageDownGenericList(&ListUi);
667             RefreshPage = TRUE;
668         }
669         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
670                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_PRIOR))  /* PAGE UP */
671         {
672             ScrollPageUpGenericList(&ListUi);
673             RefreshPage = TRUE;
674         }
675         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
676                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
677         {
678             if (ConfirmQuit(Ir))
679                 return QUIT_PAGE;
680             else
681                 RedrawGenericList(&ListUi);
682         }
683         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
684         {
685             ASSERT(GetNumberOfListEntries(USetupData.LanguageList) >= 1);
686 
687             SelectedLanguageId =
688                 ((PGENENTRY)GetListEntryData(GetCurrentListEntry(USetupData.LanguageList)))->Id;
689 
690             USetupData.LanguageId = (LANGID)(wcstol(SelectedLanguageId, NULL, 16) & 0xFFFF);
691 
692             if (wcscmp(SelectedLanguageId, DefaultLanguage))
693             {
694                 UpdateKBLayout();
695             }
696 
697             /* Load the font */
698             SetConsoleCodePage();
699 
700             return WELCOME_PAGE;
701         }
702         else if ((Ir->Event.KeyEvent.uChar.AsciiChar > 0x60) && (Ir->Event.KeyEvent.uChar.AsciiChar < 0x7b))
703         {
704             /* a-z */
705             GenericListKeyPress(&ListUi, Ir->Event.KeyEvent.uChar.AsciiChar);
706             RefreshPage = TRUE;
707         }
708 
709         if (RefreshPage)
710         {
711             ASSERT(GetNumberOfListEntries(USetupData.LanguageList) >= 1);
712 
713             NewLanguageId =
714                 ((PGENENTRY)GetListEntryData(GetCurrentListEntry(USetupData.LanguageList)))->Id;
715 
716             if (wcscmp(SelectedLanguageId, NewLanguageId))
717             {
718                 /* Clear the language page */
719                 MUIClearPage(LANGUAGE_PAGE);
720 
721                 SelectedLanguageId = NewLanguageId;
722 
723                 /* Load the font */
724                 SetConsoleCodePage();
725 
726                 /* Redraw language selection page in native language */
727                 MUIDisplayPage(LANGUAGE_PAGE);
728             }
729 
730             RefreshPage = FALSE;
731         }
732     }
733 
734     return WELCOME_PAGE;
735 }
736 
737 
738 /*
739  * Start page
740  *
741  * Next pages:
742  *  LanguagePage (at once, default)
743  *  InstallIntroPage (at once, if unattended)
744  *  QuitPage
745  *
746  * SIDEEFFECTS
747  *  Init Sdi
748  *  Init USetupData.SourcePath
749  *  Init USetupData.SourceRootPath
750  *  Init USetupData.SourceRootDir
751  *  Init USetupData.SetupInf
752  *  Init USetupData.RequiredPartitionDiskSpace
753  *  Init IsUnattendedSetup
754  *  If unattended, init *List and sets the Codepage
755  *  If unattended, init SelectedLanguageId
756  *  If unattended, init USetupData.LanguageId
757  *
758  * RETURNS
759  *   Number of the next page.
760  */
761 static PAGE_NUMBER
762 SetupStartPage(PINPUT_RECORD Ir)
763 {
764     ULONG Error;
765     PGENERIC_LIST_ENTRY ListEntry;
766     PCWSTR LocaleId;
767 
768     MUIDisplayPage(SETUP_INIT_PAGE);
769 
770     /* Initialize Setup, phase 1 */
771     Error = InitializeSetup(&USetupData, 1);
772     if (Error != ERROR_SUCCESS)
773     {
774         MUIDisplayError(Error, Ir, POPUP_WAIT_ENTER);
775         return QUIT_PAGE;
776     }
777 
778     /* Initialize the user-mode PnP manager */
779     if (!EnableUserModePnpManager())
780         DPRINT1("The user-mode PnP manager could not initialize, expect unavailable devices!\n");
781 
782     /* Wait for any immediate pending installations to finish */
783     if (WaitNoPendingInstallEvents(NULL) != STATUS_WAIT_0)
784         DPRINT1("WaitNoPendingInstallEvents() failed to wait!\n");
785 
786     CheckUnattendedSetup(&USetupData);
787 
788     if (IsUnattendedSetup)
789     {
790         // TODO: Read options from inf
791         /* Load the hardware, language and keyboard layout lists */
792 
793         USetupData.ComputerList = CreateComputerTypeList(USetupData.SetupInf);
794         USetupData.DisplayList = CreateDisplayDriverList(USetupData.SetupInf);
795         USetupData.KeyboardList = CreateKeyboardDriverList(USetupData.SetupInf);
796 
797         USetupData.LanguageList = CreateLanguageList(USetupData.SetupInf, DefaultLanguage);
798 
799         /* new part */
800         SelectedLanguageId = DefaultLanguage;
801         wcscpy(DefaultLanguage, USetupData.LocaleID);
802         USetupData.LanguageId = (LANGID)(wcstol(SelectedLanguageId, NULL, 16) & 0xFFFF);
803 
804         USetupData.LayoutList = CreateKeyboardLayoutList(USetupData.SetupInf, SelectedLanguageId, DefaultKBLayout);
805 
806         /* first we hack LanguageList */
807         for (ListEntry = GetFirstListEntry(USetupData.LanguageList); ListEntry;
808              ListEntry = GetNextListEntry(ListEntry))
809         {
810             LocaleId = ((PGENENTRY)GetListEntryData(ListEntry))->Id;
811             if (!wcsicmp(USetupData.LocaleID, LocaleId))
812             {
813                 DPRINT("found %S in LanguageList\n", LocaleId);
814                 SetCurrentListEntry(USetupData.LanguageList, ListEntry);
815                 break;
816             }
817         }
818 
819         /* now LayoutList */
820         for (ListEntry = GetFirstListEntry(USetupData.LayoutList); ListEntry;
821              ListEntry = GetNextListEntry(ListEntry))
822         {
823             LocaleId = ((PGENENTRY)GetListEntryData(ListEntry))->Id;
824             if (!wcsicmp(USetupData.LocaleID, LocaleId))
825             {
826                 DPRINT("found %S in LayoutList\n", LocaleId);
827                 SetCurrentListEntry(USetupData.LayoutList, ListEntry);
828                 break;
829             }
830         }
831 
832         SetConsoleCodePage();
833 
834         return INSTALL_INTRO_PAGE;
835     }
836 
837     return LANGUAGE_PAGE;
838 }
839 
840 
841 /*
842  * Displays the WelcomePage.
843  *
844  * Next pages:
845  *  InstallIntroPage (default)
846  *  RepairIntroPage
847  *  RecoveryPage
848  *  LicensePage
849  *  QuitPage
850  *
851  * RETURNS
852  *   Number of the next page.
853  */
854 static PAGE_NUMBER
855 WelcomePage(PINPUT_RECORD Ir)
856 {
857     MUIDisplayPage(WELCOME_PAGE);
858 
859     while (TRUE)
860     {
861         CONSOLE_ConInKey(Ir);
862 
863         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
864             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
865         {
866             if (ConfirmQuit(Ir))
867                 return QUIT_PAGE;
868 
869             break;
870         }
871         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
872         {
873             return INSTALL_INTRO_PAGE;
874         }
875         else if (toupper(Ir->Event.KeyEvent.uChar.AsciiChar) == 'R') /* R */
876         {
877             return RECOVERY_PAGE; // REPAIR_INTRO_PAGE;
878         }
879         else if (toupper(Ir->Event.KeyEvent.uChar.AsciiChar) == 'L') /* L */
880         {
881             return LICENSE_PAGE;
882         }
883     }
884 
885     return WELCOME_PAGE;
886 }
887 
888 
889 /*
890  * Displays the License page.
891  *
892  * Next page:
893  *  WelcomePage (default)
894  *
895  * RETURNS
896  *   Number of the next page.
897  */
898 static PAGE_NUMBER
899 LicensePage(PINPUT_RECORD Ir)
900 {
901     MUIDisplayPage(LICENSE_PAGE);
902 
903     while (TRUE)
904     {
905         CONSOLE_ConInKey(Ir);
906 
907         if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
908         {
909             return WELCOME_PAGE;
910         }
911     }
912 
913     return LICENSE_PAGE;
914 }
915 
916 
917 /*
918  * Displays the RepairIntroPage.
919  *
920  * Next pages:
921  *  RebootPage (default)
922  *  InstallIntroPage
923  *  RecoveryPage
924  *  IntroPage
925  *
926  * RETURNS
927  *   Number of the next page.
928  */
929 static PAGE_NUMBER
930 RepairIntroPage(PINPUT_RECORD Ir)
931 {
932     MUIDisplayPage(REPAIR_INTRO_PAGE);
933 
934     while (TRUE)
935     {
936         CONSOLE_ConInKey(Ir);
937 
938         if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)  /* ENTER */
939         {
940             return REBOOT_PAGE;
941         }
942         else if (toupper(Ir->Event.KeyEvent.uChar.AsciiChar) == 'U')  /* U */
943         {
944             RepairUpdateFlag = TRUE;
945             return INSTALL_INTRO_PAGE;
946         }
947         else if (toupper(Ir->Event.KeyEvent.uChar.AsciiChar) == 'R')  /* R */
948         {
949             return RECOVERY_PAGE;
950         }
951         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
952                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE))  /* ESC */
953         {
954             return WELCOME_PAGE;
955         }
956     }
957 
958     return REPAIR_INTRO_PAGE;
959 }
960 
961 /*
962  * Displays the UpgradeRepairPage.
963  *
964  * Next pages:
965  *  RebootPage (default)
966  *  InstallIntroPage
967  *  RecoveryPage
968  *  WelcomePage
969  *
970  * RETURNS
971  *   Number of the next page.
972  */
973 static PAGE_NUMBER
974 UpgradeRepairPage(PINPUT_RECORD Ir)
975 {
976     GENERIC_LIST_UI ListUi;
977 
978 /*** HACK!! ***/
979     if (PartitionList == NULL)
980     {
981         PartitionList = CreatePartitionList();
982         if (PartitionList == NULL)
983         {
984             /* FIXME: show an error dialog */
985             MUIDisplayError(ERROR_DRIVE_INFORMATION, Ir, POPUP_WAIT_ENTER);
986             return QUIT_PAGE;
987         }
988         else if (IsListEmpty(&PartitionList->DiskListHead))
989         {
990             MUIDisplayError(ERROR_NO_HDD, Ir, POPUP_WAIT_ENTER);
991             return QUIT_PAGE;
992         }
993 
994         /* Reset the formatter machine state */
995         TempPartition = NULL;
996         FormatState = Start;
997     }
998 /**************/
999 
1000     NtOsInstallsList = CreateNTOSInstallationsList(PartitionList);
1001     if (!NtOsInstallsList)
1002         DPRINT1("Failed to get a list of NTOS installations; continue installation...\n");
1003 
1004     /*
1005      * If there is no available installation (or just a single one??) that can
1006      * be updated in the list, just continue with the regular installation.
1007      */
1008     if (!NtOsInstallsList || GetNumberOfListEntries(NtOsInstallsList) == 0)
1009     {
1010         RepairUpdateFlag = FALSE;
1011 
1012         // return INSTALL_INTRO_PAGE;
1013         return DEVICE_SETTINGS_PAGE;
1014         // return SCSI_CONTROLLER_PAGE;
1015     }
1016 
1017     MUIDisplayPage(UPGRADE_REPAIR_PAGE);
1018 
1019     InitGenericListUi(&ListUi, NtOsInstallsList, GetNTOSInstallationName);
1020     DrawGenericList(&ListUi,
1021                     2, 23,
1022                     xScreen - 3,
1023                     yScreen - 3);
1024 
1025     // return HandleGenericList(&ListUi, DEVICE_SETTINGS_PAGE, Ir);
1026     while (TRUE)
1027     {
1028         CONSOLE_ConInKey(Ir);
1029 
1030         if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x00)
1031         {
1032             switch (Ir->Event.KeyEvent.wVirtualKeyCode)
1033             {
1034             case VK_DOWN:   /* DOWN */
1035                 ScrollDownGenericList(&ListUi);
1036                 break;
1037             case VK_UP:     /* UP */
1038                 ScrollUpGenericList(&ListUi);
1039                 break;
1040             case VK_NEXT:   /* PAGE DOWN */
1041                 ScrollPageDownGenericList(&ListUi);
1042                 break;
1043             case VK_PRIOR:  /* PAGE UP */
1044                 ScrollPageUpGenericList(&ListUi);
1045                 break;
1046             case VK_F3:     /* F3 */
1047             {
1048                 if (ConfirmQuit(Ir))
1049                     return QUIT_PAGE;
1050                 else
1051                     RedrawGenericList(&ListUi);
1052                 break;
1053             }
1054             case VK_ESCAPE: /* ESC */
1055             {
1056                 RestoreGenericListUiState(&ListUi);
1057                 // return nextPage;    // prevPage;
1058 
1059                 // return INSTALL_INTRO_PAGE;
1060                 return DEVICE_SETTINGS_PAGE;
1061                 // return SCSI_CONTROLLER_PAGE;
1062             }
1063             }
1064         }
1065         else
1066         {
1067             // switch (toupper(Ir->Event.KeyEvent.uChar.AsciiChar))
1068             // if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
1069             if (toupper(Ir->Event.KeyEvent.uChar.AsciiChar) == 'U')  /* U */
1070             {
1071                 /* Retrieve the current installation */
1072                 ASSERT(GetNumberOfListEntries(NtOsInstallsList) >= 1);
1073 
1074                 CurrentInstallation =
1075                     (PNTOS_INSTALLATION)GetListEntryData(GetCurrentListEntry(NtOsInstallsList));
1076 
1077                 DPRINT1("Selected installation for repair: \"%S\" ; DiskNumber = %d , PartitionNumber = %d\n",
1078                         CurrentInstallation->InstallationName, CurrentInstallation->DiskNumber, CurrentInstallation->PartitionNumber);
1079 
1080                 RepairUpdateFlag = TRUE;
1081 
1082                 // return nextPage;
1083                 /***/return INSTALL_INTRO_PAGE;/***/
1084             }
1085             else if ((Ir->Event.KeyEvent.uChar.AsciiChar > 0x60) &&
1086                      (Ir->Event.KeyEvent.uChar.AsciiChar < 0x7b))   /* a-z */
1087             {
1088                 GenericListKeyPress(&ListUi, Ir->Event.KeyEvent.uChar.AsciiChar);
1089             }
1090         }
1091     }
1092 
1093     return UPGRADE_REPAIR_PAGE;
1094 }
1095 
1096 
1097 /*
1098  * Displays the InstallIntroPage.
1099  *
1100  * Next pages:
1101  *  DeviceSettingsPage  (At once if repair or update is selected)
1102  *  SelectPartitionPage (At once if unattended setup)
1103  *  DeviceSettingsPage  (default)
1104  *  QuitPage
1105  *
1106  * RETURNS
1107  *   Number of the next page.
1108  */
1109 static PAGE_NUMBER
1110 InstallIntroPage(PINPUT_RECORD Ir)
1111 {
1112     if (RepairUpdateFlag)
1113     {
1114 #if 1 /* Old code that looks good */
1115 
1116         // return SELECT_PARTITION_PAGE;
1117         return DEVICE_SETTINGS_PAGE;
1118 
1119 #else /* Possible new code? */
1120 
1121         return DEVICE_SETTINGS_PAGE;
1122         // return SCSI_CONTROLLER_PAGE;
1123 
1124 #endif
1125     }
1126 
1127     if (IsUnattendedSetup)
1128         return SELECT_PARTITION_PAGE;
1129 
1130     MUIDisplayPage(INSTALL_INTRO_PAGE);
1131 
1132     while (TRUE)
1133     {
1134         CONSOLE_ConInKey(Ir);
1135 
1136         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1137             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3)) /* F3 */
1138         {
1139             if (ConfirmQuit(Ir))
1140                 return QUIT_PAGE;
1141 
1142             break;
1143         }
1144         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
1145         {
1146             return UPGRADE_REPAIR_PAGE;
1147         }
1148     }
1149 
1150     return INSTALL_INTRO_PAGE;
1151 }
1152 
1153 
1154 #if 0
1155 static PAGE_NUMBER
1156 ScsiControllerPage(PINPUT_RECORD Ir)
1157 {
1158     // MUIDisplayPage(SCSI_CONTROLLER_PAGE);
1159 
1160     CONSOLE_SetTextXY(6, 8, "Setup detected the following mass storage devices:");
1161 
1162     /* FIXME: print loaded mass storage driver descriptions */
1163 #if 0
1164     CONSOLE_SetTextXY(8, 10, "TEST device");
1165 #endif
1166 
1167     CONSOLE_SetStatusText("   ENTER = Continue   F3 = Quit");
1168 
1169     while (TRUE)
1170     {
1171         CONSOLE_ConInKey(Ir);
1172 
1173         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1174             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3)) /* F3 */
1175         {
1176             if (ConfirmQuit(Ir))
1177                 return QUIT_PAGE;
1178 
1179             break;
1180         }
1181         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
1182         {
1183             return DEVICE_SETTINGS_PAGE;
1184         }
1185     }
1186 
1187     return SCSI_CONTROLLER_PAGE;
1188 }
1189 
1190 static PAGE_NUMBER
1191 OemDriverPage(PINPUT_RECORD Ir)
1192 {
1193     // MUIDisplayPage(OEM_DRIVER_PAGE);
1194 
1195     CONSOLE_SetTextXY(6, 8, "This is the OEM driver page!");
1196 
1197     /* FIXME: Implement!! */
1198 
1199     CONSOLE_SetStatusText("   ENTER = Continue   F3 = Quit");
1200 
1201     while (TRUE)
1202     {
1203         CONSOLE_ConInKey(Ir);
1204 
1205         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1206             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3)) /* F3 */
1207         {
1208             if (ConfirmQuit(Ir))
1209                 return QUIT_PAGE;
1210 
1211             break;
1212         }
1213         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
1214         {
1215             return DEVICE_SETTINGS_PAGE;
1216         }
1217     }
1218 
1219     return OEM_DRIVER_PAGE;
1220 }
1221 #endif
1222 
1223 
1224 /*
1225  * Displays the DeviceSettingsPage.
1226  *
1227  * Next pages:
1228  *  SelectPartitionPage (At once if repair or update is selected)
1229  *  ComputerSettingsPage
1230  *  DisplaySettingsPage
1231  *  KeyboardSettingsPage
1232  *  LayoutsettingsPage
1233  *  SelectPartitionPage
1234  *  QuitPage
1235  *
1236  * SIDEEFFECTS
1237  *  Init USetupData.ComputerList
1238  *  Init USetupData.DisplayList
1239  *  Init USetupData.KeyboardList
1240  *  Init USetupData.LayoutList
1241  *
1242  * RETURNS
1243  *   Number of the next page.
1244  */
1245 static PAGE_NUMBER
1246 DeviceSettingsPage(PINPUT_RECORD Ir)
1247 {
1248     static ULONG Line = 16;
1249 
1250     /* Initialize the computer settings list */
1251     if (USetupData.ComputerList == NULL)
1252     {
1253         USetupData.ComputerList = CreateComputerTypeList(USetupData.SetupInf);
1254         if (USetupData.ComputerList == NULL)
1255         {
1256             MUIDisplayError(ERROR_LOAD_COMPUTER, Ir, POPUP_WAIT_ENTER);
1257             return QUIT_PAGE;
1258         }
1259     }
1260 
1261     /* Initialize the display settings list */
1262     if (USetupData.DisplayList == NULL)
1263     {
1264         USetupData.DisplayList = CreateDisplayDriverList(USetupData.SetupInf);
1265         if (USetupData.DisplayList == NULL)
1266         {
1267             MUIDisplayError(ERROR_LOAD_DISPLAY, Ir, POPUP_WAIT_ENTER);
1268             return QUIT_PAGE;
1269         }
1270     }
1271 
1272     /* Initialize the keyboard settings list */
1273     if (USetupData.KeyboardList == NULL)
1274     {
1275         USetupData.KeyboardList = CreateKeyboardDriverList(USetupData.SetupInf);
1276         if (USetupData.KeyboardList == NULL)
1277         {
1278             MUIDisplayError(ERROR_LOAD_KEYBOARD, Ir, POPUP_WAIT_ENTER);
1279             return QUIT_PAGE;
1280         }
1281     }
1282 
1283     /* Initialize the keyboard layout list */
1284     if (USetupData.LayoutList == NULL)
1285     {
1286         USetupData.LayoutList = CreateKeyboardLayoutList(USetupData.SetupInf, SelectedLanguageId, DefaultKBLayout);
1287         if (USetupData.LayoutList == NULL)
1288         {
1289             /* FIXME: report error */
1290             MUIDisplayError(ERROR_LOAD_KBLAYOUT, Ir, POPUP_WAIT_ENTER);
1291             return QUIT_PAGE;
1292         }
1293     }
1294 
1295     if (RepairUpdateFlag)
1296         return SELECT_PARTITION_PAGE;
1297 
1298     // if (IsUnattendedSetup)
1299         // return SELECT_PARTITION_PAGE;
1300 
1301     MUIDisplayPage(DEVICE_SETTINGS_PAGE);
1302 
1303     DrawGenericListCurrentItem(USetupData.ComputerList, GetSettingDescription, 25, 11);
1304     DrawGenericListCurrentItem(USetupData.DisplayList , GetSettingDescription, 25, 12);
1305     DrawGenericListCurrentItem(USetupData.KeyboardList, GetSettingDescription, 25, 13);
1306     DrawGenericListCurrentItem(USetupData.LayoutList  , GetSettingDescription, 25, 14);
1307 
1308     CONSOLE_InvertTextXY(24, Line, 48, 1);
1309 
1310     while (TRUE)
1311     {
1312         CONSOLE_ConInKey(Ir);
1313 
1314         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1315             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DOWN))  /* DOWN */
1316         {
1317             CONSOLE_NormalTextXY(24, Line, 48, 1);
1318 
1319             if (Line == 14)
1320                 Line = 16;
1321             else if (Line == 16)
1322                 Line = 11;
1323             else
1324                 Line++;
1325 
1326             CONSOLE_InvertTextXY(24, Line, 48, 1);
1327         }
1328         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1329                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_UP))  /* UP */
1330         {
1331             CONSOLE_NormalTextXY(24, Line, 48, 1);
1332 
1333             if (Line == 11)
1334                 Line = 16;
1335             else if (Line == 16)
1336                 Line = 14;
1337             else
1338                 Line--;
1339 
1340             CONSOLE_InvertTextXY(24, Line, 48, 1);
1341         }
1342         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1343                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
1344         {
1345             if (ConfirmQuit(Ir))
1346                 return QUIT_PAGE;
1347 
1348             break;
1349         }
1350         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
1351         {
1352             if (Line == 11)
1353                 return COMPUTER_SETTINGS_PAGE;
1354             else if (Line == 12)
1355                 return DISPLAY_SETTINGS_PAGE;
1356             else if (Line == 13)
1357                 return KEYBOARD_SETTINGS_PAGE;
1358             else if (Line == 14)
1359                 return LAYOUT_SETTINGS_PAGE;
1360             else if (Line == 16)
1361                 return SELECT_PARTITION_PAGE;
1362         }
1363     }
1364 
1365     return DEVICE_SETTINGS_PAGE;
1366 }
1367 
1368 
1369 /*
1370  * Handles generic selection lists.
1371  *
1372  * PARAMS
1373  * GenericList: The list to handle.
1374  * nextPage: The page it needs to jump to after this page.
1375  * Ir: The PINPUT_RECORD
1376  */
1377 static PAGE_NUMBER
1378 HandleGenericList(PGENERIC_LIST_UI ListUi,
1379                   PAGE_NUMBER nextPage,
1380                   PINPUT_RECORD Ir)
1381 {
1382     while (TRUE)
1383     {
1384         CONSOLE_ConInKey(Ir);
1385 
1386         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1387             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DOWN))  /* DOWN */
1388         {
1389             ScrollDownGenericList(ListUi);
1390         }
1391         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1392                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_UP))  /* UP */
1393         {
1394             ScrollUpGenericList(ListUi);
1395         }
1396         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1397                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_NEXT))  /* PAGE DOWN */
1398         {
1399             ScrollPageDownGenericList(ListUi);
1400         }
1401         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1402                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_PRIOR))  /* PAGE UP */
1403         {
1404             ScrollPageUpGenericList(ListUi);
1405         }
1406         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1407                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
1408         {
1409             if (ConfirmQuit(Ir))
1410                 return QUIT_PAGE;
1411             else
1412                 RedrawGenericList(ListUi);
1413         }
1414         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1415                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE))  /* ESC */
1416         {
1417             RestoreGenericListUiState(ListUi);
1418             return nextPage;    // Use some "prevPage;" instead?
1419         }
1420         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
1421         {
1422             return nextPage;
1423         }
1424         else if ((Ir->Event.KeyEvent.uChar.AsciiChar > 0x60) && (Ir->Event.KeyEvent.uChar.AsciiChar < 0x7b))
1425         {
1426             /* a-z */
1427             GenericListKeyPress(ListUi, Ir->Event.KeyEvent.uChar.AsciiChar);
1428         }
1429     }
1430 }
1431 
1432 
1433 /*
1434  * Displays the ComputerSettingsPage.
1435  *
1436  * Next pages:
1437  *  DeviceSettingsPage
1438  *  QuitPage
1439  *
1440  * RETURNS
1441  *   Number of the next page.
1442  */
1443 static PAGE_NUMBER
1444 ComputerSettingsPage(PINPUT_RECORD Ir)
1445 {
1446     GENERIC_LIST_UI ListUi;
1447     MUIDisplayPage(COMPUTER_SETTINGS_PAGE);
1448 
1449     InitGenericListUi(&ListUi, USetupData.ComputerList, GetSettingDescription);
1450     DrawGenericList(&ListUi,
1451                     2, 18,
1452                     xScreen - 3,
1453                     yScreen - 3);
1454 
1455     return HandleGenericList(&ListUi, DEVICE_SETTINGS_PAGE, Ir);
1456 }
1457 
1458 
1459 /*
1460  * Displays the DisplaySettingsPage.
1461  *
1462  * Next pages:
1463  *  DeviceSettingsPage
1464  *  QuitPage
1465  *
1466  * RETURNS
1467  *   Number of the next page.
1468  */
1469 static PAGE_NUMBER
1470 DisplaySettingsPage(PINPUT_RECORD Ir)
1471 {
1472     GENERIC_LIST_UI ListUi;
1473     MUIDisplayPage(DISPLAY_SETTINGS_PAGE);
1474 
1475     InitGenericListUi(&ListUi, USetupData.DisplayList, GetSettingDescription);
1476     DrawGenericList(&ListUi,
1477                     2, 18,
1478                     xScreen - 3,
1479                     yScreen - 3);
1480 
1481     return HandleGenericList(&ListUi, DEVICE_SETTINGS_PAGE, Ir);
1482 }
1483 
1484 
1485 /*
1486  * Displays the KeyboardSettingsPage.
1487  *
1488  * Next pages:
1489  *  DeviceSettingsPage
1490  *  QuitPage
1491  *
1492  * RETURNS
1493  *   Number of the next page.
1494  */
1495 static PAGE_NUMBER
1496 KeyboardSettingsPage(PINPUT_RECORD Ir)
1497 {
1498     GENERIC_LIST_UI ListUi;
1499     MUIDisplayPage(KEYBOARD_SETTINGS_PAGE);
1500 
1501     InitGenericListUi(&ListUi, USetupData.KeyboardList, GetSettingDescription);
1502     DrawGenericList(&ListUi,
1503                     2, 18,
1504                     xScreen - 3,
1505                     yScreen - 3);
1506 
1507     return HandleGenericList(&ListUi, DEVICE_SETTINGS_PAGE, Ir);
1508 }
1509 
1510 
1511 /*
1512  * Displays the LayoutSettingsPage.
1513  *
1514  * Next pages:
1515  *  DeviceSettingsPage
1516  *  QuitPage
1517  *
1518  * RETURNS
1519  *   Number of the next page.
1520  */
1521 static PAGE_NUMBER
1522 LayoutSettingsPage(PINPUT_RECORD Ir)
1523 {
1524     GENERIC_LIST_UI ListUi;
1525     MUIDisplayPage(LAYOUT_SETTINGS_PAGE);
1526 
1527     InitGenericListUi(&ListUi, USetupData.LayoutList, GetSettingDescription);
1528     DrawGenericList(&ListUi,
1529                     2, 18,
1530                     xScreen - 3,
1531                     yScreen - 3);
1532 
1533     return HandleGenericList(&ListUi, DEVICE_SETTINGS_PAGE, Ir);
1534 }
1535 
1536 
1537 static BOOL
1538 IsDiskSizeValid(PPARTENTRY PartEntry)
1539 {
1540     ULONGLONG size;
1541 
1542     size = PartEntry->SectorCount.QuadPart * PartEntry->DiskEntry->BytesPerSector;
1543     size = (size + (512 * KB)) / MB;  /* in MBytes */
1544 
1545     if (size < USetupData.RequiredPartitionDiskSpace)
1546     {
1547         /* Partition is too small so ask for another one */
1548         DPRINT1("Partition is too small (size: %I64u MB), required disk space is %lu MB\n", size, USetupData.RequiredPartitionDiskSpace);
1549         return FALSE;
1550     }
1551     else
1552     {
1553         return TRUE;
1554     }
1555 }
1556 
1557 
1558 /*
1559  * Displays the SelectPartitionPage.
1560  *
1561  * Next pages:
1562  *  SelectFileSystemPage (At once if unattended)
1563  *  SelectFileSystemPage (Default if free space is selected)
1564  *  CreatePrimaryPartitionPage
1565  *  CreateExtendedPartitionPage
1566  *  CreateLogicalPartitionPage
1567  *  ConfirmDeleteSystemPartitionPage (if the selected partition is the system partition, aka with the boot flag set)
1568  *  DeletePartitionPage
1569  *  QuitPage
1570  *
1571  * SIDEEFFECTS
1572  *  Set InstallShortcut (only if not unattended + free space is selected)
1573  *
1574  * RETURNS
1575  *   Number of the next page.
1576  */
1577 static PAGE_NUMBER
1578 SelectPartitionPage(PINPUT_RECORD Ir)
1579 {
1580     PARTLIST_UI ListUi;
1581     ULONG Error;
1582 
1583     if (PartitionList == NULL)
1584     {
1585         PartitionList = CreatePartitionList();
1586         if (PartitionList == NULL)
1587         {
1588             MUIDisplayError(ERROR_DRIVE_INFORMATION, Ir, POPUP_WAIT_ENTER);
1589             return QUIT_PAGE;
1590         }
1591         else if (IsListEmpty(&PartitionList->DiskListHead))
1592         {
1593             MUIDisplayError(ERROR_NO_HDD, Ir, POPUP_WAIT_ENTER);
1594             return QUIT_PAGE;
1595         }
1596 
1597         /* Reset the formatter machine state */
1598         TempPartition = NULL;
1599         FormatState = Start;
1600     }
1601 
1602     if (RepairUpdateFlag)
1603     {
1604         ASSERT(CurrentInstallation);
1605 
1606         /* Determine the selected installation disk & partition */
1607         InstallPartition = SelectPartition(PartitionList,
1608                                            CurrentInstallation->DiskNumber,
1609                                            CurrentInstallation->PartitionNumber);
1610         if (!InstallPartition)
1611         {
1612             DPRINT1("RepairUpdateFlag == TRUE, SelectPartition() returned FALSE, assert!\n");
1613             ASSERT(FALSE);
1614         }
1615         ASSERT(!IsContainerPartition(InstallPartition->PartitionType));
1616 
1617         return SELECT_FILE_SYSTEM_PAGE;
1618     }
1619 
1620     MUIDisplayPage(SELECT_PARTITION_PAGE);
1621 
1622     InitPartitionListUi(&ListUi, PartitionList,
1623                         CurrentPartition,
1624                         2, 23,
1625                         xScreen - 3,
1626                         yScreen - 3);
1627     DrawPartitionList(&ListUi);
1628 
1629     if (IsUnattendedSetup)
1630     {
1631         /* Determine the selected installation disk & partition */
1632         InstallPartition = SelectPartition(PartitionList,
1633                                            USetupData.DestinationDiskNumber,
1634                                            USetupData.DestinationPartitionNumber);
1635         if (!InstallPartition)
1636         {
1637             CurrentPartition = ListUi.CurrentPartition;
1638 
1639             if (USetupData.AutoPartition)
1640             {
1641                 ASSERT(CurrentPartition != NULL);
1642                 ASSERT(!IsContainerPartition(CurrentPartition->PartitionType));
1643 
1644                 if (CurrentPartition->LogicalPartition)
1645                 {
1646                     CreateLogicalPartition(PartitionList,
1647                                            CurrentPartition,
1648                                            CurrentPartition->SectorCount.QuadPart,
1649                                            TRUE);
1650                 }
1651                 else
1652                 {
1653                     CreatePrimaryPartition(PartitionList,
1654                                            CurrentPartition,
1655                                            CurrentPartition->SectorCount.QuadPart,
1656                                            TRUE);
1657                 }
1658 
1659 // FIXME?? Aren't we going to enter an infinite loop, if this test fails??
1660                 if (!IsDiskSizeValid(CurrentPartition))
1661                 {
1662                     MUIDisplayError(ERROR_INSUFFICIENT_PARTITION_SIZE, Ir, POPUP_WAIT_ANY_KEY,
1663                                     USetupData.RequiredPartitionDiskSpace);
1664                     return SELECT_PARTITION_PAGE; /* let the user select another partition */
1665                 }
1666 
1667                 InstallPartition = CurrentPartition;
1668                 return SELECT_FILE_SYSTEM_PAGE;
1669             }
1670         }
1671         else
1672         {
1673             ASSERT(!IsContainerPartition(InstallPartition->PartitionType));
1674 
1675             DrawPartitionList(&ListUi); // FIXME: Doesn't make much sense...
1676 
1677 // FIXME?? Aren't we going to enter an infinite loop, if this test fails??
1678             if (!IsDiskSizeValid(InstallPartition))
1679             {
1680                 MUIDisplayError(ERROR_INSUFFICIENT_PARTITION_SIZE, Ir, POPUP_WAIT_ANY_KEY,
1681                                 USetupData.RequiredPartitionDiskSpace);
1682                 return SELECT_PARTITION_PAGE; /* let the user select another partition */
1683             }
1684 
1685             return SELECT_FILE_SYSTEM_PAGE;
1686         }
1687     }
1688 
1689     while (TRUE)
1690     {
1691         CurrentPartition = ListUi.CurrentPartition;
1692 
1693         /* Update status text */
1694         if (CurrentPartition == NULL)
1695         {
1696             CONSOLE_SetStatusText(MUIGetString(STRING_INSTALLCREATEPARTITION));
1697         }
1698         else if (CurrentPartition->LogicalPartition)
1699         {
1700             if (CurrentPartition->IsPartitioned)
1701             {
1702                 CONSOLE_SetStatusText(MUIGetString(STRING_DELETEPARTITION));
1703             }
1704             else
1705             {
1706                 CONSOLE_SetStatusText(MUIGetString(STRING_INSTALLCREATELOGICAL));
1707             }
1708         }
1709         else
1710         {
1711             if (CurrentPartition->IsPartitioned)
1712             {
1713                 if (IsContainerPartition(CurrentPartition->PartitionType))
1714                 {
1715                     CONSOLE_SetStatusText(MUIGetString(STRING_DELETEPARTITION));
1716                 }
1717                 else
1718                 {
1719                     CONSOLE_SetStatusText(MUIGetString(STRING_INSTALLDELETEPARTITION));
1720                 }
1721             }
1722             else
1723             {
1724                 CONSOLE_SetStatusText(MUIGetString(STRING_INSTALLCREATEPARTITION));
1725             }
1726         }
1727 
1728         CONSOLE_ConInKey(Ir);
1729 
1730         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1731             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
1732         {
1733             if (ConfirmQuit(Ir))
1734             {
1735                 DestroyPartitionList(PartitionList);
1736                 PartitionList = NULL;
1737                 return QUIT_PAGE;
1738             }
1739 
1740             break;
1741         }
1742         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1743                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DOWN))  /* DOWN */
1744         {
1745             ScrollDownPartitionList(&ListUi);
1746         }
1747         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1748                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_UP))  /* UP */
1749         {
1750             ScrollUpPartitionList(&ListUi);
1751         }
1752         else if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_RETURN)  /* ENTER */
1753         {
1754             ASSERT(CurrentPartition != NULL);
1755 
1756             if (IsContainerPartition(CurrentPartition->PartitionType))
1757                 continue; // return SELECT_PARTITION_PAGE;
1758 
1759             /*
1760              * Check whether the user wants to install ReactOS on a disk that
1761              * is not recognized by the computer's firmware and if so, display
1762              * a warning since such disks may not be bootable.
1763              */
1764             if (CurrentPartition->DiskEntry->MediaType == FixedMedia &&
1765                 !CurrentPartition->DiskEntry->BiosFound)
1766             {
1767                 PopupError("The disk you have selected for installing ReactOS\n"
1768                            "is not visible by the firmware of your computer,\n"
1769                            "and so may not be bootable.\n"
1770                            "Press ENTER to continue nonetheless.",
1771                            MUIGetString(STRING_CONTINUE),
1772                            Ir, POPUP_WAIT_ENTER);
1773                 // return SELECT_PARTITION_PAGE;
1774             }
1775 
1776             if (CurrentPartition->IsPartitioned == FALSE)
1777             {
1778                 if (CurrentPartition->LogicalPartition)
1779                 {
1780                     Error = LogicalPartitionCreationChecks(CurrentPartition);
1781                     if (Error != NOT_AN_ERROR)
1782                     {
1783                         MUIDisplayError(Error, Ir, POPUP_WAIT_ANY_KEY);
1784                         return SELECT_PARTITION_PAGE;
1785                     }
1786 
1787                     CreateLogicalPartition(PartitionList,
1788                                            CurrentPartition,
1789                                            0ULL,
1790                                            TRUE);
1791                 }
1792                 else
1793                 {
1794                     Error = PrimaryPartitionCreationChecks(CurrentPartition);
1795                     if (Error != NOT_AN_ERROR)
1796                     {
1797                         MUIDisplayError(Error, Ir, POPUP_WAIT_ANY_KEY);
1798                         return SELECT_PARTITION_PAGE;
1799                     }
1800 
1801                     CreatePrimaryPartition(PartitionList,
1802                                            CurrentPartition,
1803                                            0ULL,
1804                                            TRUE);
1805                 }
1806             }
1807 
1808             if (!IsDiskSizeValid(CurrentPartition))
1809             {
1810                 MUIDisplayError(ERROR_INSUFFICIENT_PARTITION_SIZE, Ir, POPUP_WAIT_ANY_KEY,
1811                                 USetupData.RequiredPartitionDiskSpace);
1812                 return SELECT_PARTITION_PAGE; /* let the user select another partition */
1813             }
1814 
1815             InstallPartition = CurrentPartition;
1816             return SELECT_FILE_SYSTEM_PAGE;
1817         }
1818         else if (Ir->Event.KeyEvent.wVirtualKeyCode == 'P')  /* P */
1819         {
1820             ASSERT(CurrentPartition != NULL);
1821 
1822             if (CurrentPartition->LogicalPartition == FALSE)
1823             {
1824                 Error = PrimaryPartitionCreationChecks(CurrentPartition);
1825                 if (Error != NOT_AN_ERROR)
1826                 {
1827                     MUIDisplayError(Error, Ir, POPUP_WAIT_ANY_KEY);
1828                     return SELECT_PARTITION_PAGE;
1829                 }
1830 
1831                 return CREATE_PRIMARY_PARTITION_PAGE;
1832             }
1833         }
1834         else if (Ir->Event.KeyEvent.wVirtualKeyCode == 'E')  /* E */
1835         {
1836             ASSERT(CurrentPartition != NULL);
1837 
1838             if (CurrentPartition->LogicalPartition == FALSE)
1839             {
1840                 Error = ExtendedPartitionCreationChecks(CurrentPartition);
1841                 if (Error != NOT_AN_ERROR)
1842                 {
1843                     MUIDisplayError(Error, Ir, POPUP_WAIT_ANY_KEY);
1844                     return SELECT_PARTITION_PAGE;
1845                 }
1846 
1847                 return CREATE_EXTENDED_PARTITION_PAGE;
1848             }
1849         }
1850         else if (Ir->Event.KeyEvent.wVirtualKeyCode == 'L')  /* L */
1851         {
1852             ASSERT(CurrentPartition != NULL);
1853 
1854             if (CurrentPartition->LogicalPartition)
1855             {
1856                 Error = LogicalPartitionCreationChecks(CurrentPartition);
1857                 if (Error != NOT_AN_ERROR)
1858                 {
1859                     MUIDisplayError(Error, Ir, POPUP_WAIT_ANY_KEY);
1860                     return SELECT_PARTITION_PAGE;
1861                 }
1862 
1863                 return CREATE_LOGICAL_PARTITION_PAGE;
1864             }
1865         }
1866         else if (Ir->Event.KeyEvent.wVirtualKeyCode == 'D')  /* D */
1867         {
1868             UNICODE_STRING CurrentPartitionU;
1869             WCHAR PathBuffer[MAX_PATH];
1870 
1871             ASSERT(CurrentPartition != NULL);
1872 
1873             if (CurrentPartition->IsPartitioned == FALSE)
1874             {
1875                 MUIDisplayError(ERROR_DELETE_SPACE, Ir, POPUP_WAIT_ANY_KEY);
1876                 return SELECT_PARTITION_PAGE;
1877             }
1878 
1879 // TODO: Do something similar before trying to format the partition?
1880             if (!CurrentPartition->New &&
1881                 !IsContainerPartition(CurrentPartition->PartitionType) &&
1882                 CurrentPartition->FormatState != Unformatted)
1883             {
1884                 ASSERT(CurrentPartition->PartitionNumber != 0);
1885 
1886                 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
1887                         L"\\Device\\Harddisk%lu\\Partition%lu\\",
1888                         CurrentPartition->DiskEntry->DiskNumber,
1889                         CurrentPartition->PartitionNumber);
1890                 RtlInitUnicodeString(&CurrentPartitionU, PathBuffer);
1891 
1892                 /*
1893                  * Check whether the user attempts to delete the partition on which
1894                  * the installation source is present. If so, fail with an error.
1895                  */
1896                 // &USetupData.SourceRootPath
1897                 if (RtlPrefixUnicodeString(&CurrentPartitionU, &USetupData.SourcePath, TRUE))
1898                 {
1899                     MUIDisplayError(ERROR_SOURCE_PATH, Ir, POPUP_WAIT_ENTER);
1900                     return SELECT_PARTITION_PAGE;
1901                 }
1902             }
1903 
1904             if (CurrentPartition == PartitionList->SystemPartition ||
1905                 IsPartitionActive(CurrentPartition))
1906             {
1907                 return CONFIRM_DELETE_SYSTEM_PARTITION_PAGE;
1908             }
1909 
1910             return DELETE_PARTITION_PAGE;
1911         }
1912     }
1913 
1914     return SELECT_PARTITION_PAGE;
1915 }
1916 
1917 
1918 #define PARTITION_SIZE_INPUT_FIELD_LENGTH 9
1919 /* Restriction for MaxSize */
1920 #define PARTITION_MAXSIZE (pow(10, (PARTITION_SIZE_INPUT_FIELD_LENGTH - 1)) - 1)
1921 
1922 static VOID
1923 ShowPartitionSizeInputBox(SHORT Left,
1924                           SHORT Top,
1925                           SHORT Right,
1926                           SHORT Bottom,
1927                           ULONG MaxSize,
1928                           PWSTR InputBuffer,
1929                           PBOOLEAN Quit,
1930                           PBOOLEAN Cancel)
1931 {
1932     INPUT_RECORD Ir;
1933     COORD coPos;
1934     DWORD Written;
1935     CHAR Buffer[128];
1936     INT Length, Pos;
1937     WCHAR ch;
1938     SHORT iLeft;
1939     SHORT iTop;
1940 
1941     if (Quit != NULL)
1942         *Quit = FALSE;
1943 
1944     if (Cancel != NULL)
1945         *Cancel = FALSE;
1946 
1947     DrawBox(Left, Top, Right - Left + 1, Bottom - Top + 1);
1948 
1949     /* Print message */
1950     coPos.X = Left + 2;
1951     coPos.Y = Top + 2;
1952     strcpy(Buffer, MUIGetString(STRING_PARTITIONSIZE));
1953     iLeft = coPos.X + strlen(Buffer) + 1;
1954     iTop = coPos.Y;
1955 
1956     WriteConsoleOutputCharacterA(StdOutput,
1957                                  Buffer,
1958                                  strlen(Buffer),
1959                                  coPos,
1960                                  &Written);
1961 
1962     sprintf(Buffer, MUIGetString(STRING_MAXSIZE), MaxSize);
1963     coPos.X = iLeft + PARTITION_SIZE_INPUT_FIELD_LENGTH + 1;
1964     coPos.Y = iTop;
1965     WriteConsoleOutputCharacterA(StdOutput,
1966                                  Buffer,
1967                                  strlen(Buffer),
1968                                  coPos,
1969                                  &Written);
1970 
1971     swprintf(InputBuffer, L"%lu", MaxSize);
1972     Length = wcslen(InputBuffer);
1973     Pos = Length;
1974     CONSOLE_SetInputTextXY(iLeft,
1975                            iTop,
1976                            PARTITION_SIZE_INPUT_FIELD_LENGTH,
1977                            InputBuffer);
1978     CONSOLE_SetCursorXY(iLeft + Length, iTop);
1979     CONSOLE_SetCursorType(TRUE, TRUE);
1980 
1981     while (TRUE)
1982     {
1983         CONSOLE_ConInKey(&Ir);
1984 
1985         if ((Ir.Event.KeyEvent.uChar.AsciiChar == 0x00) &&
1986             (Ir.Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
1987         {
1988             if (Quit != NULL)
1989                 *Quit = TRUE;
1990 
1991             InputBuffer[0] = UNICODE_NULL;
1992             CONSOLE_SetCursorType(TRUE, FALSE);
1993             break;
1994         }
1995         else if (Ir.Event.KeyEvent.wVirtualKeyCode == VK_RETURN)    /* ENTER */
1996         {
1997             CONSOLE_SetCursorType(TRUE, FALSE);
1998             break;
1999         }
2000         else if (Ir.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)    /* ESC */
2001         {
2002             if (Cancel != NULL)
2003                 *Cancel = TRUE;
2004 
2005             InputBuffer[0] = UNICODE_NULL;
2006             CONSOLE_SetCursorType(TRUE, FALSE);
2007             break;
2008         }
2009         else if ((Ir.Event.KeyEvent.uChar.AsciiChar == 0x00) &&
2010                  (Ir.Event.KeyEvent.wVirtualKeyCode == VK_HOME))  /* HOME */
2011         {
2012             Pos = 0;
2013             CONSOLE_SetCursorXY(iLeft + Pos, iTop);
2014         }
2015         else if ((Ir.Event.KeyEvent.uChar.AsciiChar == 0x00) &&
2016                  (Ir.Event.KeyEvent.wVirtualKeyCode == VK_END))  /* END */
2017         {
2018             Pos = Length;
2019             CONSOLE_SetCursorXY(iLeft + Pos, iTop);
2020         }
2021         else if ((Ir.Event.KeyEvent.uChar.AsciiChar == 0x00) &&
2022                  (Ir.Event.KeyEvent.wVirtualKeyCode == VK_LEFT))  /* LEFT */
2023         {
2024             if (Pos > 0)
2025             {
2026                 Pos--;
2027                 CONSOLE_SetCursorXY(iLeft + Pos, iTop);
2028             }
2029         }
2030         else if ((Ir.Event.KeyEvent.uChar.AsciiChar == 0x00) &&
2031                  (Ir.Event.KeyEvent.wVirtualKeyCode == VK_RIGHT))  /* RIGHT */
2032         {
2033             if (Pos < Length)
2034             {
2035                 Pos++;
2036                 CONSOLE_SetCursorXY(iLeft + Pos, iTop);
2037             }
2038         }
2039         else if ((Ir.Event.KeyEvent.uChar.AsciiChar == 0x00) &&
2040                  (Ir.Event.KeyEvent.wVirtualKeyCode == VK_DELETE))  /* DEL */
2041         {
2042             if (Pos < Length)
2043             {
2044                 memmove(&InputBuffer[Pos],
2045                         &InputBuffer[Pos + 1],
2046                         (Length - Pos - 1) * sizeof(WCHAR));
2047                 InputBuffer[Length - 1] = UNICODE_NULL;
2048 
2049                 Length--;
2050                 CONSOLE_SetInputTextXY(iLeft,
2051                                        iTop,
2052                                        PARTITION_SIZE_INPUT_FIELD_LENGTH,
2053                                        InputBuffer);
2054                 CONSOLE_SetCursorXY(iLeft + Pos, iTop);
2055             }
2056         }
2057         else if (Ir.Event.KeyEvent.wVirtualKeyCode == VK_BACK)  /* BACKSPACE */
2058         {
2059             if (Pos > 0)
2060             {
2061                 if (Pos < Length)
2062                     memmove(&InputBuffer[Pos - 1],
2063                             &InputBuffer[Pos],
2064                             (Length - Pos) * sizeof(WCHAR));
2065                 InputBuffer[Length - 1] = UNICODE_NULL;
2066 
2067                 Pos--;
2068                 Length--;
2069                 CONSOLE_SetInputTextXY(iLeft,
2070                                        iTop,
2071                                        PARTITION_SIZE_INPUT_FIELD_LENGTH,
2072                                        InputBuffer);
2073                 CONSOLE_SetCursorXY(iLeft + Pos, iTop);
2074             }
2075         }
2076         else if (Ir.Event.KeyEvent.uChar.AsciiChar != 0x00)
2077         {
2078             if (Length < PARTITION_SIZE_INPUT_FIELD_LENGTH - 1)
2079             {
2080                 ch = (WCHAR)Ir.Event.KeyEvent.uChar.AsciiChar;
2081 
2082                 if ((ch >= L'0') && (ch <= L'9'))
2083                 {
2084                     if (Pos < Length)
2085                         memmove(&InputBuffer[Pos + 1],
2086                                 &InputBuffer[Pos],
2087                                 (Length - Pos) * sizeof(WCHAR));
2088                     InputBuffer[Length + 1] = UNICODE_NULL;
2089                     InputBuffer[Pos] = ch;
2090 
2091                     Pos++;
2092                     Length++;
2093                     CONSOLE_SetInputTextXY(iLeft,
2094                                            iTop,
2095                                            PARTITION_SIZE_INPUT_FIELD_LENGTH,
2096                                            InputBuffer);
2097                     CONSOLE_SetCursorXY(iLeft + Pos, iTop);
2098                 }
2099             }
2100         }
2101     }
2102 }
2103 
2104 
2105 /*
2106  * Displays the CreatePrimaryPartitionPage.
2107  *
2108  * Next pages:
2109  *  SelectPartitionPage
2110  *  SelectFileSystemPage (default)
2111  *  QuitPage
2112  *
2113  * RETURNS
2114  *   Number of the next page.
2115  */
2116 static PAGE_NUMBER
2117 CreatePrimaryPartitionPage(PINPUT_RECORD Ir)
2118 {
2119     PPARTENTRY PartEntry;
2120     PDISKENTRY DiskEntry;
2121     BOOLEAN Quit;
2122     BOOLEAN Cancel;
2123     WCHAR InputBuffer[50];
2124     ULONG MaxSize;
2125     ULONGLONG PartSize;
2126     ULONGLONG DiskSize;
2127     ULONGLONG SectorCount;
2128     PCSTR Unit;
2129 
2130     if (PartitionList == NULL || CurrentPartition == NULL)
2131     {
2132         /* FIXME: show an error dialog */
2133         return QUIT_PAGE;
2134     }
2135 
2136     PartEntry = CurrentPartition;
2137     DiskEntry = CurrentPartition->DiskEntry;
2138 
2139     CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
2140 
2141     CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_CHOOSENEWPARTITION));
2142 
2143     DiskSize = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2144     PrettifySize1(&DiskSize, &Unit);
2145 
2146     if (DiskEntry->DriverName.Length > 0)
2147     {
2148         CONSOLE_PrintTextXY(6, 10,
2149                             MUIGetString(STRING_HDINFOPARTCREATE_1),
2150                             DiskSize,
2151                             Unit,
2152                             DiskEntry->DiskNumber,
2153                             DiskEntry->Port,
2154                             DiskEntry->Bus,
2155                             DiskEntry->Id,
2156                             &DiskEntry->DriverName,
2157                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2158                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2159                                                                           "RAW");
2160     }
2161     else
2162     {
2163         CONSOLE_PrintTextXY(6, 10,
2164                             MUIGetString(STRING_HDINFOPARTCREATE_2),
2165                             DiskSize,
2166                             Unit,
2167                             DiskEntry->DiskNumber,
2168                             DiskEntry->Port,
2169                             DiskEntry->Bus,
2170                             DiskEntry->Id,
2171                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2172                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2173                                                                           "RAW");
2174     }
2175 
2176     CONSOLE_SetTextXY(6, 12, MUIGetString(STRING_HDDSIZE));
2177 
2178 #if 0
2179     CONSOLE_PrintTextXY(8, 10, "Maximum size of the new partition is %I64u MB",
2180                         CurrentPartition->SectorCount * DiskEntry->BytesPerSector / MB);
2181 #endif
2182 
2183     CONSOLE_SetStatusText(MUIGetString(STRING_CREATEPARTITION));
2184 
2185     PartEntry = CurrentPartition;
2186     while (TRUE)
2187     {
2188         MaxSize = (PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector) / MB;  /* in MBytes (rounded) */
2189 
2190         if (MaxSize > PARTITION_MAXSIZE)
2191             MaxSize = PARTITION_MAXSIZE;
2192 
2193         ShowPartitionSizeInputBox(12, 14, xScreen - 12, 17, /* left, top, right, bottom */
2194                                   MaxSize, InputBuffer, &Quit, &Cancel);
2195 
2196         if (Quit)
2197         {
2198             if (ConfirmQuit(Ir))
2199                 return QUIT_PAGE;
2200 
2201             break;
2202         }
2203         else if (Cancel)
2204         {
2205             return SELECT_PARTITION_PAGE;
2206         }
2207         else
2208         {
2209             PartSize = _wcstoui64(InputBuffer, NULL, 10);
2210 
2211             if (PartSize < 1)
2212             {
2213                 /* Too small */
2214                 continue;
2215             }
2216 
2217             if (PartSize > MaxSize)
2218             {
2219                 /* Too large */
2220                 continue;
2221             }
2222 
2223             /* Convert to bytes */
2224             if (PartSize == MaxSize)
2225             {
2226                 /* Use all of the unpartitioned disk space */
2227                 SectorCount = PartEntry->SectorCount.QuadPart;
2228             }
2229             else
2230             {
2231                 /* Calculate the sector count from the size in MB */
2232                 SectorCount = PartSize * MB / DiskEntry->BytesPerSector;
2233 
2234                 /* But never get larger than the unpartitioned disk space */
2235                 if (SectorCount > PartEntry->SectorCount.QuadPart)
2236                     SectorCount = PartEntry->SectorCount.QuadPart;
2237             }
2238 
2239             DPRINT ("Partition size: %I64u bytes\n", PartSize);
2240 
2241             CreatePrimaryPartition(PartitionList,
2242                                    CurrentPartition,
2243                                    SectorCount,
2244                                    FALSE);
2245 
2246             return SELECT_PARTITION_PAGE;
2247         }
2248     }
2249 
2250     return CREATE_PRIMARY_PARTITION_PAGE;
2251 }
2252 
2253 
2254 /*
2255  * Displays the CreateExtendedPartitionPage.
2256  *
2257  * Next pages:
2258  *  SelectPartitionPage (default)
2259  *  QuitPage
2260  *
2261  * RETURNS
2262  *   Number of the next page.
2263  */
2264 static PAGE_NUMBER
2265 CreateExtendedPartitionPage(PINPUT_RECORD Ir)
2266 {
2267     PPARTENTRY PartEntry;
2268     PDISKENTRY DiskEntry;
2269     BOOLEAN Quit;
2270     BOOLEAN Cancel;
2271     WCHAR InputBuffer[50];
2272     ULONG MaxSize;
2273     ULONGLONG PartSize;
2274     ULONGLONG DiskSize;
2275     ULONGLONG SectorCount;
2276     PCSTR Unit;
2277 
2278     if (PartitionList == NULL || CurrentPartition == NULL)
2279     {
2280         /* FIXME: show an error dialog */
2281         return QUIT_PAGE;
2282     }
2283 
2284     PartEntry = CurrentPartition;
2285     DiskEntry = CurrentPartition->DiskEntry;
2286 
2287     CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
2288 
2289     CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_CHOOSE_NEW_EXTENDED_PARTITION));
2290 
2291     DiskSize = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2292     PrettifySize1(&DiskSize, &Unit);
2293 
2294     if (DiskEntry->DriverName.Length > 0)
2295     {
2296         CONSOLE_PrintTextXY(6, 10,
2297                             MUIGetString(STRING_HDINFOPARTCREATE_1),
2298                             DiskSize,
2299                             Unit,
2300                             DiskEntry->DiskNumber,
2301                             DiskEntry->Port,
2302                             DiskEntry->Bus,
2303                             DiskEntry->Id,
2304                             &DiskEntry->DriverName,
2305                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2306                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2307                                                                           "RAW");
2308     }
2309     else
2310     {
2311         CONSOLE_PrintTextXY(6, 10,
2312                             MUIGetString(STRING_HDINFOPARTCREATE_2),
2313                             DiskSize,
2314                             Unit,
2315                             DiskEntry->DiskNumber,
2316                             DiskEntry->Port,
2317                             DiskEntry->Bus,
2318                             DiskEntry->Id,
2319                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2320                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2321                                                                           "RAW");
2322     }
2323 
2324     CONSOLE_SetTextXY(6, 12, MUIGetString(STRING_HDDSIZE));
2325 
2326 #if 0
2327     CONSOLE_PrintTextXY(8, 10, "Maximum size of the new partition is %I64u MB",
2328                         CurrentPartition->SectorCount * DiskEntry->BytesPerSector / MB);
2329 #endif
2330 
2331     CONSOLE_SetStatusText(MUIGetString(STRING_CREATEPARTITION));
2332 
2333     PartEntry = CurrentPartition;
2334     while (TRUE)
2335     {
2336         MaxSize = (PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector) / MB;  /* in MBytes (rounded) */
2337 
2338         if (MaxSize > PARTITION_MAXSIZE)
2339             MaxSize = PARTITION_MAXSIZE;
2340 
2341         ShowPartitionSizeInputBox(12, 14, xScreen - 12, 17, /* left, top, right, bottom */
2342                                   MaxSize, InputBuffer, &Quit, &Cancel);
2343 
2344         if (Quit)
2345         {
2346             if (ConfirmQuit(Ir))
2347                 return QUIT_PAGE;
2348 
2349             break;
2350         }
2351         else if (Cancel)
2352         {
2353             return SELECT_PARTITION_PAGE;
2354         }
2355         else
2356         {
2357             PartSize = _wcstoui64(InputBuffer, NULL, 10);
2358 
2359             if (PartSize < 1)
2360             {
2361                 /* Too small */
2362                 continue;
2363             }
2364 
2365             if (PartSize > MaxSize)
2366             {
2367                 /* Too large */
2368                 continue;
2369             }
2370 
2371             /* Convert to bytes */
2372             if (PartSize == MaxSize)
2373             {
2374                 /* Use all of the unpartitioned disk space */
2375                 SectorCount = PartEntry->SectorCount.QuadPart;
2376             }
2377             else
2378             {
2379                 /* Calculate the sector count from the size in MB */
2380                 SectorCount = PartSize * MB / DiskEntry->BytesPerSector;
2381 
2382                 /* But never get larger than the unpartitioned disk space */
2383                 if (SectorCount > PartEntry->SectorCount.QuadPart)
2384                     SectorCount = PartEntry->SectorCount.QuadPart;
2385             }
2386 
2387             DPRINT ("Partition size: %I64u bytes\n", PartSize);
2388 
2389             CreateExtendedPartition(PartitionList,
2390                                     CurrentPartition,
2391                                     SectorCount);
2392 
2393             return SELECT_PARTITION_PAGE;
2394         }
2395     }
2396 
2397     return CREATE_EXTENDED_PARTITION_PAGE;
2398 }
2399 
2400 
2401 /*
2402  * Displays the CreateLogicalPartitionPage.
2403  *
2404  * Next pages:
2405  *  SelectFileSystemPage (default)
2406  *  QuitPage
2407  *
2408  * RETURNS
2409  *   Number of the next page.
2410  */
2411 static PAGE_NUMBER
2412 CreateLogicalPartitionPage(PINPUT_RECORD Ir)
2413 {
2414     PPARTENTRY PartEntry;
2415     PDISKENTRY DiskEntry;
2416     BOOLEAN Quit;
2417     BOOLEAN Cancel;
2418     WCHAR InputBuffer[50];
2419     ULONG MaxSize;
2420     ULONGLONG PartSize;
2421     ULONGLONG DiskSize;
2422     ULONGLONG SectorCount;
2423     PCSTR Unit;
2424 
2425     if (PartitionList == NULL || CurrentPartition == NULL)
2426     {
2427         /* FIXME: show an error dialog */
2428         return QUIT_PAGE;
2429     }
2430 
2431     PartEntry = CurrentPartition;
2432     DiskEntry = CurrentPartition->DiskEntry;
2433 
2434     CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
2435 
2436     CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_CHOOSE_NEW_LOGICAL_PARTITION));
2437 
2438     DiskSize = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2439     PrettifySize1(&DiskSize, &Unit);
2440 
2441     if (DiskEntry->DriverName.Length > 0)
2442     {
2443         CONSOLE_PrintTextXY(6, 10,
2444                             MUIGetString(STRING_HDINFOPARTCREATE_1),
2445                             DiskSize,
2446                             Unit,
2447                             DiskEntry->DiskNumber,
2448                             DiskEntry->Port,
2449                             DiskEntry->Bus,
2450                             DiskEntry->Id,
2451                             &DiskEntry->DriverName,
2452                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2453                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2454                                                                           "RAW");
2455     }
2456     else
2457     {
2458         CONSOLE_PrintTextXY(6, 10,
2459                             MUIGetString(STRING_HDINFOPARTCREATE_2),
2460                             DiskSize,
2461                             Unit,
2462                             DiskEntry->DiskNumber,
2463                             DiskEntry->Port,
2464                             DiskEntry->Bus,
2465                             DiskEntry->Id,
2466                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2467                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2468                                                                           "RAW");
2469     }
2470 
2471     CONSOLE_SetTextXY(6, 12, MUIGetString(STRING_HDDSIZE));
2472 
2473 #if 0
2474     CONSOLE_PrintTextXY(8, 10, "Maximum size of the new partition is %I64u MB",
2475                         CurrentPartition->SectorCount * DiskEntry->BytesPerSector / MB);
2476 #endif
2477 
2478     CONSOLE_SetStatusText(MUIGetString(STRING_CREATEPARTITION));
2479 
2480     PartEntry = CurrentPartition;
2481     while (TRUE)
2482     {
2483         MaxSize = (PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector) / MB;  /* in MBytes (rounded) */
2484 
2485         if (MaxSize > PARTITION_MAXSIZE)
2486             MaxSize = PARTITION_MAXSIZE;
2487 
2488         ShowPartitionSizeInputBox(12, 14, xScreen - 12, 17, /* left, top, right, bottom */
2489                                   MaxSize, InputBuffer, &Quit, &Cancel);
2490 
2491         if (Quit)
2492         {
2493             if (ConfirmQuit(Ir))
2494                 return QUIT_PAGE;
2495 
2496             break;
2497         }
2498         else if (Cancel)
2499         {
2500             return SELECT_PARTITION_PAGE;
2501         }
2502         else
2503         {
2504             PartSize = _wcstoui64(InputBuffer, NULL, 10);
2505 
2506             if (PartSize < 1)
2507             {
2508                 /* Too small */
2509                 continue;
2510             }
2511 
2512             if (PartSize > MaxSize)
2513             {
2514                 /* Too large */
2515                 continue;
2516             }
2517 
2518             /* Convert to bytes */
2519             if (PartSize == MaxSize)
2520             {
2521                 /* Use all of the unpartitioned disk space */
2522                 SectorCount = PartEntry->SectorCount.QuadPart;
2523             }
2524             else
2525             {
2526                 /* Calculate the sector count from the size in MB */
2527                 SectorCount = PartSize * MB / DiskEntry->BytesPerSector;
2528 
2529                 /* But never get larger than the unpartitioned disk space */
2530                 if (SectorCount > PartEntry->SectorCount.QuadPart)
2531                     SectorCount = PartEntry->SectorCount.QuadPart;
2532             }
2533 
2534             DPRINT("Partition size: %I64u bytes\n", PartSize);
2535 
2536             CreateLogicalPartition(PartitionList,
2537                                    CurrentPartition,
2538                                    SectorCount,
2539                                    FALSE);
2540 
2541             return SELECT_PARTITION_PAGE;
2542         }
2543     }
2544 
2545     return CREATE_LOGICAL_PARTITION_PAGE;
2546 }
2547 
2548 
2549 /*
2550  * Displays the ConfirmDeleteSystemPartitionPage.
2551  *
2552  * Next pages:
2553  *  DeletePartitionPage (default)
2554  *  SelectPartitionPage
2555  *
2556  * RETURNS
2557  *   Number of the next page.
2558  */
2559 static PAGE_NUMBER
2560 ConfirmDeleteSystemPartitionPage(PINPUT_RECORD Ir)
2561 {
2562     MUIDisplayPage(CONFIRM_DELETE_SYSTEM_PARTITION_PAGE);
2563 
2564     while (TRUE)
2565     {
2566         CONSOLE_ConInKey(Ir);
2567 
2568         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
2569             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
2570         {
2571             if (ConfirmQuit(Ir))
2572                 return QUIT_PAGE;
2573 
2574             break;
2575         }
2576         else if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_RETURN) /* ENTER */
2577         {
2578             return DELETE_PARTITION_PAGE;
2579         }
2580         else if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)  /* ESC */
2581         {
2582             return SELECT_PARTITION_PAGE;
2583         }
2584     }
2585 
2586     return CONFIRM_DELETE_SYSTEM_PARTITION_PAGE;
2587 }
2588 
2589 
2590 /*
2591  * Displays the DeletePartitionPage.
2592  *
2593  * Next pages:
2594  *  SelectPartitionPage (default)
2595  *  QuitPage
2596  *
2597  * RETURNS
2598  *   Number of the next page.
2599  */
2600 static PAGE_NUMBER
2601 DeletePartitionPage(PINPUT_RECORD Ir)
2602 {
2603     PPARTENTRY PartEntry;
2604     PDISKENTRY DiskEntry;
2605     ULONGLONG DiskSize;
2606     ULONGLONG PartSize;
2607     PCSTR Unit;
2608     CHAR PartTypeString[32];
2609 
2610     if (PartitionList == NULL || CurrentPartition == NULL)
2611     {
2612         /* FIXME: show an error dialog */
2613         return QUIT_PAGE;
2614     }
2615 
2616     PartEntry = CurrentPartition;
2617     DiskEntry = CurrentPartition->DiskEntry;
2618 
2619     MUIDisplayPage(DELETE_PARTITION_PAGE);
2620 
2621     /* Adjust partition type */
2622     GetPartTypeStringFromPartitionType(PartEntry->PartitionType,
2623                                        PartTypeString,
2624                                        ARRAYSIZE(PartTypeString));
2625 
2626     PartSize = PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2627     PrettifySize2(&PartSize, &Unit);
2628 
2629     if (*PartTypeString == '\0') // STRING_FORMATUNKNOWN ??
2630     {
2631         CONSOLE_PrintTextXY(6, 10,
2632                             MUIGetString(STRING_HDDINFOUNK2),
2633                             (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
2634                             (PartEntry->DriveLetter == 0) ? '-' : ':',
2635                             PartEntry->PartitionType,
2636                             PartSize,
2637                             Unit);
2638     }
2639     else
2640     {
2641         CONSOLE_PrintTextXY(6, 10,
2642                             "   %c%c  %s    %I64u %s",
2643                             (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
2644                             (PartEntry->DriveLetter == 0) ? '-' : ':',
2645                             PartTypeString,
2646                             PartSize,
2647                             Unit);
2648     }
2649 
2650     DiskSize = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2651     PrettifySize1(&DiskSize, &Unit);
2652 
2653     if (DiskEntry->DriverName.Length > 0)
2654     {
2655         CONSOLE_PrintTextXY(6, 12,
2656                             MUIGetString(STRING_HDINFOPARTDELETE_1),
2657                             DiskSize,
2658                             Unit,
2659                             DiskEntry->DiskNumber,
2660                             DiskEntry->Port,
2661                             DiskEntry->Bus,
2662                             DiskEntry->Id,
2663                             &DiskEntry->DriverName,
2664                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2665                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2666                                                                           "RAW");
2667     }
2668     else
2669     {
2670         CONSOLE_PrintTextXY(6, 12,
2671                             MUIGetString(STRING_HDINFOPARTDELETE_2),
2672                             DiskSize,
2673                             Unit,
2674                             DiskEntry->DiskNumber,
2675                             DiskEntry->Port,
2676                             DiskEntry->Bus,
2677                             DiskEntry->Id,
2678                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2679                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2680                                                                           "RAW");
2681     }
2682 
2683     while (TRUE)
2684     {
2685         CONSOLE_ConInKey(Ir);
2686 
2687         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
2688             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
2689         {
2690             if (ConfirmQuit(Ir))
2691                 return QUIT_PAGE;
2692 
2693             break;
2694         }
2695         else if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)  /* ESC */
2696         {
2697             return SELECT_PARTITION_PAGE;
2698         }
2699         else if (Ir->Event.KeyEvent.wVirtualKeyCode == 'L') /* L */
2700         {
2701             DeletePartition(PartitionList,
2702                             CurrentPartition,
2703                             &CurrentPartition);
2704             return SELECT_PARTITION_PAGE;
2705         }
2706     }
2707 
2708     return DELETE_PARTITION_PAGE;
2709 }
2710 
2711 
2712 static VOID
2713 ResetFileSystemList(VOID)
2714 {
2715     if (!FileSystemList)
2716         return;
2717 
2718     DestroyFileSystemList(FileSystemList);
2719     FileSystemList = NULL;
2720 }
2721 
2722 /*
2723  * Displays the SelectFileSystemPage.
2724  *
2725  * Next pages:
2726  *  CheckFileSystemPage (At once if RepairUpdate is selected)
2727  *  CheckFileSystemPage (At once if Unattended and not USetupData.FormatPartition)
2728  *  FormatPartitionPage (At once if Unattended and USetupData.FormatPartition)
2729  *  SelectPartitionPage (If the user aborts)
2730  *  FormatPartitionPage (Default)
2731  *  QuitPage
2732  *
2733  * SIDEEFFECTS
2734  *  Calls UpdatePartitionType()
2735  *  Calls FindSupportedSystemPartition()
2736  *
2737  * RETURNS
2738  *   Number of the next page.
2739  */
2740 static PAGE_NUMBER
2741 SelectFileSystemPage(PINPUT_RECORD Ir)
2742 {
2743     PPARTENTRY PartEntry;
2744     PDISKENTRY DiskEntry;
2745     ULONGLONG DiskSize;
2746     ULONGLONG PartSize;
2747     PCSTR DiskUnit;
2748     PCSTR PartUnit;
2749     CHAR PartTypeString[32];
2750     FORMATMACHINESTATE PreviousFormatState;
2751     PCWSTR DefaultFs;
2752 
2753     DPRINT("SelectFileSystemPage()\n");
2754 
2755     if (PartitionList == NULL || InstallPartition == NULL)
2756     {
2757         /* FIXME: show an error dialog */
2758         return QUIT_PAGE;
2759     }
2760 
2761     /* Find or set the active system partition when starting formatting */
2762     if (FormatState == Start)
2763     {
2764         /*
2765          * If we install on a fixed disk, try to find a supported system
2766          * partition on the system. Otherwise if we install on a removable disk
2767          * use the install partition as the system partition.
2768          */
2769         if (InstallPartition->DiskEntry->MediaType == FixedMedia)
2770         {
2771             SystemPartition = FindSupportedSystemPartition(PartitionList,
2772                                                            FALSE,
2773                                                            InstallPartition->DiskEntry,
2774                                                            InstallPartition);
2775             /* Use the original system partition as the old active partition hint */
2776             PartEntry = PartitionList->SystemPartition;
2777 
2778             if ( SystemPartition && PartitionList->SystemPartition &&
2779                 (SystemPartition != PartitionList->SystemPartition) )
2780             {
2781                 DPRINT1("We are using a different system partition!!!!\n");
2782 
2783                 MUIDisplayPage(CHANGE_SYSTEM_PARTITION);
2784 
2785                 {
2786                 PPARTENTRY PartEntry; // Shadow variable
2787 
2788                 PartEntry = PartitionList->SystemPartition;
2789                 DiskEntry = PartEntry->DiskEntry;
2790 
2791                 /* Adjust partition size */
2792                 PartSize = PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2793                 if (PartSize >= 10 * GB) /* 10 GB */
2794                 {
2795                     PartSize = PartSize / GB;
2796                     PartUnit = MUIGetString(STRING_GB);
2797                 }
2798                 else
2799                 {
2800                     PartSize = PartSize / MB;
2801                     PartUnit = MUIGetString(STRING_MB);
2802                 }
2803 
2804                 /* Adjust partition type */
2805                 GetPartTypeStringFromPartitionType(PartEntry->PartitionType,
2806                                                    PartTypeString,
2807                                                    ARRAYSIZE(PartTypeString));
2808 
2809                 if (*PartTypeString == '\0') // STRING_FORMATUNKNOWN ??
2810                 {
2811                     CONSOLE_PrintTextXY(8, 10,
2812                                         MUIGetString(STRING_HDDINFOUNK4),
2813                                         (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
2814                                         (PartEntry->DriveLetter == 0) ? '-' : ':',
2815                                         PartEntry->PartitionType,
2816                                         PartSize,
2817                                         PartUnit);
2818                 }
2819                 else
2820                 {
2821                     CONSOLE_PrintTextXY(8, 10,
2822                                         "%c%c  %s    %I64u %s",
2823                                         (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
2824                                         (PartEntry->DriveLetter == 0) ? '-' : ':',
2825                                         PartTypeString,
2826                                         PartSize,
2827                                         PartUnit);
2828                 }
2829 
2830 
2831                 /* Adjust disk size */
2832                 DiskSize = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2833                 if (DiskSize >= 10 * GB) /* 10 GB */
2834                 {
2835                     DiskSize = DiskSize / GB;
2836                     DiskUnit = MUIGetString(STRING_GB);
2837                 }
2838                 else
2839                 {
2840                     DiskSize = DiskSize / MB;
2841                     DiskUnit = MUIGetString(STRING_MB);
2842                 }
2843 
2844                 CONSOLE_PrintTextXY(8, 14, MUIGetString(STRING_HDINFOPARTZEROED_1),
2845                                     DiskEntry->DiskNumber,
2846                                     DiskSize,
2847                                     DiskUnit,
2848                                     DiskEntry->Port,
2849                                     DiskEntry->Bus,
2850                                     DiskEntry->Id,
2851                                     &DiskEntry->DriverName,
2852                                     DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
2853                                     DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
2854                                                                                   "RAW");
2855 
2856 
2857                 PartEntry = SystemPartition;
2858                 DiskEntry = PartEntry->DiskEntry;
2859 
2860                 /* Adjust partition size */
2861                 PartSize = PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
2862                 if (PartSize >= 10 * GB) /* 10 GB */
2863                 {
2864                     PartSize = PartSize / GB;
2865                     PartUnit = MUIGetString(STRING_GB);
2866                 }
2867                 else
2868                 {
2869                     PartSize = PartSize / MB;
2870                     PartUnit = MUIGetString(STRING_MB);
2871                 }
2872 
2873                 /* Adjust partition type */
2874                 GetPartTypeStringFromPartitionType(PartEntry->PartitionType,
2875                                                    PartTypeString,
2876                                                    ARRAYSIZE(PartTypeString));
2877 
2878                 if (*PartTypeString == '\0') // STRING_FORMATUNKNOWN ??
2879                 {
2880                     CONSOLE_PrintTextXY(8, 23,
2881                                         MUIGetString(STRING_HDDINFOUNK4),
2882                                         (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
2883                                         (PartEntry->DriveLetter == 0) ? '-' : ':',
2884                                         PartEntry->PartitionType,
2885                                         PartSize,
2886                                         PartUnit);
2887                 }
2888                 else
2889                 {
2890                     CONSOLE_PrintTextXY(8, 23,
2891                                         "%c%c  %s    %I64u %s",
2892                                         (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
2893                                         (PartEntry->DriveLetter == 0) ? '-' : ':',
2894                                         PartTypeString,
2895                                         PartSize,
2896                                         PartUnit);
2897                 }
2898 
2899                 }
2900 
2901                 while (TRUE)
2902                 {
2903                     CONSOLE_ConInKey(Ir);
2904 
2905                     if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_RETURN) /* ENTER */
2906                     {
2907                         break;
2908                     }
2909                     else if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE)  /* ESC */
2910                     {
2911                         return SELECT_PARTITION_PAGE;
2912                     }
2913                 }
2914 
2915                 CONSOLE_ClearScreen();
2916                 CONSOLE_Flush();
2917             }
2918         }
2919         else // if (InstallPartition->DiskEntry->MediaType == RemovableMedia)
2920         {
2921             SystemPartition = InstallPartition;
2922             /* Don't specify any old active partition hint */
2923             PartEntry = NULL;
2924         }
2925 
2926         if (!SystemPartition)
2927         {
2928             /* FIXME: improve the error dialog */
2929             //
2930             // Error dialog should say that we cannot find a suitable
2931             // system partition and create one on the system. At this point,
2932             // it may be nice to ask the user whether he wants to continue,
2933             // or use an external drive as the system drive/partition
2934             // (e.g. floppy, USB drive, etc...)
2935             //
2936             PopupError("The ReactOS Setup could not find a supported system partition\n"
2937                        "on your system or could not create a new one. Without such partition\n"
2938                        "the Setup program cannot install ReactOS.\n"
2939                        "Press ENTER to return to the partition selection list.",
2940                        MUIGetString(STRING_CONTINUE),
2941                        Ir, POPUP_WAIT_ENTER);
2942             return SELECT_PARTITION_PAGE;
2943         }
2944 
2945         /*
2946          * If the system partition can be created in some
2947          * non-partitioned space, create it now.
2948          */
2949         if (!SystemPartition->IsPartitioned)
2950         {
2951             CreatePrimaryPartition(PartitionList,
2952                                    SystemPartition,
2953                                    0LL, // SystemPartition->SectorCount.QuadPart,
2954                                    TRUE);
2955             ASSERT(SystemPartition->IsPartitioned);
2956         }
2957 
2958         /* Set it as such */
2959         if (!SetActivePartition(PartitionList, SystemPartition, PartEntry))
2960         {
2961             DPRINT1("SetActivePartition(0x%p) failed?!\n", SystemPartition);
2962             ASSERT(FALSE);
2963         }
2964 
2965         /* Commit all partition changes to all the disks */
2966         if (!WritePartitionsToDisk(PartitionList))
2967         {
2968             DPRINT("WritePartitionsToDisk() failed\n");
2969             MUIDisplayError(ERROR_WRITE_PTABLE, Ir, POPUP_WAIT_ENTER);
2970             return QUIT_PAGE;
2971         }
2972 
2973         /*
2974          * In all cases, whether or not we are going to perform a formatting,
2975          * we must perform a filesystem check of both the system and the
2976          * installation partitions.
2977          */
2978         InstallPartition->NeedsCheck = TRUE;
2979         if (SystemPartition != InstallPartition)
2980             SystemPartition->NeedsCheck = TRUE;
2981 
2982         /*
2983          * In case we just repair an existing installation, or make
2984          * an unattended setup without formatting, just go to the
2985          * filesystem check step.
2986          */
2987         if (RepairUpdateFlag)
2988             return CHECK_FILE_SYSTEM_PAGE;
2989 
2990         if (IsUnattendedSetup && !USetupData.FormatPartition)
2991             return CHECK_FILE_SYSTEM_PAGE;
2992     }
2993 
2994     // ASSERT(SystemPartition->IsPartitioned);
2995 
2996     /* Reset the filesystem list for each partition that is to be formatted */
2997     ResetFileSystemList();
2998 
2999     PreviousFormatState = FormatState;
3000     switch (FormatState)
3001     {
3002         case Start:
3003         {
3004             /*
3005              * We start by formatting the system partition in case it is new
3006              * (it didn't exist before) and is not the same as the installation
3007              * partition. Otherwise we just require a filesystem check on it,
3008              * and start by formatting the installation partition instead.
3009              */
3010 
3011             ASSERT(SystemPartition->IsPartitioned);
3012 
3013             if ((SystemPartition != InstallPartition) &&
3014                 (SystemPartition->FormatState == Unformatted))
3015             {
3016                 TempPartition = SystemPartition;
3017                 TempPartition->NeedsCheck = TRUE;
3018 
3019                 // TODO: Should we let the user using a custom file-system,
3020                 // or should we always use FAT(32) for it?
3021                 // For "compatibility", FAT(32) would be best indeed.
3022 
3023                 FormatState = FormatSystemPartition;
3024                 DPRINT1("FormatState: Start --> FormatSystemPartition\n");
3025             }
3026             else
3027             {
3028                 TempPartition = InstallPartition;
3029                 TempPartition->NeedsCheck = TRUE;
3030 
3031                 if (SystemPartition != InstallPartition)
3032                 {
3033                     /* The system partition is separate, so it had better be formatted! */
3034                     ASSERT((SystemPartition->FormatState == Preformatted) ||
3035                            (SystemPartition->FormatState == Formatted));
3036 
3037                     /* Require a filesystem check on the system partition too */
3038                     SystemPartition->NeedsCheck = TRUE;
3039                 }
3040 
3041                 FormatState = FormatInstallPartition;
3042                 DPRINT1("FormatState: Start --> FormatInstallPartition\n");
3043             }
3044             break;
3045         }
3046 
3047         case FormatSystemPartition:
3048         {
3049             TempPartition = InstallPartition;
3050             TempPartition->NeedsCheck = TRUE;
3051 
3052             FormatState = FormatInstallPartition;
3053             DPRINT1("FormatState: FormatSystemPartition --> FormatInstallPartition\n");
3054             break;
3055         }
3056 
3057         case FormatInstallPartition:
3058         case FormatOtherPartition:
3059         {
3060             if (GetNextUnformattedPartition(PartitionList,
3061                                             NULL,
3062                                             &TempPartition))
3063             {
3064                 FormatState = FormatOtherPartition;
3065                 TempPartition->NeedsCheck = TRUE;
3066 
3067                 if (FormatState == FormatInstallPartition)
3068                     DPRINT1("FormatState: FormatInstallPartition --> FormatOtherPartition\n");
3069                 else
3070                     DPRINT1("FormatState: FormatOtherPartition --> FormatOtherPartition\n");
3071             }
3072             else
3073             {
3074                 FormatState = FormatDone;
3075 
3076                 if (FormatState == FormatInstallPartition)
3077                     DPRINT1("FormatState: FormatInstallPartition --> FormatDone\n");
3078                 else
3079                     DPRINT1("FormatState: FormatOtherPartition --> FormatDone\n");
3080 
3081                 return CHECK_FILE_SYSTEM_PAGE;
3082             }
3083             break;
3084         }
3085 
3086         case FormatDone:
3087         {
3088             DPRINT1("FormatState: FormatDone\n");
3089             return CHECK_FILE_SYSTEM_PAGE;
3090         }
3091 
3092         default:
3093         {
3094             DPRINT1("FormatState: Invalid value %ld\n", FormatState);
3095             ASSERT(FALSE);
3096             return QUIT_PAGE;
3097         }
3098     }
3099 
3100     PartEntry = TempPartition;
3101     DiskEntry = TempPartition->DiskEntry;
3102 
3103     ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
3104 
3105     /* Adjust disk size */
3106     DiskSize = DiskEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
3107     PrettifySize1(&DiskSize, &DiskUnit);
3108 
3109     /* Adjust partition size */
3110     PartSize = PartEntry->SectorCount.QuadPart * DiskEntry->BytesPerSector;
3111     PrettifySize2(&PartSize, &PartUnit);
3112 
3113     /* Adjust partition type */
3114     GetPartTypeStringFromPartitionType(PartEntry->PartitionType,
3115                                        PartTypeString,
3116                                        ARRAYSIZE(PartTypeString));
3117 
3118     MUIDisplayPage(SELECT_FILE_SYSTEM_PAGE);
3119 
3120     if (PartEntry->AutoCreate)
3121     {
3122         CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_NEWPARTITION));
3123 
3124 #if 0
3125         CONSOLE_PrintTextXY(8, 10, "Partition %lu (%I64u %s) %s of",
3126                             PartEntry->PartitionNumber,
3127                             PartSize,
3128                             PartUnit,
3129                             PartTypeString);
3130 #endif
3131 
3132         CONSOLE_PrintTextXY(8, 10, MUIGetString(STRING_HDINFOPARTZEROED_1),
3133                             DiskEntry->DiskNumber,
3134                             DiskSize,
3135                             DiskUnit,
3136                             DiskEntry->Port,
3137                             DiskEntry->Bus,
3138                             DiskEntry->Id,
3139                             &DiskEntry->DriverName,
3140                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
3141                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
3142                                                                           "RAW");
3143 
3144         CONSOLE_SetTextXY(6, 12, MUIGetString(STRING_PARTFORMAT));
3145 
3146         PartEntry->AutoCreate = FALSE;
3147     }
3148     else if (PartEntry->New)
3149     {
3150         switch (FormatState)
3151         {
3152             case FormatSystemPartition:
3153                 CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_NONFORMATTEDSYSTEMPART));
3154                 break;
3155 
3156             case FormatInstallPartition:
3157                 CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_NONFORMATTEDPART));
3158                 break;
3159 
3160             case FormatOtherPartition:
3161                 CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_NONFORMATTEDOTHERPART));
3162                 break;
3163 
3164             default:
3165                 ASSERT(FALSE);
3166                 break;
3167         }
3168 
3169         CONSOLE_PrintTextXY(8, 10, MUIGetString(STRING_HDINFOPARTZEROED_1),
3170                             DiskEntry->DiskNumber,
3171                             DiskSize,
3172                             DiskUnit,
3173                             DiskEntry->Port,
3174                             DiskEntry->Bus,
3175                             DiskEntry->Id,
3176                             &DiskEntry->DriverName,
3177                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
3178                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
3179                                                                           "RAW");
3180 
3181         CONSOLE_SetTextXY(6, 12, MUIGetString(STRING_PARTFORMAT));
3182     }
3183     else
3184     {
3185         CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_INSTALLONPART));
3186 
3187         if (*PartTypeString == '\0') // STRING_FORMATUNKNOWN ??
3188         {
3189             CONSOLE_PrintTextXY(8, 10,
3190                                 MUIGetString(STRING_HDDINFOUNK4),
3191                                 (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
3192                                 (PartEntry->DriveLetter == 0) ? '-' : ':',
3193                                 PartEntry->PartitionType,
3194                                 PartSize,
3195                                 PartUnit);
3196         }
3197         else
3198         {
3199             CONSOLE_PrintTextXY(8, 10,
3200                                 "%c%c  %s    %I64u %s",
3201                                 (PartEntry->DriveLetter == 0) ? '-' : (CHAR)PartEntry->DriveLetter,
3202                                 (PartEntry->DriveLetter == 0) ? '-' : ':',
3203                                 PartTypeString,
3204                                 PartSize,
3205                                 PartUnit);
3206         }
3207 
3208         CONSOLE_PrintTextXY(6, 12, MUIGetString(STRING_HDINFOPARTEXISTS_1),
3209                             DiskEntry->DiskNumber,
3210                             DiskSize,
3211                             DiskUnit,
3212                             DiskEntry->Port,
3213                             DiskEntry->Bus,
3214                             DiskEntry->Id,
3215                             &DiskEntry->DriverName,
3216                             DiskEntry->DiskStyle == PARTITION_STYLE_MBR ? "MBR" :
3217                             DiskEntry->DiskStyle == PARTITION_STYLE_GPT ? "GPT" :
3218                                                                           "RAW");
3219     }
3220 
3221     ASSERT(FileSystemList == NULL);
3222 
3223     if (IsUnattendedSetup)
3224     {
3225         ASSERT(USetupData.FormatPartition);
3226 
3227         switch (USetupData.FsType)
3228         {
3229             /* 1 is for BtrFS */
3230             case 1:
3231                 DefaultFs = L"BTRFS";
3232                 break;
3233 
3234             /* If we don't understand input, default to FAT */
3235             default:
3236                 DefaultFs = L"FAT";
3237                 break;
3238         }
3239     }
3240     else
3241     {
3242         /* By default select the "FAT" file system */
3243         DefaultFs = L"FAT";
3244     }
3245 
3246     /* Create the file system list */
3247     // TODO: Display only the FSes compatible with the selected partition!
3248     FileSystemList = CreateFileSystemList(6, 26,
3249                                           PartEntry->New ||
3250                                           PartEntry->FormatState == Unformatted,
3251                                           DefaultFs);
3252     if (FileSystemList == NULL)
3253     {
3254         /* FIXME: show an error dialog */
3255         return QUIT_PAGE;
3256     }
3257 
3258     if (IsUnattendedSetup)
3259     {
3260         ASSERT(USetupData.FormatPartition);
3261         return FORMAT_PARTITION_PAGE;
3262     }
3263 
3264     DrawFileSystemList(FileSystemList);
3265 
3266     while (TRUE)
3267     {
3268         CONSOLE_ConInKey(Ir);
3269 
3270         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3271             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
3272         {
3273             if (ConfirmQuit(Ir))
3274             {
3275                 /* Reset the filesystem list */
3276                 ResetFileSystemList();
3277                 return QUIT_PAGE;
3278             }
3279 
3280             break;
3281         }
3282         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3283                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE))  /* ESC */
3284         {
3285             /* Reset the formatter machine state */
3286             TempPartition = NULL;
3287             FormatState = Start;
3288 
3289             /* Reset the filesystem list */
3290             ResetFileSystemList();
3291 
3292             return SELECT_PARTITION_PAGE;
3293         }
3294         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3295                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DOWN))  /* DOWN */
3296         {
3297             ScrollDownFileSystemList(FileSystemList);
3298         }
3299         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3300                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_UP))  /* UP */
3301         {
3302             ScrollUpFileSystemList(FileSystemList);
3303         }
3304         else if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_RETURN) /* ENTER */
3305         {
3306             if (!FileSystemList->Selected->FileSystem)
3307             {
3308                 ASSERT(!TempPartition->New && TempPartition->FormatState != Unformatted);
3309 
3310                 /*
3311                  * Skip formatting this partition. We will also ignore
3312                  * filesystem checks on it, unless it is either the system
3313                  * or the installation partition.
3314                  */
3315                 if (TempPartition != SystemPartition &&
3316                     TempPartition != InstallPartition)
3317                 {
3318                     PartEntry->NeedsCheck = FALSE;
3319                 }
3320 
3321                 return SELECT_FILE_SYSTEM_PAGE;
3322             }
3323             else
3324             {
3325                 /* Format this partition */
3326                 return FORMAT_PARTITION_PAGE;
3327             }
3328         }
3329     }
3330 
3331     FormatState = PreviousFormatState;
3332 
3333     return SELECT_FILE_SYSTEM_PAGE;
3334 }
3335 
3336 
3337 /*
3338  * Displays the FormatPartitionPage.
3339  *
3340  * Next pages:
3341  *  InstallDirectoryPage (At once if IsUnattendedSetup or InstallShortcut)
3342  *  SelectPartitionPage  (At once)
3343  *  QuitPage
3344  *
3345  * SIDEEFFECTS
3346  *  Sets InstallPartition->FormatState
3347  *  Sets USetupData.DestinationRootPath
3348  *
3349  * RETURNS
3350  *   Number of the next page.
3351  */
3352 static PAGE_NUMBER
3353 FormatPartitionPage(PINPUT_RECORD Ir)
3354 {
3355     NTSTATUS Status;
3356     PPARTENTRY PartEntry;
3357     PDISKENTRY DiskEntry;
3358     PFILE_SYSTEM_ITEM SelectedFileSystem;
3359     WCHAR PathBuffer[MAX_PATH];
3360     CHAR Buffer[MAX_PATH];
3361 
3362     DPRINT("FormatPartitionPage()\n");
3363 
3364     MUIDisplayPage(FORMAT_PARTITION_PAGE);
3365 
3366     if (PartitionList == NULL || TempPartition == NULL)
3367     {
3368         /* FIXME: show an error dialog */
3369         return QUIT_PAGE;
3370     }
3371 
3372     PartEntry = TempPartition;
3373     DiskEntry = TempPartition->DiskEntry;
3374 
3375     ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
3376 
3377     SelectedFileSystem = FileSystemList->Selected;
3378     ASSERT(SelectedFileSystem && SelectedFileSystem->FileSystem);
3379 
3380     while (TRUE)
3381     {
3382         if (!IsUnattendedSetup)
3383             CONSOLE_ConInKey(Ir);
3384 
3385         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3386             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
3387         {
3388             if (ConfirmQuit(Ir))
3389             {
3390                 /* Reset the filesystem list */
3391                 ResetFileSystemList();
3392                 return QUIT_PAGE;
3393             }
3394 
3395             break;
3396         }
3397         else if (Ir->Event.KeyEvent.wVirtualKeyCode == VK_RETURN || IsUnattendedSetup) /* ENTER */
3398         {
3399             /*
3400              * Remove the "Press ENTER to continue" message prompt when the ENTER
3401              * key is pressed as the user wants to begin the partition formatting.
3402              */
3403             MUIClearStyledText(FORMAT_PARTITION_PAGE, TEXT_ID_FORMAT_PROMPT, TEXT_TYPE_REGULAR);
3404             CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
3405 
3406             /* Format the partition */
3407             Status = DoFormat(PartEntry,
3408                               SelectedFileSystem->FileSystem,
3409                               SelectedFileSystem->QuickFormat);
3410             if (Status == STATUS_PARTITION_FAILURE)
3411             {
3412                 MUIDisplayError(ERROR_WRITE_PTABLE, Ir, POPUP_WAIT_ENTER);
3413 
3414                 /* Reset the filesystem list */
3415                 ResetFileSystemList();
3416                 return QUIT_PAGE;
3417             }
3418             else if (Status == STATUS_UNRECOGNIZED_VOLUME)
3419             {
3420                 /* FIXME: show an error dialog */
3421                 // MUIDisplayError(ERROR_FORMATTING_PARTITION, Ir, POPUP_WAIT_ANY_KEY, PathBuffer);
3422 
3423                 /* Reset the filesystem list */
3424                 ResetFileSystemList();
3425                 return QUIT_PAGE;
3426             }
3427             else if (Status == STATUS_NOT_SUPPORTED)
3428             {
3429                 RtlStringCbPrintfA(Buffer,
3430                                    sizeof(Buffer),
3431                                    "Setup is currently unable to format a partition in %S.\n"
3432                                    "\n"
3433                                    "  \x07  Press ENTER to continue Setup.\n"
3434                                    "  \x07  Press F3 to quit Setup.",
3435                                    SelectedFileSystem->FileSystem);
3436 
3437                 PopupError(Buffer,
3438                            MUIGetString(STRING_QUITCONTINUE),
3439                            NULL, POPUP_WAIT_NONE);
3440 
3441                 while (TRUE)
3442                 {
3443                     CONSOLE_ConInKey(Ir);
3444 
3445                     if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x00 &&
3446                         Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3)  /* F3 */
3447                     {
3448                         if (ConfirmQuit(Ir))
3449                         {
3450                             /* Reset the filesystem list */
3451                             ResetFileSystemList();
3452                             return QUIT_PAGE;
3453                         }
3454                         else
3455                         {
3456                             return SELECT_FILE_SYSTEM_PAGE;
3457                         }
3458                     }
3459                     else if (Ir->Event.KeyEvent.uChar.AsciiChar == VK_RETURN) /* ENTER */
3460                     {
3461                         return SELECT_FILE_SYSTEM_PAGE;
3462                     }
3463                 }
3464             }
3465             else if (!NT_SUCCESS(Status))
3466             {
3467                 /** HACK!! **/
3468                 RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
3469                                     L"\\Device\\Harddisk%lu\\Partition%lu",
3470                                     DiskEntry->DiskNumber,
3471                                     PartEntry->PartitionNumber);
3472 
3473                 DPRINT1("FormatPartition() failed with status 0x%08lx\n", Status);
3474                 MUIDisplayError(ERROR_FORMATTING_PARTITION, Ir, POPUP_WAIT_ANY_KEY, PathBuffer);
3475 
3476                 /* Reset the filesystem list */
3477                 ResetFileSystemList();
3478                 return QUIT_PAGE;
3479             }
3480 
3481             return SELECT_FILE_SYSTEM_PAGE;
3482         }
3483     }
3484 
3485     return FORMAT_PARTITION_PAGE;
3486 }
3487 
3488 
3489 /*
3490  * Displays the CheckFileSystemPage.
3491  *
3492  * Next pages:
3493  *  InstallDirectoryPage (At once)
3494  *  QuitPage
3495  *
3496  * SIDEEFFECTS
3497  *  Inits or reloads FileSystemList
3498  *
3499  * RETURNS
3500  *   Number of the next page.
3501  */
3502 static PAGE_NUMBER
3503 CheckFileSystemPage(PINPUT_RECORD Ir)
3504 {
3505     NTSTATUS Status;
3506     PPARTENTRY PartEntry;
3507     CHAR Buffer[MAX_PATH];
3508 
3509     if (PartitionList == NULL)
3510     {
3511         /* FIXME: show an error dialog */
3512         return QUIT_PAGE;
3513     }
3514 
3515     if (!GetNextUncheckedPartition(PartitionList, NULL, &PartEntry))
3516     {
3517         return INSTALL_DIRECTORY_PAGE;
3518     }
3519 
3520     ASSERT(PartEntry->IsPartitioned && PartEntry->PartitionNumber != 0);
3521 
3522     CONSOLE_SetTextXY(6, 8, MUIGetString(STRING_CHECKINGPART));
3523     CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
3524 
3525     DPRINT1("CheckFileSystemPage -- PartitionType: 0x%02X ; FileSystem: %S\n",
3526             PartEntry->PartitionType, (*PartEntry->FileSystem ? PartEntry->FileSystem : L"n/a"));
3527 
3528     /* Check the partition */
3529     Status = DoChkdsk(PartEntry);
3530     if (Status == STATUS_NOT_SUPPORTED)
3531     {
3532         /*
3533          * Partition checking is not supported with the current filesystem,
3534          * so disable FS checks on it.
3535          */
3536         PartEntry->NeedsCheck = FALSE;
3537 
3538         RtlStringCbPrintfA(Buffer,
3539                            sizeof(Buffer),
3540                            "Setup is currently unable to check a partition formatted in %S.\n"
3541                            "\n"
3542                            "  \x07  Press ENTER to continue Setup.\n"
3543                            "  \x07  Press F3 to quit Setup.",
3544                            PartEntry->FileSystem);
3545 
3546         PopupError(Buffer,
3547                    MUIGetString(STRING_QUITCONTINUE),
3548                    NULL, POPUP_WAIT_NONE);
3549 
3550         while (TRUE)
3551         {
3552             CONSOLE_ConInKey(Ir);
3553 
3554             if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x00 &&
3555                 Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3)  /* F3 */
3556             {
3557                 if (ConfirmQuit(Ir))
3558                     return QUIT_PAGE;
3559                 else
3560                     return CHECK_FILE_SYSTEM_PAGE;
3561             }
3562             else if (Ir->Event.KeyEvent.uChar.AsciiChar == VK_RETURN) /* ENTER */
3563             {
3564                 return CHECK_FILE_SYSTEM_PAGE;
3565             }
3566         }
3567     }
3568     else if (!NT_SUCCESS(Status))
3569     {
3570         DPRINT1("ChkdskPartition() failed with status 0x%08lx\n", Status);
3571 
3572         RtlStringCbPrintfA(Buffer,
3573                            sizeof(Buffer),
3574                            "ChkDsk detected some disk errors.\n(Status 0x%08lx).\n",
3575                            Status);
3576 
3577         PopupError(Buffer,
3578                    MUIGetString(STRING_CONTINUE),
3579                    Ir, POPUP_WAIT_ENTER);
3580     }
3581 
3582     PartEntry->NeedsCheck = FALSE;
3583     return CHECK_FILE_SYSTEM_PAGE;
3584 }
3585 
3586 
3587 static BOOLEAN
3588 IsValidPath(
3589     IN PCWSTR InstallDir)
3590 {
3591     UINT i, Length;
3592 
3593     Length = wcslen(InstallDir);
3594 
3595     // TODO: Add check for 8.3 too.
3596 
3597     /* Path must be at least 2 characters long */
3598 //    if (Length < 2)
3599 //        return FALSE;
3600 
3601     /* Path must start with a backslash */
3602 //    if (InstallDir[0] != L'\\')
3603 //        return FALSE;
3604 
3605     /* Path must not end with a backslash */
3606     if (InstallDir[Length - 1] == L'\\')
3607         return FALSE;
3608 
3609     /* Path must not contain whitespace characters */
3610     for (i = 0; i < Length; i++)
3611     {
3612         if (iswspace(InstallDir[i]))
3613             return FALSE;
3614     }
3615 
3616     /* Path component must not end with a dot */
3617     for (i = 0; i < Length; i++)
3618     {
3619         if (InstallDir[i] == L'\\' && i > 0)
3620         {
3621             if (InstallDir[i - 1] == L'.')
3622                 return FALSE;
3623         }
3624     }
3625 
3626     if (InstallDir[Length - 1] == L'.')
3627         return FALSE;
3628 
3629     return TRUE;
3630 }
3631 
3632 
3633 /*
3634  * Displays the InstallDirectoryPage.
3635  *
3636  * Next pages:
3637  *  PrepareCopyPage
3638  *  QuitPage
3639  *
3640  * RETURNS
3641  *   Number of the next page.
3642  */
3643 static PAGE_NUMBER
3644 InstallDirectoryPage(PINPUT_RECORD Ir)
3645 {
3646     NTSTATUS Status;
3647     ULONG Length, Pos;
3648     WCHAR c;
3649     WCHAR InstallDir[MAX_PATH];
3650 
3651     /* We do not need the filesystem list anymore */
3652     ResetFileSystemList();
3653 
3654     if (PartitionList == NULL || InstallPartition == NULL)
3655     {
3656         /* FIXME: show an error dialog */
3657         return QUIT_PAGE;
3658     }
3659 
3660     // if (IsUnattendedSetup)
3661     if (RepairUpdateFlag)
3662         wcscpy(InstallDir, CurrentInstallation->PathComponent); // SystemNtPath
3663     else if (USetupData.InstallationDirectory[0])
3664         wcscpy(InstallDir, USetupData.InstallationDirectory);
3665     else
3666         wcscpy(InstallDir, L"\\ReactOS");
3667 
3668     /*
3669      * Check the validity of the predefined 'InstallDir'. If we are either
3670      * in unattended setup or in update/repair mode, and the installation path
3671      * is valid, just perform the installation. Otherwise (either in the case
3672      * of an invalid path, or we are in regular setup), display the UI and allow
3673      * the user to specify a new installation path.
3674      */
3675     if ((RepairUpdateFlag || IsUnattendedSetup) && IsValidPath(InstallDir))
3676     {
3677         Status = InitDestinationPaths(&USetupData, InstallDir, InstallPartition);
3678         if (!NT_SUCCESS(Status))
3679         {
3680             DPRINT1("InitDestinationPaths() failed. Status code: 0x%lx", Status);
3681             MUIDisplayError(ERROR_NO_BUILD_PATH, Ir, POPUP_WAIT_ENTER);
3682             return QUIT_PAGE;
3683         }
3684 
3685         /*
3686          * Check whether the user attempts to install ReactOS within the
3687          * installation source directory, or in a subdirectory thereof.
3688          * If so, fail with an error.
3689          */
3690         if (RtlPrefixUnicodeString(&USetupData.SourcePath, &USetupData.DestinationPath, TRUE))
3691         {
3692             MUIDisplayError(ERROR_SOURCE_DIR, Ir, POPUP_WAIT_ENTER);
3693             return INSTALL_DIRECTORY_PAGE;
3694         }
3695 
3696         return PREPARE_COPY_PAGE;
3697     }
3698 
3699     Length = wcslen(InstallDir);
3700     Pos = Length;
3701 
3702     MUIDisplayPage(INSTALL_DIRECTORY_PAGE);
3703     CONSOLE_SetInputTextXY(8, 11, 51, InstallDir);
3704     CONSOLE_SetCursorXY(8 + Pos, 11);
3705     CONSOLE_SetCursorType(TRUE, TRUE);
3706 
3707     while (TRUE)
3708     {
3709         CONSOLE_ConInKey(Ir);
3710 
3711         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3712             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
3713         {
3714             CONSOLE_SetCursorType(TRUE, FALSE);
3715 
3716             if (ConfirmQuit(Ir))
3717                 return QUIT_PAGE;
3718 
3719             CONSOLE_SetCursorType(TRUE, TRUE);
3720             break;
3721         }
3722         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3723                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DELETE))  /* DEL */
3724         {
3725             if (Pos < Length)
3726             {
3727                 memmove(&InstallDir[Pos],
3728                         &InstallDir[Pos + 1],
3729                         (Length - Pos - 1) * sizeof(WCHAR));
3730                 InstallDir[Length - 1] = UNICODE_NULL;
3731 
3732                 Length--;
3733                 CONSOLE_SetInputTextXY(8, 11, 51, InstallDir);
3734                 CONSOLE_SetCursorXY(8 + Pos, 11);
3735             }
3736         }
3737         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3738                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_HOME))  /* HOME */
3739         {
3740             Pos = 0;
3741             CONSOLE_SetCursorXY(8 + Pos, 11);
3742         }
3743         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3744                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_END))  /* END */
3745         {
3746             Pos = Length;
3747             CONSOLE_SetCursorXY(8 + Pos, 11);
3748         }
3749         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3750                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_LEFT))  /* LEFT */
3751         {
3752             if (Pos > 0)
3753             {
3754                 Pos--;
3755                 CONSOLE_SetCursorXY(8 + Pos, 11);
3756             }
3757         }
3758         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
3759                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_RIGHT))  /* RIGHT */
3760         {
3761             if (Pos < Length)
3762             {
3763                 Pos++;
3764                 CONSOLE_SetCursorXY(8 + Pos, 11);
3765             }
3766         }
3767         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
3768         {
3769             CONSOLE_SetCursorType(TRUE, FALSE);
3770 
3771             /*
3772              * Check for the validity of the installation directory and pop up
3773              * an error if it is not the case. Then the user can fix its input.
3774              */
3775             if (!IsValidPath(InstallDir))
3776             {
3777                 MUIDisplayError(ERROR_DIRECTORY_NAME, Ir, POPUP_WAIT_ENTER);
3778                 return INSTALL_DIRECTORY_PAGE;
3779             }
3780 
3781             Status = InitDestinationPaths(&USetupData, InstallDir, InstallPartition);
3782             if (!NT_SUCCESS(Status))
3783             {
3784                 DPRINT1("InitDestinationPaths() failed. Status code: 0x%lx", Status);
3785                 MUIDisplayError(ERROR_NO_BUILD_PATH, Ir, POPUP_WAIT_ENTER);
3786                 return QUIT_PAGE;
3787             }
3788 
3789             /*
3790              * Check whether the user attempts to install ReactOS within the
3791              * installation source directory, or in a subdirectory thereof.
3792              * If so, fail with an error.
3793              */
3794             if (RtlPrefixUnicodeString(&USetupData.SourcePath, &USetupData.DestinationPath, TRUE))
3795             {
3796                 MUIDisplayError(ERROR_SOURCE_DIR, Ir, POPUP_WAIT_ENTER);
3797                 return INSTALL_DIRECTORY_PAGE;
3798             }
3799 
3800             return PREPARE_COPY_PAGE;
3801         }
3802         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x08) /* BACKSPACE */
3803         {
3804             if (Pos > 0)
3805             {
3806                 if (Pos < Length)
3807                     memmove(&InstallDir[Pos - 1],
3808                             &InstallDir[Pos],
3809                             (Length - Pos) * sizeof(WCHAR));
3810                 InstallDir[Length - 1] = UNICODE_NULL;
3811 
3812                 Pos--;
3813                 Length--;
3814                 CONSOLE_SetInputTextXY(8, 11, 51, InstallDir);
3815                 CONSOLE_SetCursorXY(8 + Pos, 11);
3816             }
3817         }
3818         else if (isprint(Ir->Event.KeyEvent.uChar.AsciiChar))
3819         {
3820             if (Length < 50)
3821             {
3822                 c = (WCHAR)Ir->Event.KeyEvent.uChar.AsciiChar;
3823                 if (iswalpha(c) || iswdigit(c) || c == '.' || c == '\\' || c == '-' || c == '_')
3824                 {
3825                     if (Pos < Length)
3826                         memmove(&InstallDir[Pos + 1],
3827                                 &InstallDir[Pos],
3828                                 (Length - Pos) * sizeof(WCHAR));
3829                     InstallDir[Length + 1] = UNICODE_NULL;
3830                     InstallDir[Pos] = c;
3831 
3832                     Pos++;
3833                     Length++;
3834                     CONSOLE_SetInputTextXY(8, 11, 51, InstallDir);
3835                     CONSOLE_SetCursorXY(8 + Pos, 11);
3836                 }
3837             }
3838         }
3839     }
3840 
3841     return INSTALL_DIRECTORY_PAGE;
3842 }
3843 
3844 
3845 // PSETUP_ERROR_ROUTINE
3846 static VOID
3847 __cdecl
3848 USetupErrorRoutine(
3849     IN PUSETUP_DATA pSetupData,
3850     ...)
3851 {
3852     INPUT_RECORD Ir;
3853     va_list arg_ptr;
3854 
3855     va_start(arg_ptr, pSetupData);
3856 
3857     if (pSetupData->LastErrorNumber >= ERROR_SUCCESS &&
3858         pSetupData->LastErrorNumber <  ERROR_LAST_ERROR_CODE)
3859     {
3860         // Note: the "POPUP_WAIT_ENTER" actually depends on the LastErrorNumber...
3861         MUIDisplayErrorV(pSetupData->LastErrorNumber, &Ir, POPUP_WAIT_ENTER, arg_ptr);
3862     }
3863 
3864     va_end(arg_ptr);
3865 }
3866 
3867 /*
3868  * Displays the PrepareCopyPage.
3869  *
3870  * Next pages:
3871  *  FileCopyPage(At once)
3872  *  QuitPage
3873  *
3874  * SIDEEFFECTS
3875  * Calls PrepareFileCopy
3876  *
3877  * RETURNS
3878  *   Number of the next page.
3879  */
3880 static PAGE_NUMBER
3881 PrepareCopyPage(PINPUT_RECORD Ir)
3882 {
3883     // ERROR_NUMBER ErrorNumber;
3884     BOOLEAN Success;
3885 
3886     MUIDisplayPage(PREPARE_COPY_PAGE);
3887 
3888     /* ErrorNumber = */ Success = PrepareFileCopy(&USetupData, NULL);
3889     if (/*ErrorNumber != ERROR_SUCCESS*/ !Success)
3890     {
3891         // MUIDisplayError(ErrorNumber, Ir, POPUP_WAIT_ENTER);
3892         return QUIT_PAGE;
3893     }
3894 
3895     return FILE_COPY_PAGE;
3896 }
3897 
3898 typedef struct _COPYCONTEXT
3899 {
3900     ULONG TotalOperations;
3901     ULONG CompletedOperations;
3902     PPROGRESSBAR ProgressBar;
3903     PPROGRESSBAR MemoryBars[4];
3904 } COPYCONTEXT, *PCOPYCONTEXT;
3905 
3906 static VOID
3907 SetupUpdateMemoryInfo(IN PCOPYCONTEXT CopyContext,
3908                       IN BOOLEAN First)
3909 {
3910     SYSTEM_PERFORMANCE_INFORMATION PerfInfo;
3911 
3912     /* Get the memory information from the system */
3913     NtQuerySystemInformation(SystemPerformanceInformation,
3914                              &PerfInfo,
3915                              sizeof(PerfInfo),
3916                              NULL);
3917 
3918     /* Check if this is initial setup */
3919     if (First)
3920     {
3921         /* Set maximum limits to be total RAM pages */
3922         ProgressSetStepCount(CopyContext->MemoryBars[0], PerfInfo.CommitLimit);
3923         ProgressSetStepCount(CopyContext->MemoryBars[1], PerfInfo.CommitLimit);
3924         ProgressSetStepCount(CopyContext->MemoryBars[2], PerfInfo.CommitLimit);
3925     }
3926 
3927     /* Set current values */
3928     ProgressSetStep(CopyContext->MemoryBars[0], PerfInfo.PagedPoolPages + PerfInfo.NonPagedPoolPages);
3929     ProgressSetStep(CopyContext->MemoryBars[1], PerfInfo.ResidentSystemCachePage);
3930     ProgressSetStep(CopyContext->MemoryBars[2], PerfInfo.AvailablePages);
3931 }
3932 
3933 static UINT
3934 CALLBACK
3935 FileCopyCallback(PVOID Context,
3936                  UINT Notification,
3937                  UINT_PTR Param1,
3938                  UINT_PTR Param2)
3939 {
3940     PCOPYCONTEXT CopyContext = (PCOPYCONTEXT)Context;
3941     PFILEPATHS_W FilePathInfo;
3942     PCWSTR SrcFileName, DstFileName;
3943 
3944     switch (Notification)
3945     {
3946         case SPFILENOTIFY_STARTSUBQUEUE:
3947         {
3948             CopyContext->TotalOperations = (ULONG)Param2;
3949             CopyContext->CompletedOperations = 0;
3950             ProgressSetStepCount(CopyContext->ProgressBar,
3951                                  CopyContext->TotalOperations);
3952             SetupUpdateMemoryInfo(CopyContext, TRUE);
3953             break;
3954         }
3955 
3956         case SPFILENOTIFY_STARTDELETE:
3957         case SPFILENOTIFY_STARTRENAME:
3958         case SPFILENOTIFY_STARTCOPY:
3959         {
3960             FilePathInfo = (PFILEPATHS_W)Param1;
3961 
3962             if (Notification == SPFILENOTIFY_STARTDELETE)
3963             {
3964                 /* Display delete message */
3965                 ASSERT(Param2 == FILEOP_DELETE);
3966 
3967                 DstFileName = wcsrchr(FilePathInfo->Target, L'\\');
3968                 if (DstFileName) ++DstFileName;
3969                 else DstFileName = FilePathInfo->Target;
3970 
3971                 CONSOLE_SetStatusText(MUIGetString(STRING_DELETING),
3972                                       DstFileName);
3973             }
3974             else if (Notification == SPFILENOTIFY_STARTRENAME)
3975             {
3976                 /* Display move/rename message */
3977                 ASSERT(Param2 == FILEOP_RENAME);
3978 
3979                 SrcFileName = wcsrchr(FilePathInfo->Source, L'\\');
3980                 if (SrcFileName) ++SrcFileName;
3981                 else SrcFileName = FilePathInfo->Source;
3982 
3983                 DstFileName = wcsrchr(FilePathInfo->Target, L'\\');
3984                 if (DstFileName) ++DstFileName;
3985                 else DstFileName = FilePathInfo->Target;
3986 
3987                 if (!wcsicmp(SrcFileName, DstFileName))
3988                     Param2 = STRING_MOVING;
3989                 else
3990                     Param2 = STRING_RENAMING;
3991 
3992                 CONSOLE_SetStatusText(MUIGetString(Param2),
3993                                       SrcFileName, DstFileName);
3994             }
3995             else if (Notification == SPFILENOTIFY_STARTCOPY)
3996             {
3997                 /* Display copy message */
3998                 ASSERT(Param2 == FILEOP_COPY);
3999 
4000                 /* NOTE: When extracting from CABs the Source is the CAB name */
4001                 DstFileName = wcsrchr(FilePathInfo->Target, L'\\');
4002                 if (DstFileName) ++DstFileName;
4003                 else DstFileName = FilePathInfo->Target;
4004 
4005                 CONSOLE_SetStatusText(MUIGetString(STRING_COPYING),
4006                                       DstFileName);
4007 #ifdef __REACTOS__ /* HACK */
4008                 DoWatchDestFileName(DstFileName);
4009 #endif
4010             }
4011 
4012             SetupUpdateMemoryInfo(CopyContext, FALSE);
4013             break;
4014         }
4015 
4016         case SPFILENOTIFY_COPYERROR:
4017         {
4018             FilePathInfo = (PFILEPATHS_W)Param1;
4019 
4020             DPRINT1("An error happened while trying to copy file '%S' (error 0x%08lx), skipping it...\n",
4021                     FilePathInfo->Target, FilePathInfo->Win32Error);
4022             return FILEOP_SKIP;
4023         }
4024 
4025         case SPFILENOTIFY_ENDDELETE:
4026         case SPFILENOTIFY_ENDRENAME:
4027         case SPFILENOTIFY_ENDCOPY:
4028         {
4029             CopyContext->CompletedOperations++;
4030 
4031             /* SYSREG checkpoint */
4032             if (CopyContext->TotalOperations >> 1 == CopyContext->CompletedOperations)
4033                 DPRINT1("CHECKPOINT:HALF_COPIED\n");
4034 
4035             ProgressNextStep(CopyContext->ProgressBar);
4036             SetupUpdateMemoryInfo(CopyContext, FALSE);
4037             break;
4038         }
4039     }
4040 
4041     return FILEOP_DOIT;
4042 }
4043 
4044 
4045 /*
4046  * Displays the FileCopyPage.
4047  *
4048  * Next pages:
4049  *  RegistryPage(At once)
4050  *
4051  * SIDEEFFECTS
4052  *  Calls DoFileCopy
4053  *
4054  * RETURNS
4055  *   Number of the next page.
4056  */
4057 static PAGE_NUMBER
4058 FileCopyPage(PINPUT_RECORD Ir)
4059 {
4060     COPYCONTEXT CopyContext;
4061     UINT MemBarWidth;
4062 
4063     MUIDisplayPage(FILE_COPY_PAGE);
4064 
4065     /* Create context for the copy process */
4066     CopyContext.TotalOperations = 0;
4067     CopyContext.CompletedOperations = 0;
4068 
4069     /* Create the progress bar as well */
4070     CopyContext.ProgressBar = CreateProgressBar(13,
4071                                                 26,
4072                                                 xScreen - 13,
4073                                                 yScreen - 20,
4074                                                 10,
4075                                                 24,
4076                                                 TRUE,
4077                                                 MUIGetString(STRING_SETUPCOPYINGFILES));
4078 
4079     // fit memory bars to screen width, distribute them uniform
4080     MemBarWidth = (xScreen - 26) / 5;
4081     MemBarWidth -= MemBarWidth % 2;  // make even
4082     /* ATTENTION: The following progress bars are debug stuff, which should not be translated!! */
4083     /* Create the paged pool progress bar */
4084     CopyContext.MemoryBars[0] = CreateProgressBar(13,
4085                                                   40,
4086                                                   13 + MemBarWidth,
4087                                                   43,
4088                                                   13,
4089                                                   44,
4090                                                   FALSE,
4091                                                   "Kernel Pool");
4092 
4093     /* Create the non paged pool progress bar */
4094     CopyContext.MemoryBars[1] = CreateProgressBar((xScreen / 2)- (MemBarWidth / 2),
4095                                                   40,
4096                                                   (xScreen / 2) + (MemBarWidth / 2),
4097                                                   43,
4098                                                   (xScreen / 2)- (MemBarWidth / 2),
4099                                                   44,
4100                                                   FALSE,
4101                                                   "Kernel Cache");
4102 
4103     /* Create the global memory progress bar */
4104     CopyContext.MemoryBars[2] = CreateProgressBar(xScreen - 13 - MemBarWidth,
4105                                                   40,
4106                                                   xScreen - 13,
4107                                                   43,
4108                                                   xScreen - 13 - MemBarWidth,
4109                                                   44,
4110                                                   FALSE,
4111                                                   "Free Memory");
4112 
4113     /* Do the file copying */
4114     DoFileCopy(&USetupData, FileCopyCallback, &CopyContext);
4115 
4116     /* If we get here, we're done, so cleanup the progress bar */
4117     DestroyProgressBar(CopyContext.ProgressBar);
4118     DestroyProgressBar(CopyContext.MemoryBars[0]);
4119     DestroyProgressBar(CopyContext.MemoryBars[1]);
4120     DestroyProgressBar(CopyContext.MemoryBars[2]);
4121 
4122     /* Create the $winnt$.inf file */
4123     InstallSetupInfFile(&USetupData);
4124 
4125     /* Go display the next page */
4126     return REGISTRY_PAGE;
4127 }
4128 
4129 
4130 static VOID
4131 __cdecl
4132 RegistryStatus(IN REGISTRY_STATUS RegStatus, ...)
4133 {
4134     /* WARNING: Please keep this lookup table in sync with the resources! */
4135     static const UINT StringIDs[] =
4136     {
4137         STRING_DONE,                    /* Success */
4138         STRING_REGHIVEUPDATE,           /* RegHiveUpdate */
4139         STRING_IMPORTFILE,              /* ImportRegHive */
4140         STRING_DISPLAYSETTINGSUPDATE,   /* DisplaySettingsUpdate */
4141         STRING_LOCALESETTINGSUPDATE,    /* LocaleSettingsUpdate */
4142         STRING_ADDKBLAYOUTS,            /* KeybLayouts */
4143         STRING_KEYBOARDSETTINGSUPDATE,  /* KeybSettingsUpdate */
4144         STRING_CODEPAGEINFOUPDATE,      /* CodePageInfoUpdate */
4145     };
4146 
4147     va_list args;
4148 
4149     if (RegStatus < ARRAYSIZE(StringIDs))
4150     {
4151         va_start(args, RegStatus);
4152         CONSOLE_SetStatusTextV(MUIGetString(StringIDs[RegStatus]), args);
4153         va_end(args);
4154     }
4155     else
4156     {
4157         CONSOLE_SetStatusText("Unknown status %d", RegStatus);
4158     }
4159 }
4160 
4161 /*
4162  * Displays the RegistryPage.
4163  *
4164  * Next pages:
4165  *  SuccessPage (if RepairUpdate)
4166  *  BootLoaderPage (default)
4167  *  QuitPage
4168  *
4169  * SIDEEFFECTS
4170  *  Calls UpdateRegistry
4171  *
4172  * RETURNS
4173  *   Number of the next page.
4174  */
4175 static PAGE_NUMBER
4176 RegistryPage(PINPUT_RECORD Ir)
4177 {
4178     ULONG Error;
4179 
4180     MUIDisplayPage(REGISTRY_PAGE);
4181 
4182     Error = UpdateRegistry(&USetupData,
4183                            RepairUpdateFlag,
4184                            PartitionList,
4185                            InstallPartition->DriveLetter,
4186                            SelectedLanguageId,
4187                            RegistryStatus,
4188                            &s_SubstSettings);
4189     if (Error != ERROR_SUCCESS)
4190     {
4191         MUIDisplayError(Error, Ir, POPUP_WAIT_ENTER);
4192         return QUIT_PAGE;
4193     }
4194     else
4195     {
4196         CONSOLE_SetStatusText(MUIGetString(STRING_DONE));
4197         return BOOT_LOADER_PAGE;
4198     }
4199 }
4200 
4201 
4202 /*
4203  * Displays the BootLoaderPage.
4204  *
4205  * Next pages:
4206  *  SuccessPage (if RepairUpdate)
4207  *  BootLoaderHarddiskMbrPage
4208  *  BootLoaderHarddiskVbrPage
4209  *  BootLoaderFloppyPage
4210  *  SuccessPage
4211  *  QuitPage
4212  *
4213  * SIDEEFFECTS
4214  *  Calls RegInitializeRegistry
4215  *  Calls ImportRegistryFile
4216  *  Calls SetDefaultPagefile
4217  *  Calls SetMountedDeviceValues
4218  *
4219  * RETURNS
4220  *   Number of the next page.
4221  */
4222 static PAGE_NUMBER
4223 BootLoaderPage(PINPUT_RECORD Ir)
4224 {
4225     UCHAR PartitionType;
4226     BOOLEAN InstallOnFloppy;
4227     USHORT Line = 12;
4228     WCHAR PathBuffer[MAX_PATH];
4229 
4230     CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
4231 
4232     /* We must have a supported system partition by now */
4233     ASSERT(SystemPartition && SystemPartition->IsPartitioned && SystemPartition->PartitionNumber != 0);
4234 
4235     RtlFreeUnicodeString(&USetupData.SystemRootPath);
4236     RtlStringCchPrintfW(PathBuffer, ARRAYSIZE(PathBuffer),
4237             L"\\Device\\Harddisk%lu\\Partition%lu\\",
4238             SystemPartition->DiskEntry->DiskNumber,
4239             SystemPartition->PartitionNumber);
4240     RtlCreateUnicodeString(&USetupData.SystemRootPath, PathBuffer);
4241     DPRINT1("SystemRootPath: %wZ\n", &USetupData.SystemRootPath);
4242 
4243     PartitionType = SystemPartition->PartitionType;
4244 
4245     /* For unattended setup, skip MBR installation or install on floppy if needed */
4246     if (IsUnattendedSetup)
4247     {
4248         if ((USetupData.MBRInstallType == 0) ||
4249             (USetupData.MBRInstallType == 1))
4250         {
4251             goto Quit;
4252         }
4253     }
4254 
4255     /*
4256      * We may install an MBR or VBR, but before that, check whether
4257      * we need to actually install the VBR on floppy.
4258      */
4259     if (PartitionType == PARTITION_ENTRY_UNUSED)
4260     {
4261         DPRINT("Error: system partition invalid (unused)\n");
4262         InstallOnFloppy = TRUE;
4263     }
4264     else if (PartitionType == PARTITION_OS2BOOTMGR)
4265     {
4266         /* OS/2 boot manager partition */
4267         DPRINT("Found OS/2 boot manager partition\n");
4268         InstallOnFloppy = TRUE;
4269     }
4270     else if (PartitionType == PARTITION_LINUX)
4271     {
4272         /* Linux partition */
4273         DPRINT("Found Linux native partition (ext2/ext3/ReiserFS/BTRFS/etc)\n");
4274         InstallOnFloppy = FALSE;
4275     }
4276     else if (PartitionType == PARTITION_IFS)
4277     {
4278         /* NTFS partition */
4279         DPRINT("Found NTFS partition\n");
4280 
4281         // FIXME: Make it FALSE when we'll support NTFS installation!
4282         InstallOnFloppy = TRUE;
4283     }
4284     else if ((PartitionType == PARTITION_FAT_12) ||
4285              (PartitionType == PARTITION_FAT_16) ||
4286              (PartitionType == PARTITION_HUGE)   ||
4287              (PartitionType == PARTITION_XINT13) ||
4288              (PartitionType == PARTITION_FAT32)  ||
4289              (PartitionType == PARTITION_FAT32_XINT13))
4290     {
4291         DPRINT("Found FAT partition\n");
4292         InstallOnFloppy = FALSE;
4293     }
4294     else
4295     {
4296         /* Unknown partition */
4297         DPRINT("Unknown partition found\n");
4298         InstallOnFloppy = TRUE;
4299     }
4300 
4301     /* We should install on floppy */
4302     if (InstallOnFloppy)
4303     {
4304         USetupData.MBRInstallType = 1;
4305         goto Quit;
4306     }
4307 
4308     /* Is it an unattended install on hdd? */
4309     if (IsUnattendedSetup)
4310     {
4311         if ((USetupData.MBRInstallType == 2) ||
4312             (USetupData.MBRInstallType == 3))
4313         {
4314             goto Quit;
4315         }
4316     }
4317 
4318     MUIDisplayPage(BOOT_LOADER_PAGE);
4319     CONSOLE_InvertTextXY(8, Line, 60, 1);
4320 
4321     while (TRUE)
4322     {
4323         CONSOLE_ConInKey(Ir);
4324 
4325         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
4326             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_DOWN))  /* DOWN */
4327         {
4328             CONSOLE_NormalTextXY(8, Line, 60, 1);
4329 
4330             Line++;
4331             if (Line < 12)
4332                 Line = 15;
4333 
4334             if (Line > 15)
4335                 Line = 12;
4336 
4337             CONSOLE_InvertTextXY(8, Line, 60, 1);
4338         }
4339         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
4340                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_UP))  /* UP */
4341         {
4342             CONSOLE_NormalTextXY(8, Line, 60, 1);
4343 
4344             Line--;
4345             if (Line < 12)
4346                 Line = 15;
4347 
4348             if (Line > 15)
4349                 Line = 12;
4350 
4351             CONSOLE_InvertTextXY(8, Line, 60, 1);
4352         }
4353         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
4354                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_HOME))  /* HOME */
4355         {
4356             CONSOLE_NormalTextXY(8, Line, 60, 1);
4357 
4358             Line = 12;
4359 
4360             CONSOLE_InvertTextXY(8, Line, 60, 1);
4361         }
4362         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
4363                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_END))  /* END */
4364         {
4365             CONSOLE_NormalTextXY(8, Line, 60, 1);
4366 
4367             Line = 15;
4368 
4369             CONSOLE_InvertTextXY(8, Line, 60, 1);
4370         }
4371         else if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
4372                  (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
4373         {
4374             if (ConfirmQuit(Ir))
4375                 return QUIT_PAGE;
4376 
4377             break;
4378         }
4379         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)    /* ENTER */
4380         {
4381             if (Line == 12)
4382             {
4383                 /* Install on both MBR and VBR */
4384                 USetupData.MBRInstallType = 2;
4385                 break;
4386             }
4387             else if (Line == 13)
4388             {
4389                 /* Install on VBR only */
4390                 USetupData.MBRInstallType = 3;
4391                 break;
4392             }
4393             else if (Line == 14)
4394             {
4395                 /* Install on floppy */
4396                 USetupData.MBRInstallType = 1;
4397                 break;
4398             }
4399             else if (Line == 15)
4400             {
4401                 /* Skip MBR installation */
4402                 USetupData.MBRInstallType = 0;
4403                 break;
4404             }
4405 
4406             return BOOT_LOADER_PAGE;
4407         }
4408     }
4409 
4410 Quit:
4411     switch (USetupData.MBRInstallType)
4412     {
4413         /* Skip MBR installation */
4414         case 0:
4415             return SUCCESS_PAGE;
4416 
4417         /* Install on floppy */
4418         case 1:
4419             return BOOT_LOADER_FLOPPY_PAGE;
4420 
4421         /* Install on both MBR and VBR */
4422         case 2:
4423             return BOOT_LOADER_HARDDISK_MBR_PAGE;
4424 
4425         /* Install on VBR only */
4426         case 3:
4427             return BOOT_LOADER_HARDDISK_VBR_PAGE;
4428     }
4429 
4430     return BOOT_LOADER_PAGE;
4431 }
4432 
4433 
4434 /*
4435  * Displays the BootLoaderFloppyPage.
4436  *
4437  * Next pages:
4438  *  SuccessPage (At once)
4439  *  QuitPage
4440  *
4441  * SIDEEFFECTS
4442  *  Calls InstallFatBootcodeToFloppy()
4443  *
4444  * RETURNS
4445  *   Number of the next page.
4446  */
4447 static PAGE_NUMBER
4448 BootLoaderFloppyPage(PINPUT_RECORD Ir)
4449 {
4450     NTSTATUS Status;
4451 
4452     MUIDisplayPage(BOOT_LOADER_FLOPPY_PAGE);
4453 
4454 //  CONSOLE_SetStatusText(MUIGetString(STRING_PLEASEWAIT));
4455 
4456     while (TRUE)
4457     {
4458         CONSOLE_ConInKey(Ir);
4459 
4460         if ((Ir->Event.KeyEvent.uChar.AsciiChar == 0x00) &&
4461             (Ir->Event.KeyEvent.wVirtualKeyCode == VK_F3))  /* F3 */
4462         {
4463             if (ConfirmQuit(Ir))
4464                 return QUIT_PAGE;
4465 
4466             break;
4467         }
4468         else if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D)    /* ENTER */
4469         {
4470             Status = InstallFatBootcodeToFloppy(&USetupData.SourceRootPath,
4471                                                 &USetupData.DestinationArcPath);
4472             if (!NT_SUCCESS(Status))
4473             {
4474                 if (Status == STATUS_DEVICE_NOT_READY)
4475                     MUIDisplayError(ERROR_NO_FLOPPY, Ir, POPUP_WAIT_ENTER);
4476 
4477                 /* TODO: Print error message */
4478                 return BOOT_LOADER_FLOPPY_PAGE;
4479             }
4480 
4481             return SUCCESS_PAGE;
4482         }
4483     }
4484 
4485     return BOOT_LOADER_FLOPPY_PAGE;
4486 }
4487 
4488 
4489 /*
4490  * Displays the BootLoaderHarddiskVbrPage.
4491  *
4492  * Next pages:
4493  *  SuccessPage (At once)
4494  *  QuitPage
4495  *
4496  * SIDEEFFECTS
4497  *  Calls InstallVBRToPartition()
4498  *
4499  * RETURNS
4500  *   Number of the next page.
4501  */
4502 static PAGE_NUMBER
4503 BootLoaderHarddiskVbrPage(PINPUT_RECORD Ir)
4504 {
4505     NTSTATUS Status;
4506 
4507     Status = InstallVBRToPartition(&USetupData.SystemRootPath,
4508                                    &USetupData.SourceRootPath,
4509                                    &USetupData.DestinationArcPath,
4510                                    SystemPartition->FileSystem);
4511     if (!NT_SUCCESS(Status))
4512     {
4513         MUIDisplayError(ERROR_WRITE_BOOT, Ir, POPUP_WAIT_ENTER,
4514                         SystemPartition->FileSystem);
4515         return QUIT_PAGE;
4516     }
4517 
4518     return SUCCESS_PAGE;
4519 }
4520 
4521 
4522 /*
4523  * Displays the BootLoaderHarddiskMbrPage.
4524  *
4525  * Next pages:
4526  *  SuccessPage (At once)
4527  *  QuitPage
4528  *
4529  * SIDEEFFECTS
4530  *  Calls InstallVBRToPartition()
4531  *  Calls InstallMbrBootCodeToDisk()
4532  *
4533  * RETURNS
4534  *   Number of the next page.
4535  */
4536 static PAGE_NUMBER
4537 BootLoaderHarddiskMbrPage(PINPUT_RECORD Ir)
4538 {
4539     NTSTATUS Status;
4540     WCHAR DestinationDevicePathBuffer[MAX_PATH];
4541 
4542     /* Step 1: Write the VBR */
4543     Status = InstallVBRToPartition(&USetupData.SystemRootPath,
4544                                    &USetupData.SourceRootPath,
4545                                    &USetupData.DestinationArcPath,
4546                                    SystemPartition->FileSystem);
4547     if (!NT_SUCCESS(Status))
4548     {
4549         MUIDisplayError(ERROR_WRITE_BOOT, Ir, POPUP_WAIT_ENTER,
4550                         SystemPartition->FileSystem);
4551         return QUIT_PAGE;
4552     }
4553 
4554     /* Step 2: Write the MBR if the disk containing the system partition is not a super-floppy */
4555     if (!IsSuperFloppy(SystemPartition->DiskEntry))
4556     {
4557         RtlStringCchPrintfW(DestinationDevicePathBuffer, ARRAYSIZE(DestinationDevicePathBuffer),
4558                 L"\\Device\\Harddisk%d\\Partition0",
4559                 SystemPartition->DiskEntry->DiskNumber);
4560         Status = InstallMbrBootCodeToDisk(&USetupData.SystemRootPath,
4561                                           &USetupData.SourceRootPath,
4562                                           DestinationDevicePathBuffer);
4563         if (!NT_SUCCESS(Status))
4564         {
4565             DPRINT1("InstallMbrBootCodeToDisk() failed (Status %lx)\n", Status);
4566             MUIDisplayError(ERROR_INSTALL_BOOTCODE, Ir, POPUP_WAIT_ENTER, L"MBR");
4567             return QUIT_PAGE;
4568         }
4569     }
4570 
4571     return SUCCESS_PAGE;
4572 }
4573 
4574 
4575 /**
4576  * @name ProgressTimeOutStringHandler
4577  *
4578  * Handles the generation (displaying) of the timeout
4579  * countdown to the screen dynamically.
4580  *
4581  * @param   Bar
4582  *     A pointer to a progress bar.
4583  *
4584  * @param   AlwaysUpdate
4585  *     Constantly update the progress bar (boolean type).
4586  *
4587  * @param   Buffer
4588  *     A pointer to a string buffer.
4589  *
4590  * @param   cchBufferSize
4591  *     The buffer's size in number of characters.
4592  *
4593  * @return
4594  *     TRUE or FALSE on function termination.
4595  *
4596  */
4597 static
4598 BOOLEAN NTAPI
4599 ProgressTimeOutStringHandler(
4600     IN PPROGRESSBAR Bar,
4601     IN BOOLEAN AlwaysUpdate,
4602     OUT PSTR Buffer,
4603     IN SIZE_T cchBufferSize)
4604 {
4605     ULONG OldProgress = Bar->Progress;
4606 
4607     if (Bar->StepCount == 0)
4608     {
4609         Bar->Progress = 0;
4610     }
4611     else
4612     {
4613         Bar->Progress = Bar->StepCount - Bar->CurrentStep;
4614     }
4615 
4616     /* Build the progress string if it has changed */
4617     if (Bar->ProgressFormatText &&
4618         (AlwaysUpdate || (Bar->Progress != OldProgress)))
4619     {
4620         RtlStringCchPrintfA(Buffer, cchBufferSize,
4621                             Bar->ProgressFormatText, Bar->Progress / max(1, Bar->Width) + 1);
4622 
4623         return TRUE;
4624     }
4625 
4626     return FALSE;
4627 }
4628 
4629 /**
4630  * @name ProgressCountdown
4631  *
4632  * Displays and draws a red-coloured progress bar with a countdown.
4633  * When the timeout is reached, the flush page is displayed for reboot.
4634  *
4635  * @param   Ir
4636  *     A pointer to an input keyboard record.
4637  *
4638  * @param   TimeOut
4639  *     Initial countdown value in seconds.
4640  *
4641  * @return
4642  *     Nothing.
4643  *
4644  */
4645 static VOID
4646 ProgressCountdown(
4647     IN PINPUT_RECORD Ir,
4648     IN LONG TimeOut)
4649 {
4650     NTSTATUS Status;
4651     ULONG StartTime, BarWidth, TimerDiv;
4652     LONG TimeElapsed;
4653     LONG TimerValue, OldTimerValue;
4654     LARGE_INTEGER Timeout;
4655     PPROGRESSBAR ProgressBar;
4656     BOOLEAN RefreshProgress = TRUE;
4657 
4658     /* Bail out if the timeout is already zero */
4659     if (TimeOut <= 0)
4660         return;
4661 
4662     /* Create the timeout progress bar and set it up */
4663     ProgressBar = CreateProgressBarEx(13,
4664                                       26,
4665                                       xScreen - 13,
4666                                       yScreen - 20,
4667                                       10,
4668                                       24,
4669                                       TRUE,
4670                                       FOREGROUND_RED | BACKGROUND_BLUE,
4671                                       0,
4672                                       NULL,
4673                                       MUIGetString(STRING_REBOOTPROGRESSBAR),
4674                                       ProgressTimeOutStringHandler);
4675 
4676     BarWidth = max(1, ProgressBar->Width);
4677     TimerValue = TimeOut * BarWidth;
4678     ProgressSetStepCount(ProgressBar, TimerValue);
4679 
4680     StartTime = NtGetTickCount();
4681     CONSOLE_Flush();
4682 
4683     TimerDiv = 1000 / BarWidth;
4684     TimerDiv = max(1, TimerDiv);
4685     OldTimerValue = TimerValue;
4686     while (TRUE)
4687     {
4688         /* Decrease the timer */
4689 
4690         /*
4691          * Compute how much time the previous operations took.
4692          * This allows us in particular to take account for any time
4693          * elapsed if something slowed down.
4694          */
4695         TimeElapsed = NtGetTickCount() - StartTime;
4696         if (TimeElapsed >= TimerDiv)
4697         {
4698             /* Increase StartTime by steps of 1 / ProgressBar->Width seconds */
4699             TimeElapsed /= TimerDiv;
4700             StartTime += (TimerDiv * TimeElapsed);
4701 
4702             if (TimeElapsed <= TimerValue)
4703                 TimerValue -= TimeElapsed;
4704             else
4705                 TimerValue = 0;
4706 
4707             RefreshProgress = TRUE;
4708         }
4709 
4710         if (RefreshProgress)
4711         {
4712             ProgressSetStep(ProgressBar, OldTimerValue - TimerValue);
4713             RefreshProgress = FALSE;
4714         }
4715 
4716         /* Stop when the timer reaches zero */
4717         if (TimerValue <= 0)
4718             break;
4719 
4720         /* Check for user key presses */
4721 
4722         /*
4723          * If the timer is used, use a passive wait of maximum 1 second
4724          * while monitoring for incoming console input events, so that
4725          * we are still able to display the timing count.
4726          */
4727 
4728         /* Wait a maximum of 1 second for input events */
4729         TimeElapsed = NtGetTickCount() - StartTime;
4730         if (TimeElapsed < TimerDiv)
4731         {
4732             /* Convert the time to NT format */
4733             Timeout.QuadPart = (TimerDiv - TimeElapsed) * -10000LL;
4734             Status = NtWaitForSingleObject(StdInput, FALSE, &Timeout);
4735         }
4736         else
4737         {
4738             Status = STATUS_TIMEOUT;
4739         }
4740 
4741         /* Check whether the input event has been signaled, or a timeout happened */
4742         if (Status == STATUS_TIMEOUT)
4743         {
4744             continue;
4745         }
4746         if (Status != STATUS_WAIT_0)
4747         {
4748             /* An error happened, bail out */
4749             DPRINT1("NtWaitForSingleObject() failed, Status 0x%08lx\n", Status);
4750             break;
4751         }
4752 
4753         /* Check for an ENTER key press */
4754         while (CONSOLE_ConInKeyPeek(Ir))
4755         {
4756             if (Ir->Event.KeyEvent.uChar.AsciiChar == 0x0D) /* ENTER */
4757             {
4758                 /* Found it, stop waiting */
4759                 goto Exit;
4760             }
4761         }
4762     }
4763 
4764 Exit:
4765     /* Destroy the progress bar and quit */
4766     DestroyProgressBar(ProgressBar);
4767 }
4768 
4769 
4770 /*
4771  * Displays the QuitPage.
4772  *
4773  * Next pages:
4774  *  FlushPage (At once)
4775  *
4776  * SIDEEFFECTS
4777  *  Destroy the Lists
4778  *
4779  * RETURNS
4780  *   Number of the next page.
4781  */
4782 static PAGE_NUMBER
4783 QuitPage(PINPUT_RECORD Ir)
4784 {
4785     MUIDisplayPage(QUIT_PAGE);
4786 
4787     /* Destroy the NTOS installations list */
4788     if (NtOsInstallsList != NULL)
4789     {
4790         DestroyGenericList(NtOsInstallsList, TRUE);
4791         NtOsInstallsList = NULL;
4792     }
4793 
4794     /* Destroy the partition list */
4795     if (PartitionList != NULL)
4796     {
4797         DestroyPartitionList(PartitionList);
4798         PartitionList = NULL;
4799     }
4800 
4801     /* Reset the formatter machine state */
4802     TempPartition = NULL;
4803     FormatState = Start;
4804 
4805     /* Destroy the filesystem list */
4806     ResetFileSystemList();
4807 
4808     CONSOLE_SetStatusText(MUIGetString(STRING_REBOOTCOMPUTER2));
4809 
4810     /* Wait for maximum 15 seconds or an ENTER key before quitting */
4811     ProgressCountdown(Ir, 15);
4812     return FLUSH_PAGE;
4813 }
4814 
4815 
4816 /*
4817  * Displays the SuccessPage.
4818  *
4819  * Next pages:
4820  *  FlushPage (At once)
4821  *
4822  * SIDEEFFECTS
4823  *  Destroy the Lists
4824  *
4825  * RETURNS
4826  *   Number of the next page.
4827  */
4828 static PAGE_NUMBER
4829 SuccessPage(PINPUT_RECORD Ir)
4830 {
4831     MUIDisplayPage(SUCCESS_PAGE);
4832 
4833     if (IsUnattendedSetup)
4834         return FLUSH_PAGE;
4835 
4836     /* Wait for maximum 15 seconds or an ENTER key before quitting */
4837     ProgressCountdown(Ir, 15);
4838     return FLUSH_PAGE;
4839 }
4840 
4841 
4842 /*
4843  * Displays the FlushPage.
4844  *
4845  * Next pages:
4846  *  RebootPage (At once)
4847  *
4848  * RETURNS
4849  *   Number of the next page.
4850  */
4851 static PAGE_NUMBER
4852 FlushPage(PINPUT_RECORD Ir)
4853 {
4854     MUIDisplayPage(FLUSH_PAGE);
4855     return REBOOT_PAGE;
4856 }
4857 
4858 
4859 /*
4860  * The start routine and page management
4861  */
4862 NTSTATUS
4863 RunUSetup(VOID)
4864 {
4865     NTSTATUS Status;
4866     INPUT_RECORD Ir;
4867     PAGE_NUMBER Page;
4868     BOOLEAN Old;
4869 
4870     InfSetHeap(ProcessHeap);
4871 
4872     /* Tell the Cm this is a setup boot, and it has to behave accordingly */
4873     Status = NtInitializeRegistry(CM_BOOT_FLAG_SETUP);
4874     if (!NT_SUCCESS(Status))
4875         DPRINT1("NtInitializeRegistry() failed (Status 0x%08lx)\n", Status);
4876 
4877     /* Initialize the user-mode PnP manager */
4878     Status = InitializeUserModePnpManager(&USetupData.SetupInf);
4879     if (!NT_SUCCESS(Status))
4880     {
4881         // PrintString(??);
4882         DPRINT1("The user-mode PnP manager could not initialize (Status 0x%08lx), expect unavailable devices!\n", Status);
4883     }
4884 
4885     if (!CONSOLE_Init())
4886     {
4887         PrintString(MUIGetString(STRING_CONSOLEFAIL1));
4888         PrintString(MUIGetString(STRING_CONSOLEFAIL2));
4889         PrintString(MUIGetString(STRING_CONSOLEFAIL3));
4890 
4891         /* We failed to initialize the video, just quit the installer */
4892         return STATUS_APP_INIT_FAILURE;
4893     }
4894 
4895     /* Initialize Setup, phase 0 */
4896     InitializeSetup(&USetupData, 0);
4897     USetupData.ErrorRoutine = USetupErrorRoutine;
4898 
4899     /* Hide the cursor and clear the screen and keyboard buffer */
4900     CONSOLE_SetCursorType(TRUE, FALSE);
4901     CONSOLE_ClearScreen();
4902     CONSOLE_Flush();
4903 
4904     /* Global Initialization page */
4905     Page = SetupStartPage(&Ir);
4906 
4907     while (Page != REBOOT_PAGE && Page != RECOVERY_PAGE)
4908     {
4909         CONSOLE_ClearScreen();
4910         CONSOLE_Flush();
4911 
4912         // CONSOLE_SetUnderlinedTextXY(4, 3, " ReactOS " KERNEL_VERSION_STR " Setup ");
4913 
4914         switch (Page)
4915         {
4916             /* Language page */
4917             case LANGUAGE_PAGE:
4918                 Page = LanguagePage(&Ir);
4919                 break;
4920 
4921             /* Welcome page */
4922             case WELCOME_PAGE:
4923                 Page = WelcomePage(&Ir);
4924                 break;
4925 
4926             /* License page */
4927             case LICENSE_PAGE:
4928                 Page = LicensePage(&Ir);
4929                 break;
4930 
4931             /* Install pages */
4932             case INSTALL_INTRO_PAGE:
4933                 Page = InstallIntroPage(&Ir);
4934                 break;
4935 
4936 #if 0
4937             case SCSI_CONTROLLER_PAGE:
4938                 Page = ScsiControllerPage(&Ir);
4939                 break;
4940 
4941             case OEM_DRIVER_PAGE:
4942                 Page = OemDriverPage(&Ir);
4943                 break;
4944 #endif
4945 
4946             case DEVICE_SETTINGS_PAGE:
4947                 Page = DeviceSettingsPage(&Ir);
4948                 break;
4949 
4950             case COMPUTER_SETTINGS_PAGE:
4951                 Page = ComputerSettingsPage(&Ir);
4952                 break;
4953 
4954             case DISPLAY_SETTINGS_PAGE:
4955                 Page = DisplaySettingsPage(&Ir);
4956                 break;
4957 
4958             case KEYBOARD_SETTINGS_PAGE:
4959                 Page = KeyboardSettingsPage(&Ir);
4960                 break;
4961 
4962             case LAYOUT_SETTINGS_PAGE:
4963                 Page = LayoutSettingsPage(&Ir);
4964                 break;
4965 
4966             /* Partitioning pages */
4967             case SELECT_PARTITION_PAGE:
4968                 Page = SelectPartitionPage(&Ir);
4969                 break;
4970 
4971             case CREATE_PRIMARY_PARTITION_PAGE:
4972                 Page = CreatePrimaryPartitionPage(&Ir);
4973                 break;
4974 
4975             case CREATE_EXTENDED_PARTITION_PAGE:
4976                 Page = CreateExtendedPartitionPage(&Ir);
4977                 break;
4978 
4979             case CREATE_LOGICAL_PARTITION_PAGE:
4980                 Page = CreateLogicalPartitionPage(&Ir);
4981                 break;
4982 
4983             case CONFIRM_DELETE_SYSTEM_PARTITION_PAGE:
4984                 Page = ConfirmDeleteSystemPartitionPage(&Ir);
4985                 break;
4986 
4987             case DELETE_PARTITION_PAGE:
4988                 Page = DeletePartitionPage(&Ir);
4989                 break;
4990 
4991             /* Filesystem partition operations pages */
4992             case SELECT_FILE_SYSTEM_PAGE:
4993                 Page = SelectFileSystemPage(&Ir);
4994                 break;
4995 
4996             case FORMAT_PARTITION_PAGE:
4997                 Page = FormatPartitionPage(&Ir);
4998                 break;
4999 
5000             case CHECK_FILE_SYSTEM_PAGE:
5001                 Page = CheckFileSystemPage(&Ir);
5002                 break;
5003 
5004             /* Installation pages */
5005             case INSTALL_DIRECTORY_PAGE:
5006                 Page = InstallDirectoryPage(&Ir);
5007                 break;
5008 
5009             case PREPARE_COPY_PAGE:
5010                 Page = PrepareCopyPage(&Ir);
5011                 break;
5012 
5013             case FILE_COPY_PAGE:
5014                 Page = FileCopyPage(&Ir);
5015                 break;
5016 
5017             case REGISTRY_PAGE:
5018                 Page = RegistryPage(&Ir);
5019                 break;
5020 
5021             /* Bootloader installation pages */
5022             case BOOT_LOADER_PAGE:
5023                 Page = BootLoaderPage(&Ir);
5024                 break;
5025 
5026             case BOOT_LOADER_FLOPPY_PAGE:
5027                 Page = BootLoaderFloppyPage(&Ir);
5028                 break;
5029 
5030             case BOOT_LOADER_HARDDISK_MBR_PAGE:
5031                 Page = BootLoaderHarddiskMbrPage(&Ir);
5032                 break;
5033 
5034             case BOOT_LOADER_HARDDISK_VBR_PAGE:
5035                 Page = BootLoaderHarddiskVbrPage(&Ir);
5036                 break;
5037 
5038             /* Repair pages */
5039             case REPAIR_INTRO_PAGE:
5040                 Page = RepairIntroPage(&Ir);
5041                 break;
5042 
5043             case UPGRADE_REPAIR_PAGE:
5044                 Page = UpgradeRepairPage(&Ir);
5045                 break;
5046 
5047             case SUCCESS_PAGE:
5048                 Page = SuccessPage(&Ir);
5049                 break;
5050 
5051             case FLUSH_PAGE:
5052                 Page = FlushPage(&Ir);
5053                 break;
5054 
5055             case QUIT_PAGE:
5056                 Page = QuitPage(&Ir);
5057                 break;
5058 
5059             /* Virtual pages */
5060             case SETUP_INIT_PAGE:
5061             case REBOOT_PAGE:
5062             case RECOVERY_PAGE:
5063                 break;
5064 
5065             default:
5066                 break;
5067         }
5068     }
5069 
5070     /* Terminate the user-mode PnP manager */
5071     TerminateUserModePnpManager();
5072 
5073     /* Setup has finished */
5074     FinishSetup(&USetupData);
5075 
5076     if (Page == RECOVERY_PAGE)
5077         RecoveryConsole();
5078 
5079     FreeConsole();
5080 
5081     /* Reboot */
5082     RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, FALSE, &Old);
5083     NtShutdownSystem(ShutdownReboot);
5084     RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, Old, FALSE, &Old);
5085 
5086     return STATUS_SUCCESS;
5087 }
5088 
5089 
5090 VOID NTAPI
5091 NtProcessStartup(PPEB Peb)
5092 {
5093     NTSTATUS Status;
5094     LARGE_INTEGER Time;
5095 
5096     RtlNormalizeProcessParams(Peb->ProcessParameters);
5097 
5098     ProcessHeap = Peb->ProcessHeap;
5099 
5100     NtQuerySystemTime(&Time);
5101 
5102     Status = RunUSetup();
5103 
5104     if (NT_SUCCESS(Status))
5105     {
5106         /*
5107          * Avoid a bugcheck if RunUSetup() finishes too quickly by implementing
5108          * a protective waiting.
5109          * This wait is needed because, since we are started as SMSS.EXE,
5110          * the NT kernel explicitly waits 5 seconds for the initial process
5111          * SMSS.EXE to initialize (as a protective measure), and otherwise
5112          * bugchecks with the code SESSION5_INITIALIZATION_FAILED.
5113          */
5114         Time.QuadPart += 50000000;
5115         NtDelayExecution(FALSE, &Time);
5116     }
5117     else
5118     {
5119         /* The installer failed to start: raise a hard error (crash the system/BSOD) */
5120         Status = NtRaiseHardError(STATUS_SYSTEM_PROCESS_TERMINATED,
5121                                   0, 0, NULL, 0, NULL);
5122     }
5123 
5124     NtTerminateProcess(NtCurrentProcess(), Status);
5125 }
5126 
5127 /* EOF */
5128