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