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