xref: /reactos/ntoskrnl/inbv/inbv.c (revision 7eead935)
1 /* INCLUDES ******************************************************************/
2 
3 #include <ntoskrnl.h>
4 #define NDEBUG
5 #include <debug.h>
6 #include "bootvid/bootvid.h"
7 
8 #ifndef TAG_OSTR
9 #define TAG_OSTR    'RTSO'
10 #endif
11 
12 /* GLOBALS *******************************************************************/
13 
14 /*
15  * Enable this define if you want Inbv to use coloured headless mode.
16  */
17 // #define INBV_HEADLESS_COLORS
18 
19 /*
20  * ReactOS uses the same boot screen for all the products.
21  * Also it doesn't use a parallel system thread for the
22  * rotating "progress" bar.
23  */
24 
25 /*
26  * Enable this define when ReactOS will have different SKUs
27  * (Workstation, Server, Storage Server, Cluster Server, etc...).
28  */
29 // #define REACTOS_SKUS
30 
31 /*
32  * Enable this define when Inbv will support rotating progress bar.
33  */
34 #define INBV_ROTBAR_IMPLEMENTED
35 
36 static KSPIN_LOCK BootDriverLock;
37 static KIRQL InbvOldIrql;
38 static INBV_DISPLAY_STATE InbvDisplayState = INBV_DISPLAY_STATE_DISABLED;
39 BOOLEAN InbvBootDriverInstalled = FALSE;
40 static BOOLEAN InbvDisplayDebugStrings = FALSE;
41 static INBV_DISPLAY_STRING_FILTER InbvDisplayFilter = NULL;
42 static ULONG ProgressBarLeft = 0, ProgressBarTop = 0;
43 static BOOLEAN ShowProgressBar = FALSE;
44 static INBV_PROGRESS_STATE InbvProgressState;
45 static BT_PROGRESS_INDICATOR InbvProgressIndicator = {0, 25, 0};
46 static INBV_RESET_DISPLAY_PARAMETERS InbvResetDisplayParameters = NULL;
47 static ULONG ResourceCount = 0;
48 static PUCHAR ResourceList[1 + IDB_MAX_RESOURCE]; // First entry == NULL, followed by 'ResourceCount' entries.
49 
50 #ifdef INBV_ROTBAR_IMPLEMENTED
51 /*
52  * Change this to modify progress bar behaviour
53  */
54 #define ROT_BAR_DEFAULT_MODE    RB_PROGRESS_BAR
55 
56 /*
57  * Values for PltRotBarStatus:
58  * - PltRotBarStatus == 1, do palette fading-in (done elsewhere in ReactOS);
59  * - PltRotBarStatus == 2, do rotation bar animation;
60  * - PltRotBarStatus == 3, stop the animation thread.
61  * - Any other value is ignored and the animation thread continues to run.
62  */
63 typedef enum _ROT_BAR_STATUS
64 {
65     RBS_FADEIN = 1,
66     RBS_ANIMATE,
67     RBS_STOP_ANIMATE,
68     RBS_STATUS_MAX
69 } ROT_BAR_STATUS;
70 
71 static BOOLEAN RotBarThreadActive = FALSE;
72 static ROT_BAR_TYPE RotBarSelection = RB_UNSPECIFIED;
73 static ROT_BAR_STATUS PltRotBarStatus = 0;
74 static UCHAR RotBarBuffer[24 * 9];
75 static UCHAR RotLineBuffer[640 * 6];
76 #endif
77 
78 
79 /*
80  * Headless terminal text colors
81  */
82 
83 #ifdef INBV_HEADLESS_COLORS
84 
85 // Conversion table CGA to ANSI color index
86 static const UCHAR CGA_TO_ANSI_COLOR_TABLE[16] =
87 {
88     0,  // Black
89     4,  // Blue
90     2,  // Green
91     6,  // Cyan
92     1,  // Red
93     5,  // Magenta
94     3,  // Brown/Yellow
95     7,  // Grey/White
96 
97     60, // Bright Black
98     64, // Bright Blue
99     62, // Bright Green
100     66, // Bright Cyan
101     61, // Bright Red
102     65, // Bright Magenta
103     63, // Bright Yellow
104     67  // Bright Grey (White)
105 };
106 
107 #define CGA_TO_ANSI_COLOR(CgaColor) \
108     CGA_TO_ANSI_COLOR_TABLE[CgaColor & 0x0F]
109 
110 #endif
111 
112 // Default colors: text in white, background in black
113 static ULONG InbvTerminalTextColor = 37;
114 static ULONG InbvTerminalBkgdColor = 40;
115 
116 
117 /* FADING FUNCTION ***********************************************************/
118 
119 /** From include/psdk/wingdi.h **/
120 typedef struct tagRGBQUAD
121 {
122     UCHAR    rgbBlue;
123     UCHAR    rgbGreen;
124     UCHAR    rgbRed;
125     UCHAR    rgbReserved;
126 } RGBQUAD,*LPRGBQUAD;
127 /*******************************/
128 
129 static RGBQUAD MainPalette[16];
130 
131 #define PALETTE_FADE_STEPS  15
132 #define PALETTE_FADE_TIME   (20 * 1000) /* 20 ms */
133 
134 /** From bootvid/precomp.h **/
135 //
136 // Bitmap Header
137 //
138 typedef struct tagBITMAPINFOHEADER
139 {
140     ULONG biSize;
141     LONG biWidth;
142     LONG biHeight;
143     USHORT biPlanes;
144     USHORT biBitCount;
145     ULONG biCompression;
146     ULONG biSizeImage;
147     LONG biXPelsPerMeter;
148     LONG biYPelsPerMeter;
149     ULONG biClrUsed;
150     ULONG biClrImportant;
151 } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
152 /****************************/
153 
154 //
155 // Needed prototypes
156 //
157 VOID NTAPI InbvAcquireLock(VOID);
158 VOID NTAPI InbvReleaseLock(VOID);
159 
160 static VOID
161 BootLogoFadeIn(VOID)
162 {
163     UCHAR PaletteBitmapBuffer[sizeof(BITMAPINFOHEADER) + sizeof(MainPalette)];
164     PBITMAPINFOHEADER PaletteBitmap = (PBITMAPINFOHEADER)PaletteBitmapBuffer;
165     LPRGBQUAD Palette = (LPRGBQUAD)(PaletteBitmapBuffer + sizeof(BITMAPINFOHEADER));
166     ULONG Iteration, Index, ClrUsed;
167 
168     LARGE_INTEGER Delay;
169     Delay.QuadPart = - (PALETTE_FADE_TIME * 10);
170 
171     /* Check if we are installed and we own the display */
172     if (!InbvBootDriverInstalled ||
173         (InbvDisplayState != INBV_DISPLAY_STATE_OWNED))
174     {
175         return;
176     }
177 
178     /*
179      * Build a bitmap containing the fade-in palette. The palette entries
180      * are then processed in a loop and set using VidBitBlt function.
181      */
182     ClrUsed = RTL_NUMBER_OF(MainPalette);
183     RtlZeroMemory(PaletteBitmap, sizeof(BITMAPINFOHEADER));
184     PaletteBitmap->biSize = sizeof(BITMAPINFOHEADER);
185     PaletteBitmap->biBitCount = 4;
186     PaletteBitmap->biClrUsed = ClrUsed;
187 
188     /*
189      * Main animation loop.
190      */
191     for (Iteration = 0; Iteration <= PALETTE_FADE_STEPS; ++Iteration)
192     {
193         for (Index = 0; Index < ClrUsed; Index++)
194         {
195             Palette[Index].rgbRed = (UCHAR)
196                 (MainPalette[Index].rgbRed * Iteration / PALETTE_FADE_STEPS);
197             Palette[Index].rgbGreen = (UCHAR)
198                 (MainPalette[Index].rgbGreen * Iteration / PALETTE_FADE_STEPS);
199             Palette[Index].rgbBlue = (UCHAR)
200                 (MainPalette[Index].rgbBlue * Iteration / PALETTE_FADE_STEPS);
201         }
202 
203         /* Do the animation */
204         InbvAcquireLock();
205         VidBitBlt(PaletteBitmapBuffer, 0, 0);
206         InbvReleaseLock();
207 
208         /* Wait for a bit */
209         KeDelayExecutionThread(KernelMode, FALSE, &Delay);
210     }
211 }
212 
213 /* FUNCTIONS *****************************************************************/
214 
215 INIT_FUNCTION
216 PVOID
217 NTAPI
218 FindBitmapResource(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
219                    IN ULONG ResourceId)
220 {
221     UNICODE_STRING UpString = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
222     UNICODE_STRING MpString = RTL_CONSTANT_STRING(L"ntkrnlmp.exe");
223     PLIST_ENTRY NextEntry, ListHead;
224     PLDR_DATA_TABLE_ENTRY LdrEntry;
225     PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry;
226     LDR_RESOURCE_INFO ResourceInfo;
227     NTSTATUS Status;
228     PVOID Data = NULL;
229 
230     /* Loop the driver list */
231     ListHead = &LoaderBlock->LoadOrderListHead;
232     NextEntry = ListHead->Flink;
233     while (NextEntry != ListHead)
234     {
235         /* Get the entry */
236         LdrEntry = CONTAINING_RECORD(NextEntry,
237                                      LDR_DATA_TABLE_ENTRY,
238                                      InLoadOrderLinks);
239 
240         /* Check for a match */
241         if (RtlEqualUnicodeString(&LdrEntry->BaseDllName, &UpString, TRUE) ||
242             RtlEqualUnicodeString(&LdrEntry->BaseDllName, &MpString, TRUE))
243         {
244             /* Break out */
245             break;
246         }
247     }
248 
249     /* Check if we found it */
250     if (NextEntry != ListHead)
251     {
252         /* Try to find the resource */
253         ResourceInfo.Type = 2; // RT_BITMAP;
254         ResourceInfo.Name = ResourceId;
255         ResourceInfo.Language = 0;
256         Status = LdrFindResource_U(LdrEntry->DllBase,
257                                    &ResourceInfo,
258                                    RESOURCE_DATA_LEVEL,
259                                    &ResourceDataEntry);
260         if (NT_SUCCESS(Status))
261         {
262             /* Access the resource */
263             ULONG Size = 0;
264             Status = LdrAccessResource(LdrEntry->DllBase,
265                                        ResourceDataEntry,
266                                        &Data,
267                                        &Size);
268             if ((Data) && (ResourceId < 3))
269             {
270                 KiBugCheckData[4] ^= RtlComputeCrc32(0, Data, Size);
271             }
272             if (!NT_SUCCESS(Status)) Data = NULL;
273         }
274     }
275 
276     /* Return the pointer */
277     return Data;
278 }
279 
280 INIT_FUNCTION
281 BOOLEAN
282 NTAPI
283 InbvDriverInitialize(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
284                      IN ULONG Count)
285 {
286     PCHAR CommandLine;
287     BOOLEAN ResetMode = FALSE; // By default do not reset the video mode
288     ULONG i;
289 
290     /* Quit if we're already installed */
291     if (InbvBootDriverInstalled) return TRUE;
292 
293     /* Initialize the lock and check the current display state */
294     KeInitializeSpinLock(&BootDriverLock);
295     if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
296     {
297         /* Reset the video mode in case we do not have a custom boot logo */
298         CommandLine = (LoaderBlock->LoadOptions ? _strupr(LoaderBlock->LoadOptions) : NULL);
299         ResetMode   = (CommandLine == NULL) || (strstr(CommandLine, "BOOTLOGO") == NULL);
300     }
301 
302     /* Initialize the video */
303     InbvBootDriverInstalled = VidInitialize(ResetMode);
304     if (InbvBootDriverInstalled)
305     {
306         /* Find bitmap resources in the kernel */
307         ResourceCount = min(Count, RTL_NUMBER_OF(ResourceList) - 1);
308         for (i = 1; i <= ResourceCount; i++)
309         {
310             /* Do the lookup */
311             ResourceList[i] = FindBitmapResource(LoaderBlock, i);
312         }
313 
314         /* Set the progress bar ranges */
315         InbvSetProgressBarSubset(0, 100);
316     }
317 
318     /* Return install state */
319     return InbvBootDriverInstalled;
320 }
321 
322 VOID
323 NTAPI
324 InbvAcquireLock(VOID)
325 {
326     KIRQL OldIrql;
327 
328     /* Check if we're at dispatch level or lower */
329     OldIrql = KeGetCurrentIrql();
330     if (OldIrql <= DISPATCH_LEVEL)
331     {
332         /* Loop until the lock is free */
333         while (!KeTestSpinLock(&BootDriverLock));
334 
335         /* Raise IRQL to dispatch level */
336         KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
337     }
338 
339     /* Acquire the lock */
340     KiAcquireSpinLock(&BootDriverLock);
341     InbvOldIrql = OldIrql;
342 }
343 
344 VOID
345 NTAPI
346 InbvReleaseLock(VOID)
347 {
348     KIRQL OldIrql;
349 
350     /* Capture the old IRQL */
351     OldIrql = InbvOldIrql;
352 
353     /* Release the driver lock */
354     KiReleaseSpinLock(&BootDriverLock);
355 
356     /* If we were at dispatch level or lower, restore the old IRQL */
357     if (InbvOldIrql <= DISPATCH_LEVEL) KeLowerIrql(OldIrql);
358 }
359 
360 INIT_FUNCTION
361 VOID
362 NTAPI
363 InbvEnableBootDriver(IN BOOLEAN Enable)
364 {
365     /* Check if we're installed */
366     if (InbvBootDriverInstalled)
367     {
368         /* Check for lost state */
369         if (InbvDisplayState >= INBV_DISPLAY_STATE_LOST) return;
370 
371         /* Acquire the lock */
372         InbvAcquireLock();
373 
374         /* Cleanup the screen if we own it */
375         if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED) VidCleanUp();
376 
377         /* Set the new display state */
378         InbvDisplayState = Enable ? INBV_DISPLAY_STATE_OWNED :
379                                     INBV_DISPLAY_STATE_DISABLED;
380 
381         /* Release the lock */
382         InbvReleaseLock();
383     }
384     else
385     {
386         /* Set the new display state */
387         InbvDisplayState = Enable ? INBV_DISPLAY_STATE_OWNED :
388                                     INBV_DISPLAY_STATE_DISABLED;
389     }
390 }
391 
392 VOID
393 NTAPI
394 InbvAcquireDisplayOwnership(VOID)
395 {
396     /* Check if we have a callback and we're just acquiring it now */
397     if ((InbvResetDisplayParameters) &&
398         (InbvDisplayState == INBV_DISPLAY_STATE_LOST))
399     {
400         /* Call the callback */
401         InbvResetDisplayParameters(80, 50);
402     }
403 
404     /* Acquire the display */
405     InbvDisplayState = INBV_DISPLAY_STATE_OWNED;
406 }
407 
408 VOID
409 NTAPI
410 InbvSetDisplayOwnership(IN BOOLEAN DisplayOwned)
411 {
412     /* Set the new display state */
413     InbvDisplayState = DisplayOwned ? INBV_DISPLAY_STATE_OWNED :
414                                       INBV_DISPLAY_STATE_LOST;
415 }
416 
417 BOOLEAN
418 NTAPI
419 InbvCheckDisplayOwnership(VOID)
420 {
421     /* Return if we own it or not */
422     return InbvDisplayState != INBV_DISPLAY_STATE_LOST;
423 }
424 
425 INBV_DISPLAY_STATE
426 NTAPI
427 InbvGetDisplayState(VOID)
428 {
429     /* Return the actual state */
430     return InbvDisplayState;
431 }
432 
433 BOOLEAN
434 NTAPI
435 InbvDisplayString(IN PCHAR String)
436 {
437     /* Make sure we own the display */
438     if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
439     {
440         /* If we're not allowed, return success anyway */
441         if (!InbvDisplayDebugStrings) return TRUE;
442 
443         /* Check if a filter is installed */
444         if (InbvDisplayFilter) InbvDisplayFilter(&String);
445 
446         /* Acquire the lock */
447         InbvAcquireLock();
448 
449         /* Make sure we're installed and display the string */
450         if (InbvBootDriverInstalled) VidDisplayString((PUCHAR)String);
451 
452         /* Print the string on the EMS port */
453         HeadlessDispatch(HeadlessCmdPutString,
454                          String,
455                          strlen(String) + sizeof(ANSI_NULL),
456                          NULL,
457                          NULL);
458 
459         /* Release the lock */
460         InbvReleaseLock();
461 
462         /* All done */
463         return TRUE;
464     }
465 
466     /* We don't own it, fail */
467     return FALSE;
468 }
469 
470 BOOLEAN
471 NTAPI
472 InbvEnableDisplayString(IN BOOLEAN Enable)
473 {
474     BOOLEAN OldSetting;
475 
476     /* Get the old setting */
477     OldSetting = InbvDisplayDebugStrings;
478 
479     /* Update it */
480     InbvDisplayDebugStrings = Enable;
481 
482     /* Return the old setting */
483     return OldSetting;
484 }
485 
486 VOID
487 NTAPI
488 InbvInstallDisplayStringFilter(IN INBV_DISPLAY_STRING_FILTER Filter)
489 {
490     /* Save the filter */
491     InbvDisplayFilter = Filter;
492 }
493 
494 BOOLEAN
495 NTAPI
496 InbvIsBootDriverInstalled(VOID)
497 {
498     /* Return driver state */
499     return InbvBootDriverInstalled;
500 }
501 
502 VOID
503 NTAPI
504 InbvNotifyDisplayOwnershipLost(IN INBV_RESET_DISPLAY_PARAMETERS Callback)
505 {
506     /* Check if we're installed */
507     if (InbvBootDriverInstalled)
508     {
509         /* Acquire the lock and cleanup if we own the screen */
510         InbvAcquireLock();
511         if (InbvDisplayState != INBV_DISPLAY_STATE_LOST) VidCleanUp();
512 
513         /* Set the reset callback and display state */
514         InbvResetDisplayParameters = Callback;
515         InbvDisplayState = INBV_DISPLAY_STATE_LOST;
516 
517         /* Release the lock */
518         InbvReleaseLock();
519     }
520     else
521     {
522         /* Set the reset callback and display state */
523         InbvResetDisplayParameters = Callback;
524         InbvDisplayState = INBV_DISPLAY_STATE_LOST;
525     }
526 }
527 
528 BOOLEAN
529 NTAPI
530 InbvResetDisplay(VOID)
531 {
532     /* Check if we're installed and we own it */
533     if (InbvBootDriverInstalled &&
534         (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
535     {
536         /* Do the reset */
537         VidResetDisplay(TRUE);
538         return TRUE;
539     }
540 
541     /* Nothing to reset */
542     return FALSE;
543 }
544 
545 VOID
546 NTAPI
547 InbvSetScrollRegion(IN ULONG Left,
548                     IN ULONG Top,
549                     IN ULONG Right,
550                     IN ULONG Bottom)
551 {
552     /* Just call bootvid */
553     VidSetScrollRegion(Left, Top, Right, Bottom);
554 }
555 
556 VOID
557 NTAPI
558 InbvSetTextColor(IN ULONG Color)
559 {
560     HEADLESS_CMD_SET_COLOR HeadlessSetColor;
561 
562     /* Set color for EMS port */
563 #ifdef INBV_HEADLESS_COLORS
564     InbvTerminalTextColor = 30 + CGA_TO_ANSI_COLOR(Color);
565 #else
566     InbvTerminalTextColor = 37;
567 #endif
568     HeadlessSetColor.TextColor = InbvTerminalTextColor;
569     HeadlessSetColor.BkgdColor = InbvTerminalBkgdColor;
570     HeadlessDispatch(HeadlessCmdSetColor,
571                      &HeadlessSetColor,
572                      sizeof(HeadlessSetColor),
573                      NULL,
574                      NULL);
575 
576     /* Update the text color */
577     VidSetTextColor(Color);
578 }
579 
580 VOID
581 NTAPI
582 InbvSolidColorFill(IN ULONG Left,
583                    IN ULONG Top,
584                    IN ULONG Right,
585                    IN ULONG Bottom,
586                    IN ULONG Color)
587 {
588     HEADLESS_CMD_SET_COLOR HeadlessSetColor;
589 
590     /* Make sure we own it */
591     if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
592     {
593         /* Acquire the lock */
594         InbvAcquireLock();
595 
596         /* Check if we're installed */
597         if (InbvBootDriverInstalled)
598         {
599             /* Call bootvid */
600             VidSolidColorFill(Left, Top, Right, Bottom, (UCHAR)Color);
601         }
602 
603         /* Set color for EMS port and clear display */
604 #ifdef INBV_HEADLESS_COLORS
605         InbvTerminalBkgdColor = 40 + CGA_TO_ANSI_COLOR(Color);
606 #else
607         InbvTerminalBkgdColor = 40;
608 #endif
609         HeadlessSetColor.TextColor = InbvTerminalTextColor;
610         HeadlessSetColor.BkgdColor = InbvTerminalBkgdColor;
611         HeadlessDispatch(HeadlessCmdSetColor,
612                          &HeadlessSetColor,
613                          sizeof(HeadlessSetColor),
614                          NULL,
615                          NULL);
616         HeadlessDispatch(HeadlessCmdClearDisplay,
617                          NULL, 0,
618                          NULL, NULL);
619 
620         /* Release the lock */
621         InbvReleaseLock();
622     }
623 }
624 
625 INIT_FUNCTION
626 VOID
627 NTAPI
628 InbvUpdateProgressBar(IN ULONG Progress)
629 {
630     ULONG FillCount, BoundedProgress;
631 
632     /* Make sure the progress bar is enabled, that we own and are installed */
633     if (ShowProgressBar &&
634         InbvBootDriverInstalled &&
635         (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
636     {
637         /* Compute fill count */
638         BoundedProgress = (InbvProgressState.Floor / 100) + Progress;
639         FillCount = 121 * (InbvProgressState.Bias * BoundedProgress) / 1000000;
640 
641         /* Acquire the lock */
642         InbvAcquireLock();
643 
644         /* Fill the progress bar */
645         VidSolidColorFill(ProgressBarLeft,
646                           ProgressBarTop,
647                           ProgressBarLeft + FillCount,
648                           ProgressBarTop + 12,
649                           15);
650 
651         /* Release the lock */
652         InbvReleaseLock();
653     }
654 }
655 
656 VOID
657 NTAPI
658 InbvBufferToScreenBlt(IN PUCHAR Buffer,
659                       IN ULONG X,
660                       IN ULONG Y,
661                       IN ULONG Width,
662                       IN ULONG Height,
663                       IN ULONG Delta)
664 {
665     /* Check if we're installed and we own it */
666     if (InbvBootDriverInstalled &&
667         (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
668     {
669         /* Do the blit */
670         VidBufferToScreenBlt(Buffer, X, Y, Width, Height, Delta);
671     }
672 }
673 
674 VOID
675 NTAPI
676 InbvBitBlt(IN PUCHAR Buffer,
677            IN ULONG X,
678            IN ULONG Y)
679 {
680     /* Check if we're installed and we own it */
681     if (InbvBootDriverInstalled &&
682         (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
683     {
684         /* Acquire the lock */
685         InbvAcquireLock();
686 
687         /* Do the blit */
688         VidBitBlt(Buffer, X, Y);
689 
690         /* Release the lock */
691         InbvReleaseLock();
692     }
693 }
694 
695 VOID
696 NTAPI
697 InbvScreenToBufferBlt(IN PUCHAR Buffer,
698                       IN ULONG X,
699                       IN ULONG Y,
700                       IN ULONG Width,
701                       IN ULONG Height,
702                       IN ULONG Delta)
703 {
704     /* Check if we're installed and we own it */
705     if (InbvBootDriverInstalled &&
706         (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
707     {
708         /* Do the blit */
709         VidScreenToBufferBlt(Buffer, X, Y, Width, Height, Delta);
710     }
711 }
712 
713 VOID
714 NTAPI
715 InbvSetProgressBarCoordinates(IN ULONG Left,
716                               IN ULONG Top)
717 {
718     /* Update the coordinates */
719     ProgressBarLeft = Left;
720     ProgressBarTop  = Top;
721 
722     /* Enable the progress bar */
723     ShowProgressBar = TRUE;
724 }
725 
726 VOID
727 NTAPI
728 InbvSetProgressBarSubset(IN ULONG Floor,
729                          IN ULONG Ceiling)
730 {
731     /* Sanity checks */
732     ASSERT(Floor < Ceiling);
733     ASSERT(Ceiling <= 100);
734 
735     /* Update the progress bar state */
736     InbvProgressState.Floor = Floor * 100;
737     InbvProgressState.Ceiling = Ceiling * 100;
738     InbvProgressState.Bias = (Ceiling * 100) - Floor;
739 }
740 
741 INIT_FUNCTION
742 VOID
743 NTAPI
744 InbvIndicateProgress(VOID)
745 {
746     ULONG Percentage;
747 
748     /* Increase progress */
749     InbvProgressIndicator.Count++;
750 
751     /* Compute new percentage */
752     Percentage = min(100 * InbvProgressIndicator.Count /
753                            InbvProgressIndicator.Expected,
754                      99);
755     if (Percentage != InbvProgressIndicator.Percentage)
756     {
757         /* Percentage has moved, update the progress bar */
758         InbvProgressIndicator.Percentage = Percentage;
759         InbvUpdateProgressBar(Percentage);
760     }
761 }
762 
763 PUCHAR
764 NTAPI
765 InbvGetResourceAddress(IN ULONG ResourceNumber)
766 {
767     /* Validate the resource number */
768     if (ResourceNumber > ResourceCount) return NULL;
769 
770     /* Return the address */
771     return ResourceList[ResourceNumber];
772 }
773 
774 NTSTATUS
775 NTAPI
776 NtDisplayString(IN PUNICODE_STRING DisplayString)
777 {
778     NTSTATUS Status;
779     UNICODE_STRING CapturedString;
780     OEM_STRING OemString;
781     ULONG OemLength;
782     KPROCESSOR_MODE PreviousMode;
783 
784     PAGED_CODE();
785 
786     PreviousMode = ExGetPreviousMode();
787 
788     /* We require the TCB privilege */
789     if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
790         return STATUS_PRIVILEGE_NOT_HELD;
791 
792     /* Capture the string */
793     Status = ProbeAndCaptureUnicodeString(&CapturedString, PreviousMode, DisplayString);
794     if (!NT_SUCCESS(Status))
795         return Status;
796 
797     /* Do not display the string if it is empty */
798     if (CapturedString.Length == 0 || CapturedString.Buffer == NULL)
799     {
800         Status = STATUS_SUCCESS;
801         goto Quit;
802     }
803 
804     /*
805      * Convert the string since INBV understands only ANSI/OEM. Allocate the
806      * string buffer in non-paged pool because INBV passes it down to BOOTVID.
807      * We cannot perform the allocation using RtlUnicodeStringToOemString()
808      * since its allocator uses PagedPool.
809      */
810     OemLength = RtlUnicodeStringToOemSize(&CapturedString);
811     if (OemLength > MAXUSHORT)
812     {
813         Status = STATUS_BUFFER_OVERFLOW;
814         goto Quit;
815     }
816     RtlInitEmptyAnsiString((PANSI_STRING)&OemString, NULL, (USHORT)OemLength);
817     OemString.Buffer = ExAllocatePoolWithTag(NonPagedPool, OemLength, TAG_OSTR);
818     if (OemString.Buffer == NULL)
819     {
820         Status = STATUS_NO_MEMORY;
821         goto Quit;
822     }
823     RtlUnicodeStringToOemString(&OemString, &CapturedString, FALSE);
824 
825     /* Display the string */
826     InbvDisplayString(OemString.Buffer);
827 
828     /* Free the string buffer */
829     ExFreePoolWithTag(OemString.Buffer, TAG_OSTR);
830 
831     Status = STATUS_SUCCESS;
832 
833 Quit:
834     /* Free the captured string */
835     ReleaseCapturedUnicodeString(&CapturedString, PreviousMode);
836 
837     return Status;
838 }
839 
840 #ifdef INBV_ROTBAR_IMPLEMENTED
841 static
842 VOID
843 NTAPI
844 InbvRotationThread(
845     _In_ PVOID Context)
846 {
847     ULONG X, Y, Index, Total;
848     LARGE_INTEGER Delay = {{0}};
849 
850     InbvAcquireLock();
851     if (RotBarSelection == RB_SQUARE_CELLS)
852     {
853         Index = 0;
854     }
855     else
856     {
857         Index = 32;
858     }
859     X = ProgressBarLeft + 2;
860     Y = ProgressBarTop + 2;
861     InbvReleaseLock();
862 
863     while (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
864     {
865         /* Wait for a bit */
866         KeDelayExecutionThread(KernelMode, FALSE, &Delay);
867 
868         InbvAcquireLock();
869 
870         /* Unknown unexpected command */
871         ASSERT(PltRotBarStatus < RBS_STATUS_MAX);
872 
873         if (PltRotBarStatus == RBS_STOP_ANIMATE)
874         {
875             /* Stop the thread */
876             InbvReleaseLock();
877             break;
878         }
879 
880         if (RotBarSelection == RB_SQUARE_CELLS)
881         {
882             Delay.QuadPart = -800000; // 80 ms
883             Total = 18;
884             Index %= Total;
885 
886             if (Index >= 3)
887             {
888                 /* Fill previous bar position */
889                 VidSolidColorFill(X + ((Index - 3) * 8), Y, (X + ((Index - 3) * 8)) + 8 - 1, Y + 9 - 1, 0);
890             }
891             if (Index < Total - 1)
892             {
893                 /* Draw the progress bar bit */
894                 if (Index < 2)
895                 {
896                     /* Appearing from the left */
897                     VidBufferToScreenBlt(RotBarBuffer + 8 * (2 - Index) / 2, X, Y, 22 - 8 * (2 - Index), 9, 24);
898                 }
899                 else if (Index >= Total - 3)
900                 {
901                     /* Hiding to the right */
902                     VidBufferToScreenBlt(RotBarBuffer, X + ((Index - 2) * 8), Y, 22 - 8 * (4 - (Total - Index)), 9, 24);
903                 }
904                 else
905                 {
906                     VidBufferToScreenBlt(RotBarBuffer, X + ((Index - 2) * 8), Y, 22, 9, 24);
907                 }
908             }
909             Index++;
910         }
911         else if (RotBarSelection == RB_PROGRESS_BAR)
912         {
913             Delay.QuadPart = -600000; // 60 ms
914             Total = 640;
915             Index %= Total;
916 
917             /* Right part */
918             VidBufferToScreenBlt(RotLineBuffer, Index, 474, 640 - Index, 6, 640);
919             if (Index > 0)
920             {
921                 /* Left part */
922                 VidBufferToScreenBlt(RotLineBuffer + (640 - Index) / 2, 0, 474, Index - 2, 6, 640);
923             }
924             Index += 32;
925         }
926 
927         InbvReleaseLock();
928     }
929 
930     PsTerminateSystemThread(STATUS_SUCCESS);
931 }
932 
933 INIT_FUNCTION
934 VOID
935 NTAPI
936 InbvRotBarInit(VOID)
937 {
938     PltRotBarStatus = RBS_FADEIN;
939     /* Perform other initialization if needed */
940 }
941 #endif
942 
943 INIT_FUNCTION
944 VOID
945 NTAPI
946 DisplayBootBitmap(IN BOOLEAN TextMode)
947 {
948     PVOID Header = NULL, Footer = NULL, Screen = NULL;
949 
950 #ifdef INBV_ROTBAR_IMPLEMENTED
951     UCHAR Buffer[24 * 9];
952     PVOID Bar = NULL, LineBmp = NULL;
953     ROT_BAR_TYPE TempRotBarSelection = RB_UNSPECIFIED;
954     NTSTATUS Status;
955     HANDLE ThreadHandle = NULL;
956 #endif
957 
958 #ifdef REACTOS_SKUS
959     PVOID Text = NULL;
960 #endif
961 
962 #ifdef INBV_ROTBAR_IMPLEMENTED
963     /* Check if the animation thread has already been created */
964     if (RotBarThreadActive)
965     {
966         /* Yes, just reset the progress bar but keep the thread alive */
967         InbvAcquireLock();
968         RotBarSelection = RB_UNSPECIFIED;
969         InbvReleaseLock();
970     }
971 #endif
972 
973     ShowProgressBar = FALSE;
974 
975     /* Check if this is text mode */
976     if (TextMode)
977     {
978         /* Check the type of the OS: workstation or server */
979         if (SharedUserData->NtProductType == NtProductWinNt)
980         {
981             /* Workstation; set colors */
982             InbvSetTextColor(15);
983             InbvSolidColorFill(0, 0, 639, 479, 7);
984             InbvSolidColorFill(0, 421, 639, 479, 1);
985 
986             /* Get resources */
987             Header = InbvGetResourceAddress(IDB_WKSTA_HEADER);
988             Footer = InbvGetResourceAddress(IDB_WKSTA_FOOTER);
989         }
990         else
991         {
992             /* Server; set colors */
993             InbvSetTextColor(14);
994             InbvSolidColorFill(0, 0, 639, 479, 6);
995             InbvSolidColorFill(0, 421, 639, 479, 1);
996 
997             /* Get resources */
998             Header = InbvGetResourceAddress(IDB_SERVER_HEADER);
999             Footer = InbvGetResourceAddress(IDB_SERVER_FOOTER);
1000         }
1001 
1002         /* Set the scrolling region */
1003         InbvSetScrollRegion(32, 80, 631, 400);
1004 
1005         /* Make sure we have resources */
1006         if (Header && Footer)
1007         {
1008             /* BitBlt them on the screen */
1009             InbvBitBlt(Footer, 0, 419);
1010             InbvBitBlt(Header, 0, 0);
1011         }
1012     }
1013     else
1014     {
1015         /* Is the boot driver installed? */
1016         if (!InbvBootDriverInstalled) return;
1017 
1018         /* Load the standard boot screen */
1019         Screen = InbvGetResourceAddress(IDB_BOOT_SCREEN);
1020 
1021 #ifdef REACTOS_SKUS
1022         Text = NULL;
1023         if (SharedUserData->NtProductType == NtProductWinNt)
1024         {
1025 #ifdef INBV_ROTBAR_IMPLEMENTED
1026             /* Workstation product, use appropriate status bar color */
1027             Bar = InbvGetResourceAddress(IDB_BAR_WKSTA);
1028 #endif
1029         }
1030         else
1031         {
1032             /* Display correct branding based on server suite */
1033             if (ExVerifySuite(StorageServer))
1034             {
1035                 /* Storage Server Edition */
1036                 Text = InbvGetResourceAddress(IDB_STORAGE_SERVER);
1037             }
1038             else if (ExVerifySuite(ComputeServer))
1039             {
1040                 /* Compute Cluster Edition */
1041                 Text = InbvGetResourceAddress(IDB_CLUSTER_SERVER);
1042             }
1043             else
1044             {
1045                 /* Normal edition */
1046                 Text = InbvGetResourceAddress(IDB_SERVER_LOGO);
1047             }
1048 
1049 #ifdef INBV_ROTBAR_IMPLEMENTED
1050             /* Server product, use appropriate status bar color */
1051             Bar = InbvGetResourceAddress(IDB_BAR_SERVER);
1052 #endif
1053         }
1054 #else
1055         /* Use default status bar */
1056         Bar = InbvGetResourceAddress(IDB_BAR_WKSTA);
1057 #endif
1058 
1059         /* Make sure we have a logo */
1060         if (Screen)
1061         {
1062             PBITMAPINFOHEADER BitmapInfoHeader;
1063             LPRGBQUAD Palette;
1064 
1065             /*
1066              * Save the main image palette and replace it with black palette,
1067              * so that we can do fade-in effect later.
1068              */
1069             BitmapInfoHeader = (PBITMAPINFOHEADER)Screen;
1070             Palette = (LPRGBQUAD)((PUCHAR)Screen + BitmapInfoHeader->biSize);
1071             RtlCopyMemory(MainPalette, Palette, sizeof(MainPalette));
1072             RtlZeroMemory(Palette, sizeof(MainPalette));
1073 
1074             /* Blit the background */
1075             InbvBitBlt(Screen, 0, 0);
1076 
1077 #ifdef INBV_ROTBAR_IMPLEMENTED
1078             /* Choose progress bar */
1079             TempRotBarSelection = ROT_BAR_DEFAULT_MODE;
1080 #endif
1081 
1082             /* Set progress bar coordinates and display it */
1083             InbvSetProgressBarCoordinates(259, 352);
1084 
1085 #ifdef REACTOS_SKUS
1086             /* Check for non-workstation products */
1087             if (SharedUserData->NtProductType != NtProductWinNt)
1088             {
1089                 /* Overwrite part of the logo for a server product */
1090                 InbvScreenToBufferBlt(Buffer, 413, 237, 7, 7, 8);
1091                 InbvSolidColorFill(418, 230, 454, 256, 0);
1092                 InbvBufferToScreenBlt(Buffer, 413, 237, 7, 7, 8);
1093 
1094                 /* In setup mode, you haven't selected a SKU yet */
1095                 if (ExpInTextModeSetup) Text = NULL;
1096             }
1097 #endif
1098         }
1099 
1100 #ifdef REACTOS_SKUS
1101         /* Draw the SKU text if it exits */
1102         if (Text) InbvBitBlt(Text, 180, 121);
1103 #endif
1104 
1105 #ifdef INBV_ROTBAR_IMPLEMENTED
1106         if (Bar)
1107         {
1108             /* Save previous screen pixels to buffer */
1109             InbvScreenToBufferBlt(Buffer, 0, 0, 22, 9, 24);
1110             /* Draw the progress bar bit */
1111             InbvBitBlt(Bar, 0, 0);
1112             /* Store it in global buffer */
1113             InbvScreenToBufferBlt(RotBarBuffer, 0, 0, 22, 9, 24);
1114             /* Restore screen pixels */
1115             InbvBufferToScreenBlt(Buffer, 0, 0, 22, 9, 24);
1116         }
1117 
1118         /*
1119          * Add a rotating bottom horizontal bar when using a progress bar,
1120          * to show that ReactOS can be still alive when the bar does not
1121          * appear to progress.
1122          */
1123         if (TempRotBarSelection == RB_PROGRESS_BAR)
1124         {
1125             LineBmp = InbvGetResourceAddress(IDB_ROTATING_LINE);
1126             if (LineBmp)
1127             {
1128                 /* Draw the line and store it in global buffer */
1129                 InbvBitBlt(LineBmp, 0, 474);
1130                 InbvScreenToBufferBlt(RotLineBuffer, 0, 474, 640, 6, 640);
1131             }
1132         }
1133         else
1134         {
1135             /* Hide the simple progress bar if not used */
1136             ShowProgressBar = FALSE;
1137         }
1138 #endif
1139 
1140         /* Display the boot logo and fade it in */
1141         BootLogoFadeIn();
1142 
1143 #ifdef INBV_ROTBAR_IMPLEMENTED
1144         if (!RotBarThreadActive && TempRotBarSelection != RB_UNSPECIFIED)
1145         {
1146             /* Start the animation thread */
1147             Status = PsCreateSystemThread(&ThreadHandle,
1148                                           0,
1149                                           NULL,
1150                                           NULL,
1151                                           NULL,
1152                                           InbvRotationThread,
1153                                           NULL);
1154             if (NT_SUCCESS(Status))
1155             {
1156                 /* The thread has started, close the handle as we don't need it */
1157                 RotBarThreadActive = TRUE;
1158                 ObCloseHandle(ThreadHandle, KernelMode);
1159             }
1160         }
1161 #endif
1162 
1163         /* Set filter which will draw text display if needed */
1164         InbvInstallDisplayStringFilter(DisplayFilter);
1165     }
1166 
1167 #ifdef INBV_ROTBAR_IMPLEMENTED
1168     /* Do we have the animation thread? */
1169     if (RotBarThreadActive)
1170     {
1171         /* We do, initialize the progress bar */
1172         InbvAcquireLock();
1173         RotBarSelection = TempRotBarSelection;
1174         InbvRotBarInit();
1175         InbvReleaseLock();
1176 
1177         // FIXME: This was added to allow animation start before the processor hangs
1178         if (TempRotBarSelection != RB_UNSPECIFIED)
1179         {
1180             LARGE_INTEGER Delay;
1181             Delay.QuadPart = -3000000; // 300 ms
1182             KeDelayExecutionThread(KernelMode, FALSE, &Delay);
1183         }
1184     }
1185 #endif
1186 }
1187 
1188 INIT_FUNCTION
1189 VOID
1190 NTAPI
1191 DisplayFilter(PCHAR *String)
1192 {
1193     /* Windows hack to skip first dots */
1194     static BOOLEAN DotHack = TRUE;
1195 
1196     /* If "." is given set *String to empty string */
1197     if(DotHack && strcmp(*String, ".") == 0)
1198         *String = "";
1199 
1200     if(**String)
1201     {
1202         /* Remove the filter */
1203         InbvInstallDisplayStringFilter(NULL);
1204 
1205         DotHack = FALSE;
1206 
1207         /* Draw text screen */
1208         DisplayBootBitmap(TRUE);
1209     }
1210 }
1211 
1212 INIT_FUNCTION
1213 VOID
1214 NTAPI
1215 FinalizeBootLogo(VOID)
1216 {
1217     /* Acquire lock and check the display state */
1218     InbvAcquireLock();
1219     if (InbvGetDisplayState() == INBV_DISPLAY_STATE_OWNED)
1220     {
1221         /* Clear the screen */
1222         VidSolidColorFill(0, 0, 639, 479, 0);
1223     }
1224 
1225     /* Reset progress bar and lock */
1226 #ifdef INBV_ROTBAR_IMPLEMENTED
1227     PltRotBarStatus = RBS_STOP_ANIMATE;
1228     RotBarThreadActive = FALSE;
1229 #endif
1230     InbvReleaseLock();
1231 }
1232