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