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