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