[NTOS] Addendum to 03873aee: check that the computed size of the OEM-converted string...
[reactos.git] / ntoskrnl / inbv / inbv.c
index 5526b07..9228569 100644 (file)
 #include <debug.h>
 #include "bootvid/bootvid.h"
 
+#ifndef TAG_OSTR
+#define TAG_OSTR    'RTSO'
+#endif
+
 /* GLOBALS *******************************************************************/
 
-KSPIN_LOCK BootDriverLock;
-KIRQL InbvOldIrql;
-INBV_DISPLAY_STATE InbvDisplayState;
+/*
+ * Enable this define if you want Inbv to use coloured headless mode.
+ */
+// #define INBV_HEADLESS_COLORS
+
+/*
+ * ReactOS uses the same boot screen for all the products.
+ * Also it doesn't use a parallel system thread for the
+ * rotating "progress" bar.
+ */
+
+/*
+ * Enable this define when ReactOS will have different SKUs
+ * (Workstation, Server, Storage Server, Cluster Server, etc...).
+ */
+// #define REACTOS_SKUS
+
+/*
+ * Enable this define when Inbv will support rotating progress bar.
+ */
+#define INBV_ROTBAR_IMPLEMENTED
+
+static KSPIN_LOCK BootDriverLock;
+static KIRQL InbvOldIrql;
+static INBV_DISPLAY_STATE InbvDisplayState = INBV_DISPLAY_STATE_DISABLED;
 BOOLEAN InbvBootDriverInstalled = FALSE;
-BOOLEAN InbvDisplayDebugStrings = FALSE;
-INBV_DISPLAY_STRING_FILTER InbvDisplayFilter;
-ULONG ProgressBarLeft, ProgressBarTop;
-BOOLEAN ShowProgressBar = FALSE;
-INBV_PROGRESS_STATE InbvProgressState;
-INBV_RESET_DISPLAY_PARAMETERS InbvResetDisplayParameters;
-ULONG ResourceCount;
-PUCHAR ResourceList[64];
-BOOLEAN SysThreadCreated = FALSE;
-ROT_BAR_TYPE RotBarSelection;
-ULONG PltRotBarStatus;
-BT_PROGRESS_INDICATOR InbvProgressIndicator = {0, 25, 0};
+static BOOLEAN InbvDisplayDebugStrings = FALSE;
+static INBV_DISPLAY_STRING_FILTER InbvDisplayFilter = NULL;
+static ULONG ProgressBarLeft = 0, ProgressBarTop = 0;
+static BOOLEAN ShowProgressBar = FALSE;
+static INBV_PROGRESS_STATE InbvProgressState;
+static BT_PROGRESS_INDICATOR InbvProgressIndicator = {0, 25, 0};
+static INBV_RESET_DISPLAY_PARAMETERS InbvResetDisplayParameters = NULL;
+static ULONG ResourceCount = 0;
+static PUCHAR ResourceList[1 + IDB_MAX_RESOURCE]; // First entry == NULL, followed by 'ResourceCount' entries.
+
+#ifdef INBV_ROTBAR_IMPLEMENTED
+/*
+ * Change this to modify progress bar behaviour
+ */
+#define ROT_BAR_DEFAULT_MODE    RB_PROGRESS_BAR
+
+/*
+ * Values for PltRotBarStatus:
+ * - PltRotBarStatus == 1, do palette fading-in (done elsewhere in ReactOS);
+ * - PltRotBarStatus == 2, do rotation bar animation;
+ * - PltRotBarStatus == 3, stop the animation thread.
+ * - Any other value is ignored and the animation thread continues to run.
+ */
+typedef enum _ROT_BAR_STATUS
+{
+    RBS_FADEIN = 1,
+    RBS_ANIMATE,
+    RBS_STOP_ANIMATE,
+    RBS_STATUS_MAX
+} ROT_BAR_STATUS;
+
+static BOOLEAN RotBarThreadActive = FALSE;
+static ROT_BAR_TYPE RotBarSelection = RB_UNSPECIFIED;
+static ROT_BAR_STATUS PltRotBarStatus = 0;
+static UCHAR RotBarBuffer[24 * 9];
+static UCHAR RotLineBuffer[640 * 6];
+#endif
+
+
+/*
+ * Headless terminal text colors
+ */
+
+#ifdef INBV_HEADLESS_COLORS
+
+// Conversion table CGA to ANSI color index
+static const UCHAR CGA_TO_ANSI_COLOR_TABLE[16] =
+{
+    0,  // Black
+    4,  // Blue
+    2,  // Green
+    6,  // Cyan
+    1,  // Red
+    5,  // Magenta
+    3,  // Brown/Yellow
+    7,  // Grey/White
+
+    60, // Bright Black
+    64, // Bright Blue
+    62, // Bright Green
+    66, // Bright Cyan
+    61, // Bright Red
+    65, // Bright Magenta
+    63, // Bright Yellow
+    67  // Bright Grey (White)
+};
+
+#define CGA_TO_ANSI_COLOR(CgaColor) \
+    CGA_TO_ANSI_COLOR_TABLE[CgaColor & 0x0F]
+
+#endif
+
+// Default colors: text in white, background in black
+static ULONG InbvTerminalTextColor = 37;
+static ULONG InbvTerminalBkgdColor = 40;
+
 
 /* FADING FUNCTION ***********************************************************/
 
@@ -36,10 +126,10 @@ typedef struct tagRGBQUAD
 } RGBQUAD,*LPRGBQUAD;
 /*******************************/
 
-static RGBQUAD _MainPalette[16];
+static RGBQUAD MainPalette[16];
 
 #define PALETTE_FADE_STEPS  15
-#define PALETTE_FADE_TIME   20 * 1000 /* 20ms */
+#define PALETTE_FADE_TIME   (20 * 1000) /* 20 ms */
 
 /** From bootvid/precomp.h **/
 //
@@ -68,58 +158,55 @@ VOID NTAPI InbvAcquireLock(VOID);
 VOID NTAPI InbvReleaseLock(VOID);
 
 static VOID
-NTAPI
-BootImageFadeIn(VOID)
+BootLogoFadeIn(VOID)
 {
-    UCHAR PaletteBitmapBuffer[sizeof(BITMAPINFOHEADER) + sizeof(_MainPalette)];
+    UCHAR PaletteBitmapBuffer[sizeof(BITMAPINFOHEADER) + sizeof(MainPalette)];
     PBITMAPINFOHEADER PaletteBitmap = (PBITMAPINFOHEADER)PaletteBitmapBuffer;
     LPRGBQUAD Palette = (LPRGBQUAD)(PaletteBitmapBuffer + sizeof(BITMAPINFOHEADER));
-
     ULONG Iteration, Index, ClrUsed;
 
-    /* Check if we're installed and we own it */
-    if ((InbvBootDriverInstalled) &&
-        (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
-    {
-        /* Acquire the lock */
-        InbvAcquireLock();
+    LARGE_INTEGER Delay;
+    Delay.QuadPart = - (PALETTE_FADE_TIME * 10);
 
-        /*
-         * Build a bitmap containing the fade in palette. The palette entries
-         * are then processed in a loop and set using VidBitBlt function.
-         */
-        ClrUsed = sizeof(_MainPalette) / sizeof(_MainPalette[0]);
-        RtlZeroMemory(PaletteBitmap, sizeof(BITMAPINFOHEADER));
-        PaletteBitmap->biSize = sizeof(BITMAPINFOHEADER);
-        PaletteBitmap->biBitCount = 4;
-        PaletteBitmap->biClrUsed = ClrUsed;
+    /* Check if we are installed and we own the display */
+    if (!InbvBootDriverInstalled ||
+        (InbvDisplayState != INBV_DISPLAY_STATE_OWNED))
+    {
+        return;
+    }
 
-        /*
-         * Main animation loop.
-         */
-        for (Iteration = 0; Iteration <= PALETTE_FADE_STEPS; ++Iteration)
+    /*
+     * Build a bitmap containing the fade-in palette. The palette entries
+     * are then processed in a loop and set using VidBitBlt function.
+     */
+    ClrUsed = RTL_NUMBER_OF(MainPalette);
+    RtlZeroMemory(PaletteBitmap, sizeof(BITMAPINFOHEADER));
+    PaletteBitmap->biSize = sizeof(BITMAPINFOHEADER);
+    PaletteBitmap->biBitCount = 4;
+    PaletteBitmap->biClrUsed = ClrUsed;
+
+    /*
+     * Main animation loop.
+     */
+    for (Iteration = 0; Iteration <= PALETTE_FADE_STEPS; ++Iteration)
+    {
+        for (Index = 0; Index < ClrUsed; Index++)
         {
-            for (Index = 0; Index < ClrUsed; Index++)
-            {
-                Palette[Index].rgbRed = (UCHAR)
-                    (_MainPalette[Index].rgbRed * Iteration / PALETTE_FADE_STEPS);
-                Palette[Index].rgbGreen = (UCHAR)
-                    (_MainPalette[Index].rgbGreen * Iteration / PALETTE_FADE_STEPS);
-                Palette[Index].rgbBlue = (UCHAR)
-                    (_MainPalette[Index].rgbBlue * Iteration / PALETTE_FADE_STEPS);
-            }
-
-            VidBitBlt(PaletteBitmapBuffer, 0, 0);
-
-            /* Wait for a bit. */
-            KeStallExecutionProcessor(PALETTE_FADE_TIME);
+            Palette[Index].rgbRed = (UCHAR)
+                (MainPalette[Index].rgbRed * Iteration / PALETTE_FADE_STEPS);
+            Palette[Index].rgbGreen = (UCHAR)
+                (MainPalette[Index].rgbGreen * Iteration / PALETTE_FADE_STEPS);
+            Palette[Index].rgbBlue = (UCHAR)
+                (MainPalette[Index].rgbBlue * Iteration / PALETTE_FADE_STEPS);
         }
 
-        /* Release the lock */
+        /* Do the animation */
+        InbvAcquireLock();
+        VidBitBlt(PaletteBitmapBuffer, 0, 0);
         InbvReleaseLock();
 
-        /* Wait for a bit. */
-        KeStallExecutionProcessor(PALETTE_FADE_TIME);
+        /* Wait for a bit */
+        KeDelayExecutionThread(KernelMode, FALSE, &Delay);
     }
 }
 
@@ -151,8 +238,8 @@ FindBitmapResource(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
                                      InLoadOrderLinks);
 
         /* Check for a match */
-        if ((RtlEqualUnicodeString(&LdrEntry->BaseDllName, &UpString, TRUE)) ||
-            (RtlEqualUnicodeString(&LdrEntry->BaseDllName, &MpString, TRUE)))
+        if (RtlEqualUnicodeString(&LdrEntry->BaseDllName, &UpString, TRUE) ||
+            RtlEqualUnicodeString(&LdrEntry->BaseDllName, &MpString, TRUE))
         {
             /* Break out */
             break;
@@ -163,7 +250,7 @@ FindBitmapResource(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
     if (NextEntry != ListHead)
     {
         /* Try to find the resource */
-        ResourceInfo.Type = 2; //RT_BITMAP;
+        ResourceInfo.Type = 2; // RT_BITMAP;
         ResourceInfo.Name = ResourceId;
         ResourceInfo.Language = 0;
         Status = LdrFindResource_U(LdrEntry->DllBase,
@@ -197,7 +284,7 @@ InbvDriverInitialize(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
                      IN ULONG Count)
 {
     PCHAR CommandLine;
-    BOOLEAN CustomLogo = FALSE;
+    BOOLEAN ResetMode = FALSE; // By default do not reset the video mode
     ULONG i;
 
     /* Quit if we're already installed */
@@ -207,21 +294,18 @@ InbvDriverInitialize(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
     KeInitializeSpinLock(&BootDriverLock);
     if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
     {
-        /* Check if we have a custom boot logo */
-        CommandLine = _strupr(LoaderBlock->LoadOptions);
-        CustomLogo = strstr(CommandLine, "BOOTLOGO") ? TRUE: FALSE;
+        /* Reset the video mode in case we do not have a custom boot logo */
+        CommandLine = (LoaderBlock->LoadOptions ? _strupr(LoaderBlock->LoadOptions) : NULL);
+        ResetMode   = (CommandLine == NULL) || (strstr(CommandLine, "BOOTLOGO") == NULL);
     }
 
     /* Initialize the video */
-    InbvBootDriverInstalled = VidInitialize(FALSE);
+    InbvBootDriverInstalled = VidInitialize(ResetMode);
     if (InbvBootDriverInstalled)
     {
-        /* Now reset the display, but only if there's a custom boot logo */
-        VidResetDisplay(CustomLogo);
-
         /* Find bitmap resources in the kernel */
-        ResourceCount = min(IDB_CLUSTER_SERVER, Count);
-        for (i = 1; i <= Count; i++)
+        ResourceCount = min(Count, RTL_NUMBER_OF(ResourceList) - 1);
+        for (i = 1; i <= ResourceCount; i++)
         {
             /* Do the lookup */
             ResourceList[i] = FindBitmapResource(LoaderBlock, i);
@@ -291,7 +375,7 @@ InbvEnableBootDriver(IN BOOLEAN Enable)
         if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED) VidCleanUp();
 
         /* Set the new display state */
-        InbvDisplayState = Enable ? INBV_DISPLAY_STATE_OWNED:
+        InbvDisplayState = Enable ? INBV_DISPLAY_STATE_OWNED :
                                     INBV_DISPLAY_STATE_DISABLED;
 
         /* Release the lock */
@@ -300,7 +384,7 @@ InbvEnableBootDriver(IN BOOLEAN Enable)
     else
     {
         /* Set the new display state */
-        InbvDisplayState = Enable ? INBV_DISPLAY_STATE_OWNED:
+        InbvDisplayState = Enable ? INBV_DISPLAY_STATE_OWNED :
                                     INBV_DISPLAY_STATE_DISABLED;
     }
 }
@@ -326,7 +410,7 @@ NTAPI
 InbvSetDisplayOwnership(IN BOOLEAN DisplayOwned)
 {
     /* Set the new display state */
-    InbvDisplayState = DisplayOwned ? INBV_DISPLAY_STATE_OWNED:
+    InbvDisplayState = DisplayOwned ? INBV_DISPLAY_STATE_OWNED :
                                       INBV_DISPLAY_STATE_LOST;
 }
 
@@ -446,7 +530,7 @@ NTAPI
 InbvResetDisplay(VOID)
 {
     /* Check if we're installed and we own it */
-    if ((InbvBootDriverInstalled) &&
+    if (InbvBootDriverInstalled &&
         (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
     {
         /* Do the reset */
@@ -473,7 +557,21 @@ VOID
 NTAPI
 InbvSetTextColor(IN ULONG Color)
 {
-    /* FIXME: Headless */
+    HEADLESS_CMD_SET_COLOR HeadlessSetColor;
+
+    /* Set color for EMS port */
+#ifdef INBV_HEADLESS_COLORS
+    InbvTerminalTextColor = 30 + CGA_TO_ANSI_COLOR(Color);
+#else
+    InbvTerminalTextColor = 37;
+#endif
+    HeadlessSetColor.TextColor = InbvTerminalTextColor;
+    HeadlessSetColor.BkgdColor = InbvTerminalBkgdColor;
+    HeadlessDispatch(HeadlessCmdSetColor,
+                     &HeadlessSetColor,
+                     sizeof(HeadlessSetColor),
+                     NULL,
+                     NULL);
 
     /* Update the text color */
     VidSetTextColor(Color);
@@ -487,6 +585,8 @@ InbvSolidColorFill(IN ULONG Left,
                    IN ULONG Bottom,
                    IN ULONG Color)
 {
+    HEADLESS_CMD_SET_COLOR HeadlessSetColor;
+
     /* Make sure we own it */
     if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
     {
@@ -500,7 +600,22 @@ InbvSolidColorFill(IN ULONG Left,
             VidSolidColorFill(Left, Top, Right, Bottom, (UCHAR)Color);
         }
 
-        /* FIXME: Headless */
+        /* Set color for EMS port and clear display */
+#ifdef INBV_HEADLESS_COLORS
+        InbvTerminalBkgdColor = 40 + CGA_TO_ANSI_COLOR(Color);
+#else
+        InbvTerminalBkgdColor = 40;
+#endif
+        HeadlessSetColor.TextColor = InbvTerminalTextColor;
+        HeadlessSetColor.BkgdColor = InbvTerminalBkgdColor;
+        HeadlessDispatch(HeadlessCmdSetColor,
+                         &HeadlessSetColor,
+                         sizeof(HeadlessSetColor),
+                         NULL,
+                         NULL);
+        HeadlessDispatch(HeadlessCmdClearDisplay,
+                         NULL, 0,
+                         NULL, NULL);
 
         /* Release the lock */
         InbvReleaseLock();
@@ -515,8 +630,8 @@ InbvUpdateProgressBar(IN ULONG Progress)
     ULONG FillCount, BoundedProgress;
 
     /* Make sure the progress bar is enabled, that we own and are installed */
-    if ((ShowProgressBar) &&
-        (InbvBootDriverInstalled) &&
+    if (ShowProgressBar &&
+        InbvBootDriverInstalled &&
         (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
     {
         /* Compute fill count */
@@ -548,7 +663,7 @@ InbvBufferToScreenBlt(IN PUCHAR Buffer,
                       IN ULONG Delta)
 {
     /* Check if we're installed and we own it */
-    if ((InbvBootDriverInstalled) &&
+    if (InbvBootDriverInstalled &&
         (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
     {
         /* Do the blit */
@@ -563,7 +678,7 @@ InbvBitBlt(IN PUCHAR Buffer,
            IN ULONG Y)
 {
     /* Check if we're installed and we own it */
-    if ((InbvBootDriverInstalled) &&
+    if (InbvBootDriverInstalled &&
         (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
     {
         /* Acquire the lock */
@@ -587,7 +702,7 @@ InbvScreenToBufferBlt(IN PUCHAR Buffer,
                       IN ULONG Delta)
 {
     /* Check if we're installed and we own it */
-    if ((InbvBootDriverInstalled) &&
+    if (InbvBootDriverInstalled &&
         (InbvDisplayState == INBV_DISPLAY_STATE_OWNED))
     {
         /* Do the blit */
@@ -602,7 +717,7 @@ InbvSetProgressBarCoordinates(IN ULONG Left,
 {
     /* Update the coordinates */
     ProgressBarLeft = Left;
-    ProgressBarTop = Top;
+    ProgressBarTop  = Top;
 
     /* Enable the progress bar */
     ShowProgressBar = TRUE;
@@ -653,99 +768,264 @@ InbvGetResourceAddress(IN ULONG ResourceNumber)
     if (ResourceNumber > ResourceCount) return NULL;
 
     /* Return the address */
-    return ResourceList[ResourceNumber--];
+    return ResourceList[ResourceNumber];
 }
 
 NTSTATUS
 NTAPI
 NtDisplayString(IN PUNICODE_STRING DisplayString)
 {
+    NTSTATUS Status;
+    UNICODE_STRING CapturedString;
     OEM_STRING OemString;
+    ULONG OemLength;
+    KPROCESSOR_MODE PreviousMode;
+
+    PAGED_CODE();
+
+    PreviousMode = ExGetPreviousMode();
+
+    /* We require the TCB privilege */
+    if (!SeSinglePrivilegeCheck(SeTcbPrivilege, PreviousMode))
+        return STATUS_PRIVILEGE_NOT_HELD;
+
+    /* Capture the string */
+    Status = ProbeAndCaptureUnicodeString(&CapturedString, PreviousMode, DisplayString);
+    if (!NT_SUCCESS(Status))
+        return Status;
 
-    /* Convert the string to OEM and display it */
-    RtlUnicodeStringToOemString(&OemString, DisplayString, TRUE);
+    /* Do not display the string if it is empty */
+    if (CapturedString.Length == 0 || CapturedString.Buffer == NULL)
+    {
+        Status = STATUS_SUCCESS;
+        goto Quit;
+    }
+
+    /*
+     * Convert the string since INBV understands only ANSI/OEM. Allocate the
+     * string buffer in non-paged pool because INBV passes it down to BOOTVID.
+     * We cannot perform the allocation using RtlUnicodeStringToOemString()
+     * since its allocator uses PagedPool.
+     */
+    OemLength = RtlUnicodeStringToOemSize(&CapturedString);
+    if (OemLength > MAXUSHORT)
+    {
+        Status = STATUS_BUFFER_OVERFLOW;
+        goto Quit;
+    }
+    RtlInitEmptyAnsiString((PANSI_STRING)&OemString, NULL, (USHORT)OemLength);
+    OemString.Buffer = ExAllocatePoolWithTag(NonPagedPool, OemLength, TAG_OSTR);
+    if (OemString.Buffer == NULL)
+    {
+        Status = STATUS_NO_MEMORY;
+        goto Quit;
+    }
+    RtlUnicodeStringToOemString(&OemString, &CapturedString, FALSE);
+
+    /* Display the string */
     InbvDisplayString(OemString.Buffer);
-    RtlFreeOemString(&OemString);
 
-    /* Return success */
-    return STATUS_SUCCESS;
+    /* Free the string buffer */
+    ExFreePoolWithTag(OemString.Buffer, TAG_OSTR);
+
+    Status = STATUS_SUCCESS;
+
+Quit:
+    /* Free the captured string */
+    ReleaseCapturedUnicodeString(&CapturedString, PreviousMode);
+
+    return Status;
+}
+
+#ifdef INBV_ROTBAR_IMPLEMENTED
+static
+VOID
+NTAPI
+InbvRotationThread(
+    _In_ PVOID Context)
+{
+    ULONG X, Y, Index, Total;
+    LARGE_INTEGER Delay = {{0}};
+
+    InbvAcquireLock();
+    if (RotBarSelection == RB_SQUARE_CELLS)
+    {
+        Index = 0;
+    }
+    else
+    {
+        Index = 32;
+    }
+    X = ProgressBarLeft + 2;
+    Y = ProgressBarTop + 2;
+    InbvReleaseLock();
+
+    while (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)
+    {
+        /* Wait for a bit */
+        KeDelayExecutionThread(KernelMode, FALSE, &Delay);
+
+        InbvAcquireLock();
+
+        /* Unknown unexpected command */
+        ASSERT(PltRotBarStatus < RBS_STATUS_MAX);
+
+        if (PltRotBarStatus == RBS_STOP_ANIMATE)
+        {
+            /* Stop the thread */
+            InbvReleaseLock();
+            break;
+        }
+
+        if (RotBarSelection == RB_SQUARE_CELLS)
+        {
+            Delay.QuadPart = -800000; // 80 ms
+            Total = 18;
+            Index %= Total;
+
+            if (Index >= 3)
+            {
+                /* Fill previous bar position */
+                VidSolidColorFill(X + ((Index - 3) * 8), Y, (X + ((Index - 3) * 8)) + 8 - 1, Y + 9 - 1, 0);
+            }
+            if (Index < Total - 1)
+            {
+                /* Draw the progress bar bit */
+                if (Index < 2)
+                {
+                    /* Appearing from the left */
+                    VidBufferToScreenBlt(RotBarBuffer + 8 * (2 - Index) / 2, X, Y, 22 - 8 * (2 - Index), 9, 24);
+                }
+                else if (Index >= Total - 3)
+                {
+                    /* Hiding to the right */
+                    VidBufferToScreenBlt(RotBarBuffer, X + ((Index - 2) * 8), Y, 22 - 8 * (4 - (Total - Index)), 9, 24);
+                }
+                else
+                {
+                    VidBufferToScreenBlt(RotBarBuffer, X + ((Index - 2) * 8), Y, 22, 9, 24);
+                }
+            }
+            Index++;
+        }
+        else if (RotBarSelection == RB_PROGRESS_BAR)
+        {
+            Delay.QuadPart = -600000; // 60 ms
+            Total = 640;
+            Index %= Total;
+
+            /* Right part */
+            VidBufferToScreenBlt(RotLineBuffer, Index, 474, 640 - Index, 6, 640);
+            if (Index > 0)
+            {
+                /* Left part */
+                VidBufferToScreenBlt(RotLineBuffer + (640 - Index) / 2, 0, 474, Index - 2, 6, 640);
+            }
+            Index += 32;
+        }
+
+        InbvReleaseLock();
+    }
+
+    PsTerminateSystemThread(STATUS_SUCCESS);
+}
+
+VOID
+NTAPI
+INIT_FUNCTION
+InbvRotBarInit(VOID)
+{
+    PltRotBarStatus = RBS_FADEIN;
+    /* Perform other initialization if needed */
 }
+#endif
 
 VOID
 NTAPI
 INIT_FUNCTION
 DisplayBootBitmap(IN BOOLEAN TextMode)
 {
-    PBITMAPINFOHEADER BitmapInfoHeader;
-    LPRGBQUAD Palette;
+    PVOID Header = NULL, Footer = NULL, Screen = NULL;
 
-    PVOID Header, Band, Text, Screen;
+#ifdef INBV_ROTBAR_IMPLEMENTED
+    UCHAR Buffer[24 * 9];
+    PVOID Bar = NULL, LineBmp = NULL;
     ROT_BAR_TYPE TempRotBarSelection = RB_UNSPECIFIED;
+    NTSTATUS Status;
+    HANDLE ThreadHandle = NULL;
+#endif
 
-#ifdef CORE_6781_resolved
-    UCHAR Buffer[64];
+#ifdef REACTOS_SKUS
+    PVOID Text = NULL;
 #endif
 
-    /* Check if the system thread has already been created */
-    if (SysThreadCreated)
+#ifdef INBV_ROTBAR_IMPLEMENTED
+    /* Check if the animation thread has already been created */
+    if (RotBarThreadActive)
     {
-        /* Reset the progress bar */
+        /* Yes, just reset the progress bar but keep the thread alive */
         InbvAcquireLock();
         RotBarSelection = RB_UNSPECIFIED;
         InbvReleaseLock();
     }
+#endif
 
-    /* Check if this is text mode */
     ShowProgressBar = FALSE;
+
+    /* Check if this is text mode */
     if (TextMode)
     {
-        /* Check if this is a server OS */
+        /* Check the type of the OS: workstation or server */
         if (SharedUserData->NtProductType == NtProductWinNt)
         {
-            /* It's not, set workstation settings */
+            /* Workstation; set colors */
             InbvSetTextColor(15);
             InbvSolidColorFill(0, 0, 639, 479, 7);
             InbvSolidColorFill(0, 421, 639, 479, 1);
 
             /* Get resources */
-            Header = InbvGetResourceAddress(IDB_LOGO_HEADER);
-            Band = InbvGetResourceAddress(IDB_LOGO_BAND);
+            Header = InbvGetResourceAddress(IDB_WKSTA_HEADER);
+            Footer = InbvGetResourceAddress(IDB_WKSTA_FOOTER);
         }
         else
         {
-            /* Set server settings */
+            /* Server; set colors */
             InbvSetTextColor(14);
             InbvSolidColorFill(0, 0, 639, 479, 6);
             InbvSolidColorFill(0, 421, 639, 479, 1);
 
             /* Get resources */
             Header = InbvGetResourceAddress(IDB_SERVER_HEADER);
-            Band = InbvGetResourceAddress(IDB_SERVER_BAND);
+            Footer = InbvGetResourceAddress(IDB_SERVER_FOOTER);
         }
 
         /* Set the scrolling region */
         InbvSetScrollRegion(32, 80, 631, 400);
 
         /* Make sure we have resources */
-        if ((Header) && (Band))
+        if (Header && Footer)
         {
             /* BitBlt them on the screen */
-            InbvBitBlt(Band, 0, 419);
+            InbvBitBlt(Footer, 0, 419);
             InbvBitBlt(Header, 0, 0);
         }
     }
     else
     {
         /* Is the boot driver installed? */
-        Text = NULL;
         if (!InbvBootDriverInstalled) return;
 
         /* Load the standard boot screen */
-        Screen = InbvGetResourceAddress(IDB_BOOT_LOGO);
+        Screen = InbvGetResourceAddress(IDB_BOOT_SCREEN);
+
+#ifdef REACTOS_SKUS
+        Text = NULL;
         if (SharedUserData->NtProductType == NtProductWinNt)
         {
-            /* Workstation product, display appropriate status bar color */
-            InbvGetResourceAddress(IDB_BAR_PRO);
+#ifdef INBV_ROTBAR_IMPLEMENTED
+            /* Workstation product, use appropriate status bar color */
+            Bar = InbvGetResourceAddress(IDB_BAR_WKSTA);
+#endif
         }
         else
         {
@@ -766,35 +1046,43 @@ DisplayBootBitmap(IN BOOLEAN TextMode)
                 Text = InbvGetResourceAddress(IDB_SERVER_LOGO);
             }
 
-            /* Server product, display appropriate status bar color */
-            InbvGetResourceAddress(IDB_BAR_SERVER);
+#ifdef INBV_ROTBAR_IMPLEMENTED
+            /* Server product, use appropriate status bar color */
+            Bar = InbvGetResourceAddress(IDB_BAR_SERVER);
+#endif
         }
+#else
+        /* Use default status bar */
+        Bar = InbvGetResourceAddress(IDB_BAR_WKSTA);
+#endif
 
-        /* Make sure we had a logo */
+        /* Make sure we have a logo */
         if (Screen)
         {
-            /* Choose progress bar */
-            TempRotBarSelection = RB_SQUARE_CELLS;
+            PBITMAPINFOHEADER BitmapInfoHeader;
+            LPRGBQUAD Palette;
 
             /*
-             * Save the main image palette and replace it with black palette, so
-             * we can do fade in effect later.
+             * Save the main image palette and replace it with black palette,
+             * so that we can do fade-in effect later.
              */
             BitmapInfoHeader = (PBITMAPINFOHEADER)Screen;
             Palette = (LPRGBQUAD)((PUCHAR)Screen + BitmapInfoHeader->biSize);
-            RtlCopyMemory(_MainPalette, Palette, sizeof(_MainPalette));
-            RtlZeroMemory(Palette, sizeof(_MainPalette));
+            RtlCopyMemory(MainPalette, Palette, sizeof(MainPalette));
+            RtlZeroMemory(Palette, sizeof(MainPalette));
 
             /* Blit the background */
             InbvBitBlt(Screen, 0, 0);
 
-            /* Set progress bar coordinates and display it */
-            InbvSetProgressBarCoordinates(257, 352);
+#ifdef INBV_ROTBAR_IMPLEMENTED
+            /* Choose progress bar */
+            TempRotBarSelection = ROT_BAR_DEFAULT_MODE;
+#endif
 
-            /* Display the boot logo and fade it in */
-            BootImageFadeIn();
+            /* Set progress bar coordinates and display it */
+            InbvSetProgressBarCoordinates(259, 352);
 
-#ifdef CORE_6781_resolved
+#ifdef REACTOS_SKUS
             /* Check for non-workstation products */
             if (SharedUserData->NtProductType != NtProductWinNt)
             {
@@ -809,29 +1097,92 @@ DisplayBootBitmap(IN BOOLEAN TextMode)
 #endif
         }
 
-#ifdef CORE_6781_resolved
+#ifdef REACTOS_SKUS
         /* Draw the SKU text if it exits */
         if (Text) InbvBitBlt(Text, 180, 121);
-#else
-        DBG_UNREFERENCED_LOCAL_VARIABLE(Text);
 #endif
 
-        /* Draw the progress bar bit */
-//      if (Bar) InbvBitBlt(Bar, 0, 0);
+#ifdef INBV_ROTBAR_IMPLEMENTED
+        if (Bar)
+        {
+            /* Save previous screen pixels to buffer */
+            InbvScreenToBufferBlt(Buffer, 0, 0, 22, 9, 24);
+            /* Draw the progress bar bit */
+            InbvBitBlt(Bar, 0, 0);
+            /* Store it in global buffer */
+            InbvScreenToBufferBlt(RotBarBuffer, 0, 0, 22, 9, 24);
+            /* Restore screen pixels */
+            InbvBufferToScreenBlt(Buffer, 0, 0, 22, 9, 24);
+        }
+
+        /*
+         * Add a rotating bottom horizontal bar when using a progress bar,
+         * to show that ReactOS can be still alive when the bar does not
+         * appear to progress.
+         */
+        if (TempRotBarSelection == RB_PROGRESS_BAR)
+        {
+            LineBmp = InbvGetResourceAddress(IDB_ROTATING_LINE);
+            if (LineBmp)
+            {
+                /* Draw the line and store it in global buffer */
+                InbvBitBlt(LineBmp, 0, 474);
+                InbvScreenToBufferBlt(RotLineBuffer, 0, 474, 640, 6, 640);
+            }
+        }
+        else
+        {
+            /* Hide the simple progress bar if not used */
+            ShowProgressBar = FALSE;
+        }
+#endif
+
+        /* Display the boot logo and fade it in */
+        BootLogoFadeIn();
+
+#ifdef INBV_ROTBAR_IMPLEMENTED
+        if (!RotBarThreadActive && TempRotBarSelection != RB_UNSPECIFIED)
+        {
+            /* Start the animation thread */
+            Status = PsCreateSystemThread(&ThreadHandle,
+                                          0,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          InbvRotationThread,
+                                          NULL);
+            if (NT_SUCCESS(Status))
+            {
+                /* The thread has started, close the handle as we don't need it */
+                RotBarThreadActive = TRUE;
+                ObCloseHandle(ThreadHandle, KernelMode);
+            }
+        }
+#endif
 
         /* Set filter which will draw text display if needed */
         InbvInstallDisplayStringFilter(DisplayFilter);
     }
 
-    /* Do we have a system thread? */
-    if (SysThreadCreated)
+#ifdef INBV_ROTBAR_IMPLEMENTED
+    /* Do we have the animation thread? */
+    if (RotBarThreadActive)
     {
-        /* We do, set the progress bar location */
+        /* We do, initialize the progress bar */
         InbvAcquireLock();
         RotBarSelection = TempRotBarSelection;
-        //InbvRotBarInit();
+        InbvRotBarInit();
         InbvReleaseLock();
+
+        // FIXME: This was added to allow animation start before the processor hangs
+        if (TempRotBarSelection != RB_UNSPECIFIED)
+        {
+            LARGE_INTEGER Delay;
+            Delay.QuadPart = -3000000; // 300 ms
+            KeDelayExecutionThread(KernelMode, FALSE, &Delay);
+        }
     }
+#endif
 }
 
 VOID
@@ -872,6 +1223,9 @@ FinalizeBootLogo(VOID)
     }
 
     /* Reset progress bar and lock */
-    PltRotBarStatus = 3;
+#ifdef INBV_ROTBAR_IMPLEMENTED
+    PltRotBarStatus = RBS_STOP_ANIMATE;
+    RotBarThreadActive = FALSE;
+#endif
     InbvReleaseLock();
 }