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