From 93c5e2b7c0973523c45beb0750df48714728d65b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Sun, 13 Feb 2022 21:18:12 +0100 Subject: [PATCH 1/2] [NTOS:INBV] Duplicate inbv.c to bootanim.c -- the Git way >_> This will preserve the history in the copied file. --- ntoskrnl/inbv/{inbv.c => bootanim.c} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ntoskrnl/inbv/{inbv.c => bootanim.c} (100%) diff --git a/ntoskrnl/inbv/inbv.c b/ntoskrnl/inbv/bootanim.c similarity index 100% rename from ntoskrnl/inbv/inbv.c rename to ntoskrnl/inbv/bootanim.c From 8fd64d636d59ef8fea153ac331260bcd4f8b91ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Sun, 13 Feb 2022 21:18:51 +0100 Subject: [PATCH 2/2] Restore the original inbv.c --- ntoskrnl/inbv/inbv.c | 1464 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1464 insertions(+) create mode 100644 ntoskrnl/inbv/inbv.c diff --git a/ntoskrnl/inbv/inbv.c b/ntoskrnl/inbv/inbv.c new file mode 100644 index 00000000000..4f6a8df1e0f --- /dev/null +++ b/ntoskrnl/inbv/inbv.c @@ -0,0 +1,1464 @@ +/* INCLUDES ******************************************************************/ + +#include + +#define NDEBUG +#include + +#include "inbv/logo.h" + +/* See also mm/ARM3/miarm.h */ +#define MM_READONLY 1 // PAGE_READONLY +#define MM_READWRITE 4 // PAGE_WRITECOPY + +/* GLOBALS *******************************************************************/ + +/* + * 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. + */ + +/* + * Enable this define when ReactOS will have different SKUs + * (Workstation, Server, Storage Server, Cluster Server, etc...). + */ +// #define REACTOS_SKUS + +typedef struct _INBV_PROGRESS_STATE +{ + ULONG Floor; + ULONG Ceiling; + ULONG Bias; +} INBV_PROGRESS_STATE; + +typedef struct _BT_PROGRESS_INDICATOR +{ + ULONG Count; + ULONG Expected; + ULONG Percentage; +} BT_PROGRESS_INDICATOR, *PBT_PROGRESS_INDICATOR; + +typedef enum _ROT_BAR_TYPE +{ + RB_UNSPECIFIED, + RB_SQUARE_CELLS, + RB_PROGRESS_BAR +} ROT_BAR_TYPE; + +/* + * BitBltAligned() alignments + */ +typedef enum _BBLT_VERT_ALIGNMENT +{ + AL_VERTICAL_TOP = 0, + AL_VERTICAL_CENTER, + AL_VERTICAL_BOTTOM +} BBLT_VERT_ALIGNMENT; + +typedef enum _BBLT_HORZ_ALIGNMENT +{ + AL_HORIZONTAL_LEFT = 0, + AL_HORIZONTAL_CENTER, + AL_HORIZONTAL_RIGHT +} BBLT_HORZ_ALIGNMENT; + +/* + * 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; +static BOOLEAN InbvDisplayDebugStrings = FALSE; +static INBV_DISPLAY_STRING_FILTER InbvDisplayFilter = NULL; +static ULONG ProgressBarLeft = 0, ProgressBarTop = 0; +static ULONG ProgressBarWidth = 0, ProgressBarHeight = 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[SCREEN_WIDTH * 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 ***********************************************************/ + +/** From include/psdk/wingdi.h **/ +typedef struct tagRGBQUAD +{ + UCHAR rgbBlue; + UCHAR rgbGreen; + UCHAR rgbRed; + UCHAR rgbReserved; +} RGBQUAD,*LPRGBQUAD; +/*******************************/ + +static RGBQUAD MainPalette[16]; + +#define PALETTE_FADE_STEPS 12 +#define PALETTE_FADE_TIME (15 * 1000) /* 15 ms */ + +/** From bootvid/precomp.h **/ +// +// Bitmap Header +// +typedef struct tagBITMAPINFOHEADER +{ + ULONG biSize; + LONG biWidth; + LONG biHeight; + USHORT biPlanes; + USHORT biBitCount; + ULONG biCompression; + ULONG biSizeImage; + LONG biXPelsPerMeter; + LONG biYPelsPerMeter; + ULONG biClrUsed; + ULONG biClrImportant; +} BITMAPINFOHEADER, *PBITMAPINFOHEADER; +/****************************/ + +// +// Needed prototypes +// +VOID NTAPI InbvAcquireLock(VOID); +VOID NTAPI InbvReleaseLock(VOID); + +static VOID +BootLogoFadeIn(VOID) +{ + UCHAR PaletteBitmapBuffer[sizeof(BITMAPINFOHEADER) + sizeof(MainPalette)]; + PBITMAPINFOHEADER PaletteBitmap = (PBITMAPINFOHEADER)PaletteBitmapBuffer; + LPRGBQUAD Palette = (LPRGBQUAD)(PaletteBitmapBuffer + sizeof(BITMAPINFOHEADER)); + ULONG Iteration, Index, ClrUsed; + + LARGE_INTEGER Delay; + Delay.QuadPart = -(PALETTE_FADE_TIME * 10); + + /* Check if we are installed and we own the display */ + if (!InbvBootDriverInstalled || + (InbvDisplayState != INBV_DISPLAY_STATE_OWNED)) + { + return; + } + + /* + * 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++) + { + 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); + } + + /* Do the animation */ + InbvAcquireLock(); + VidBitBlt(PaletteBitmapBuffer, 0, 0); + InbvReleaseLock(); + + /* Wait for a bit */ + KeDelayExecutionThread(KernelMode, FALSE, &Delay); + } +} + +static VOID +BitBltPalette( + IN PVOID Image, + IN BOOLEAN NoPalette, + IN ULONG X, + IN ULONG Y) +{ + LPRGBQUAD Palette; + RGBQUAD OrigPalette[RTL_NUMBER_OF(MainPalette)]; + + /* If requested, remove the palette from the image */ + if (NoPalette) + { + /* Get bitmap header and palette */ + PBITMAPINFOHEADER BitmapInfoHeader = Image; + Palette = (LPRGBQUAD)((PUCHAR)Image + BitmapInfoHeader->biSize); + + /* Save the image original palette and remove palette information */ + RtlCopyMemory(OrigPalette, Palette, sizeof(OrigPalette)); + RtlZeroMemory(Palette, sizeof(OrigPalette)); + } + + /* Draw the image */ + InbvBitBlt(Image, X, Y); + + /* Restore the image original palette */ + if (NoPalette) + { + RtlCopyMemory(Palette, OrigPalette, sizeof(OrigPalette)); + } +} + +static VOID +BitBltAligned( + IN PVOID Image, + IN BOOLEAN NoPalette, + IN BBLT_HORZ_ALIGNMENT HorizontalAlignment, + IN BBLT_VERT_ALIGNMENT VerticalAlignment, + IN ULONG MarginLeft, + IN ULONG MarginTop, + IN ULONG MarginRight, + IN ULONG MarginBottom) +{ + PBITMAPINFOHEADER BitmapInfoHeader = Image; + ULONG X, Y; + + /* Calculate X */ + switch (HorizontalAlignment) + { + case AL_HORIZONTAL_LEFT: + X = MarginLeft - MarginRight; + break; + + case AL_HORIZONTAL_CENTER: + X = MarginLeft - MarginRight + (SCREEN_WIDTH - BitmapInfoHeader->biWidth + 1) / 2; + break; + + case AL_HORIZONTAL_RIGHT: + X = MarginLeft - MarginRight + SCREEN_WIDTH - BitmapInfoHeader->biWidth; + break; + + default: + /* Unknown */ + return; + } + + /* Calculate Y */ + switch (VerticalAlignment) + { + case AL_VERTICAL_TOP: + Y = MarginTop - MarginBottom; + break; + + case AL_VERTICAL_CENTER: + Y = MarginTop - MarginBottom + (SCREEN_HEIGHT - BitmapInfoHeader->biHeight + 1) / 2; + break; + + case AL_VERTICAL_BOTTOM: + Y = MarginTop - MarginBottom + SCREEN_HEIGHT - BitmapInfoHeader->biHeight; + break; + + default: + /* Unknown */ + return; + } + + /* Finally draw the image */ + BitBltPalette(Image, NoPalette, X, Y); +} + +/* FUNCTIONS *****************************************************************/ + +CODE_SEG("INIT") +PVOID +NTAPI +FindBitmapResource(IN PLOADER_PARAMETER_BLOCK LoaderBlock, + IN ULONG ResourceId) +{ + UNICODE_STRING UpString = RTL_CONSTANT_STRING(L"ntoskrnl.exe"); + UNICODE_STRING MpString = RTL_CONSTANT_STRING(L"ntkrnlmp.exe"); + PLIST_ENTRY NextEntry, ListHead; + PLDR_DATA_TABLE_ENTRY LdrEntry; + PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry; + LDR_RESOURCE_INFO ResourceInfo; + NTSTATUS Status; + PVOID Data = NULL; + + /* Loop the driver list */ + ListHead = &LoaderBlock->LoadOrderListHead; + NextEntry = ListHead->Flink; + while (NextEntry != ListHead) + { + /* Get the entry */ + LdrEntry = CONTAINING_RECORD(NextEntry, + LDR_DATA_TABLE_ENTRY, + InLoadOrderLinks); + + /* Check for a match */ + if (RtlEqualUnicodeString(&LdrEntry->BaseDllName, &UpString, TRUE) || + RtlEqualUnicodeString(&LdrEntry->BaseDllName, &MpString, TRUE)) + { + /* Break out */ + break; + } + } + + /* Check if we found it */ + if (NextEntry != ListHead) + { + /* Try to find the resource */ + ResourceInfo.Type = 2; // RT_BITMAP; + ResourceInfo.Name = ResourceId; + ResourceInfo.Language = 0; + Status = LdrFindResource_U(LdrEntry->DllBase, + &ResourceInfo, + RESOURCE_DATA_LEVEL, + &ResourceDataEntry); + if (NT_SUCCESS(Status)) + { + /* Access the resource */ + ULONG Size = 0; + Status = LdrAccessResource(LdrEntry->DllBase, + ResourceDataEntry, + &Data, + &Size); + if ((Data) && (ResourceId < 3)) + { + KiBugCheckData[4] ^= RtlComputeCrc32(0, Data, Size); + } + if (!NT_SUCCESS(Status)) Data = NULL; + } + } + + /* Return the pointer */ + return Data; +} + +CODE_SEG("INIT") +BOOLEAN +NTAPI +InbvDriverInitialize(IN PLOADER_PARAMETER_BLOCK LoaderBlock, + IN ULONG Count) +{ + PCHAR CommandLine; + BOOLEAN ResetMode = FALSE; // By default do not reset the video mode + ULONG i; + + /* Quit if we're already installed */ + if (InbvBootDriverInstalled) return TRUE; + + /* Initialize the lock and check the current display state */ + KeInitializeSpinLock(&BootDriverLock); + if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED) + { + /* 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(ResetMode); + if (InbvBootDriverInstalled) + { + /* Find bitmap resources in the kernel */ + ResourceCount = min(Count, RTL_NUMBER_OF(ResourceList) - 1); + for (i = 1; i <= ResourceCount; i++) + { + /* Do the lookup */ + ResourceList[i] = FindBitmapResource(LoaderBlock, i); + } + + /* Set the progress bar ranges */ + InbvSetProgressBarSubset(0, 100); + } + + /* Return install state */ + return InbvBootDriverInstalled; +} + +VOID +NTAPI +InbvAcquireLock(VOID) +{ + KIRQL OldIrql; + + /* Check if we're at dispatch level or lower */ + OldIrql = KeGetCurrentIrql(); + if (OldIrql <= DISPATCH_LEVEL) + { + /* Loop until the lock is free */ + while (!KeTestSpinLock(&BootDriverLock)); + + /* Raise IRQL to dispatch level */ + KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); + } + + /* Acquire the lock */ + KiAcquireSpinLock(&BootDriverLock); + InbvOldIrql = OldIrql; +} + +VOID +NTAPI +InbvReleaseLock(VOID) +{ + KIRQL OldIrql; + + /* Capture the old IRQL */ + OldIrql = InbvOldIrql; + + /* Release the driver lock */ + KiReleaseSpinLock(&BootDriverLock); + + /* If we were at dispatch level or lower, restore the old IRQL */ + if (InbvOldIrql <= DISPATCH_LEVEL) KeLowerIrql(OldIrql); +} + +VOID +NTAPI +InbvEnableBootDriver(IN BOOLEAN Enable) +{ + /* Check if we're installed */ + if (InbvBootDriverInstalled) + { + /* Check for lost state */ + if (InbvDisplayState >= INBV_DISPLAY_STATE_LOST) return; + + /* Acquire the lock */ + InbvAcquireLock(); + + /* Cleanup the screen if we own it */ + if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED) VidCleanUp(); + + /* Set the new display state */ + InbvDisplayState = Enable ? INBV_DISPLAY_STATE_OWNED : + INBV_DISPLAY_STATE_DISABLED; + + /* Release the lock */ + InbvReleaseLock(); + } + else + { + /* Set the new display state */ + InbvDisplayState = Enable ? INBV_DISPLAY_STATE_OWNED : + INBV_DISPLAY_STATE_DISABLED; + } +} + +VOID +NTAPI +InbvAcquireDisplayOwnership(VOID) +{ + /* Check if we have a callback and we're just acquiring it now */ + if ((InbvResetDisplayParameters) && + (InbvDisplayState == INBV_DISPLAY_STATE_LOST)) + { + /* Call the callback */ + InbvResetDisplayParameters(80, 50); + } + + /* Acquire the display */ + InbvDisplayState = INBV_DISPLAY_STATE_OWNED; +} + +VOID +NTAPI +InbvSetDisplayOwnership(IN BOOLEAN DisplayOwned) +{ + /* Set the new display state */ + InbvDisplayState = DisplayOwned ? INBV_DISPLAY_STATE_OWNED : + INBV_DISPLAY_STATE_LOST; +} + +BOOLEAN +NTAPI +InbvCheckDisplayOwnership(VOID) +{ + /* Return if we own it or not */ + return InbvDisplayState != INBV_DISPLAY_STATE_LOST; +} + +INBV_DISPLAY_STATE +NTAPI +InbvGetDisplayState(VOID) +{ + /* Return the actual state */ + return InbvDisplayState; +} + +BOOLEAN +NTAPI +InbvDisplayString(IN PCHAR String) +{ + /* Make sure we own the display */ + if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED) + { + /* If we're not allowed, return success anyway */ + if (!InbvDisplayDebugStrings) return TRUE; + + /* Check if a filter is installed */ + if (InbvDisplayFilter) InbvDisplayFilter(&String); + + /* Acquire the lock */ + InbvAcquireLock(); + + /* Make sure we're installed and display the string */ + if (InbvBootDriverInstalled) VidDisplayString((PUCHAR)String); + + /* Print the string on the EMS port */ + HeadlessDispatch(HeadlessCmdPutString, + String, + strlen(String) + sizeof(ANSI_NULL), + NULL, + NULL); + + /* Release the lock */ + InbvReleaseLock(); + + /* All done */ + return TRUE; + } + + /* We don't own it, fail */ + return FALSE; +} + +BOOLEAN +NTAPI +InbvEnableDisplayString(IN BOOLEAN Enable) +{ + BOOLEAN OldSetting; + + /* Get the old setting */ + OldSetting = InbvDisplayDebugStrings; + + /* Update it */ + InbvDisplayDebugStrings = Enable; + + /* Return the old setting */ + return OldSetting; +} + +VOID +NTAPI +InbvInstallDisplayStringFilter(IN INBV_DISPLAY_STRING_FILTER Filter) +{ + /* Save the filter */ + InbvDisplayFilter = Filter; +} + +BOOLEAN +NTAPI +InbvIsBootDriverInstalled(VOID) +{ + /* Return driver state */ + return InbvBootDriverInstalled; +} + +VOID +NTAPI +InbvNotifyDisplayOwnershipLost(IN INBV_RESET_DISPLAY_PARAMETERS Callback) +{ + /* Check if we're installed */ + if (InbvBootDriverInstalled) + { + /* Acquire the lock and cleanup if we own the screen */ + InbvAcquireLock(); + if (InbvDisplayState != INBV_DISPLAY_STATE_LOST) VidCleanUp(); + + /* Set the reset callback and display state */ + InbvResetDisplayParameters = Callback; + InbvDisplayState = INBV_DISPLAY_STATE_LOST; + + /* Release the lock */ + InbvReleaseLock(); + } + else + { + /* Set the reset callback and display state */ + InbvResetDisplayParameters = Callback; + InbvDisplayState = INBV_DISPLAY_STATE_LOST; + } +} + +BOOLEAN +NTAPI +InbvResetDisplay(VOID) +{ + /* Check if we're installed and we own it */ + if (InbvBootDriverInstalled && + (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)) + { + /* Do the reset */ + VidResetDisplay(TRUE); + return TRUE; + } + + /* Nothing to reset */ + return FALSE; +} + +VOID +NTAPI +InbvSetScrollRegion(IN ULONG Left, + IN ULONG Top, + IN ULONG Right, + IN ULONG Bottom) +{ + /* Just call bootvid */ + VidSetScrollRegion(Left, Top, Right, Bottom); +} + +VOID +NTAPI +InbvSetTextColor(IN ULONG Color) +{ + 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); +} + +VOID +NTAPI +InbvSolidColorFill(IN ULONG Left, + IN ULONG Top, + IN ULONG Right, + IN ULONG Bottom, + IN ULONG Color) +{ + HEADLESS_CMD_SET_COLOR HeadlessSetColor; + + /* Make sure we own it */ + if (InbvDisplayState == INBV_DISPLAY_STATE_OWNED) + { + /* Acquire the lock */ + InbvAcquireLock(); + + /* Check if we're installed */ + if (InbvBootDriverInstalled) + { + /* Call bootvid */ + VidSolidColorFill(Left, Top, Right, Bottom, (UCHAR)Color); + } + + /* 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(); + } +} + +/** + * @brief + * Updates the progress bar percentage, relative to the current + * percentage sub-range previously set by InbvSetProgressBarSubset(). + * + * @param[in] Percentage + * The progress percentage, relative to the current sub-range. + * + * @return None. + **/ +CODE_SEG("INIT") +VOID +NTAPI +InbvUpdateProgressBar( + _In_ ULONG Percentage) +{ + ULONG TotalProgress, FillCount; + + /* Make sure the progress bar is enabled, that we own and are installed */ + if (ShowProgressBar && + InbvBootDriverInstalled && + (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)) + { + /* Compute fill count */ + TotalProgress = InbvProgressState.Floor + (Percentage * InbvProgressState.Bias); + FillCount = ProgressBarWidth * TotalProgress / (100 * 100); + + /* Acquire the lock */ + InbvAcquireLock(); + + /* Fill the progress bar */ + VidSolidColorFill(ProgressBarLeft, + ProgressBarTop, + ProgressBarLeft + FillCount, + ProgressBarTop + ProgressBarHeight, + BV_COLOR_WHITE); + + /* Release the lock */ + InbvReleaseLock(); + } +} + +VOID +NTAPI +InbvBufferToScreenBlt(IN PUCHAR Buffer, + IN ULONG X, + IN ULONG Y, + IN ULONG Width, + IN ULONG Height, + IN ULONG Delta) +{ + /* Check if we're installed and we own it */ + if (InbvBootDriverInstalled && + (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)) + { + /* Do the blit */ + VidBufferToScreenBlt(Buffer, X, Y, Width, Height, Delta); + } +} + +VOID +NTAPI +InbvBitBlt(IN PUCHAR Buffer, + IN ULONG X, + IN ULONG Y) +{ + /* Check if we're installed and we own it */ + if (InbvBootDriverInstalled && + (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)) + { + /* Acquire the lock */ + InbvAcquireLock(); + + /* Do the blit */ + VidBitBlt(Buffer, X, Y); + + /* Release the lock */ + InbvReleaseLock(); + } +} + +VOID +NTAPI +InbvScreenToBufferBlt(OUT PUCHAR Buffer, + IN ULONG X, + IN ULONG Y, + IN ULONG Width, + IN ULONG Height, + IN ULONG Delta) +{ + /* Check if we're installed and we own it */ + if (InbvBootDriverInstalled && + (InbvDisplayState == INBV_DISPLAY_STATE_OWNED)) + { + /* Do the blit */ + VidScreenToBufferBlt(Buffer, X, Y, Width, Height, Delta); + } +} + +/** + * @brief + * Sets the screen coordinates of the loading progress bar and enable it. + * + * @param[in] Left + * @param[in] Top + * The left/top coordinates. + * + * (ReactOS-specific) + * @param[in] Width + * @param[in] Height + * The width/height of the progress bar. + * + * @return None. + **/ +CODE_SEG("INIT") +VOID +NTAPI +InbvSetProgressBarCoordinates( + _In_ ULONG Left, + _In_ ULONG Top, + _In_ ULONG Width, + _In_ ULONG Height) +{ + /* Update the coordinates */ + ProgressBarLeft = Left; + ProgressBarTop = Top; + ProgressBarWidth = Width; + ProgressBarHeight = Height; + + /* Enable the progress bar */ + ShowProgressBar = TRUE; +} + +/** + * @brief + * Specifies a progress percentage sub-range. + * Further calls to InbvIndicateProgress() or InbvUpdateProgressBar() + * will update the progress percentage relative to this sub-range. + * In particular, the percentage provided to InbvUpdateProgressBar() + * is relative to this sub-range. + * + * @param[in] Floor + * The lower bound percentage of the sub-range (default: 0). + * + * @param[in] Ceiling + * The upper bound percentage of the sub-range (default: 100). + * + * @return None. + **/ +CODE_SEG("INIT") +VOID +NTAPI +InbvSetProgressBarSubset( + _In_ ULONG Floor, + _In_ ULONG Ceiling) +{ + /* Sanity checks */ + ASSERT(Floor < Ceiling); + ASSERT(Ceiling <= 100); + + /* Update the progress bar state */ + InbvProgressState.Floor = Floor * 100; + InbvProgressState.Ceiling = Ceiling * 100; + InbvProgressState.Bias = Ceiling - Floor; +} + +/** + * @brief + * Gives some progress feedback, without specifying any explicit number + * of progress steps or percentage. + * The corresponding percentage is derived from the progress indicator's + * current count, capped to the number of expected calls to be made to + * this function (default: 25, see @b InbvProgressIndicator.Expected). + * + * @return None. + **/ +CODE_SEG("INIT") +VOID +NTAPI +InbvIndicateProgress(VOID) +{ + ULONG Percentage; + + /* Increase progress */ + InbvProgressIndicator.Count++; + + /* Compute the new percentage - Don't go over 100% */ + Percentage = 100 * InbvProgressIndicator.Count / + InbvProgressIndicator.Expected; + Percentage = min(Percentage, 99); + + if (Percentage != InbvProgressIndicator.Percentage) + { + /* Percentage has changed, update the progress bar */ + InbvProgressIndicator.Percentage = Percentage; + InbvUpdateProgressBar(Percentage); + } +} + +PUCHAR +NTAPI +InbvGetResourceAddress(IN ULONG ResourceNumber) +{ + /* Validate the resource number */ + if (ResourceNumber > ResourceCount) return NULL; + + /* Return the address */ + 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; + + /* 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; + } + Status = RtlUnicodeStringToOemString(&OemString, &CapturedString, FALSE); + if (!NT_SUCCESS(Status)) + { + ExFreePoolWithTag(OemString.Buffer, TAG_OSTR); + goto Quit; + } + + /* Display the string */ + InbvDisplayString(OemString.Buffer); + + /* 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, BV_COLOR_BLACK); + } + 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 = SCREEN_WIDTH; + Index %= Total; + + /* Right part */ + VidBufferToScreenBlt(RotLineBuffer, Index, SCREEN_HEIGHT-6, SCREEN_WIDTH - Index, 6, SCREEN_WIDTH); + if (Index > 0) + { + /* Left part */ + VidBufferToScreenBlt(RotLineBuffer + (SCREEN_WIDTH - Index) / 2, 0, SCREEN_HEIGHT-6, Index - 2, 6, SCREEN_WIDTH); + } + Index += 32; + } + + InbvReleaseLock(); + } + + PsTerminateSystemThread(STATUS_SUCCESS); +} + +CODE_SEG("INIT") +VOID +NTAPI +InbvRotBarInit(VOID) +{ + PltRotBarStatus = RBS_FADEIN; + /* Perform other initialization if needed */ +} +#endif + +CODE_SEG("INIT") +VOID +NTAPI +DisplayBootBitmap(IN BOOLEAN TextMode) +{ + PVOID BootCopy = NULL, BootProgress = NULL, BootLogo = NULL, Header = NULL, Footer = NULL; + +#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 REACTOS_SKUS + PVOID Text = NULL; +#endif + +#ifdef INBV_ROTBAR_IMPLEMENTED + /* Check if the animation thread has already been created */ + if (RotBarThreadActive) + { + /* Yes, just reset the progress bar but keep the thread alive */ + InbvAcquireLock(); + RotBarSelection = RB_UNSPECIFIED; + InbvReleaseLock(); + } +#endif + + ShowProgressBar = FALSE; + + /* Check if this is text mode */ + if (TextMode) + { + /* + * Make the kernel resource section temporarily writable, + * as we are going to change the bitmaps' palette in place. + */ + MmChangeKernelResourceSectionProtection(MM_READWRITE); + + /* Check the type of the OS: workstation or server */ + if (SharedUserData->NtProductType == NtProductWinNt) + { + /* Workstation; set colors */ + InbvSetTextColor(BV_COLOR_WHITE); + InbvSolidColorFill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_DARK_GRAY); + InbvSolidColorFill(0, VID_FOOTER_BG_TOP, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_RED); + + /* Get resources */ + Header = InbvGetResourceAddress(IDB_WKSTA_HEADER); + Footer = InbvGetResourceAddress(IDB_WKSTA_FOOTER); + } + else + { + /* Server; set colors */ + InbvSetTextColor(BV_COLOR_LIGHT_CYAN); + InbvSolidColorFill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_CYAN); + InbvSolidColorFill(0, VID_FOOTER_BG_TOP, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_RED); + + /* Get resources */ + Header = InbvGetResourceAddress(IDB_SERVER_HEADER); + Footer = InbvGetResourceAddress(IDB_SERVER_FOOTER); + } + + /* Set the scrolling region */ + InbvSetScrollRegion(VID_SCROLL_AREA_LEFT, VID_SCROLL_AREA_TOP, + VID_SCROLL_AREA_RIGHT, VID_SCROLL_AREA_BOTTOM); + + /* Make sure we have resources */ + if (Header && Footer) + { + /* BitBlt them on the screen */ + BitBltAligned(Footer, + TRUE, + AL_HORIZONTAL_CENTER, + AL_VERTICAL_BOTTOM, + 0, 0, 0, 59); + BitBltAligned(Header, + FALSE, + AL_HORIZONTAL_CENTER, + AL_VERTICAL_TOP, + 0, 0, 0, 0); + } + + /* Restore the kernel resource section protection to be read-only */ + MmChangeKernelResourceSectionProtection(MM_READONLY); + } + else + { + /* Is the boot driver installed? */ + if (!InbvBootDriverInstalled) return; + + /* + * Make the kernel resource section temporarily writable, + * as we are going to change the bitmaps' palette in place. + */ + MmChangeKernelResourceSectionProtection(MM_READWRITE); + + /* Load boot screen logo */ + BootLogo = InbvGetResourceAddress(IDB_LOGO_DEFAULT); + +#ifdef REACTOS_SKUS + Text = NULL; + if (SharedUserData->NtProductType == NtProductWinNt) + { +#ifdef INBV_ROTBAR_IMPLEMENTED + /* Workstation product, use appropriate status bar color */ + Bar = InbvGetResourceAddress(IDB_BAR_WKSTA); +#endif + } + else + { + /* Display correct branding based on server suite */ + if (ExVerifySuite(StorageServer)) + { + /* Storage Server Edition */ + Text = InbvGetResourceAddress(IDB_STORAGE_SERVER); + } + else if (ExVerifySuite(ComputeServer)) + { + /* Compute Cluster Edition */ + Text = InbvGetResourceAddress(IDB_CLUSTER_SERVER); + } + else + { + /* Normal edition */ + Text = InbvGetResourceAddress(IDB_SERVER_LOGO); + } + +#ifdef INBV_ROTBAR_IMPLEMENTED + /* Server product, use appropriate status bar color */ + Bar = InbvGetResourceAddress(IDB_BAR_DEFAULT); +#endif + } +#else + /* Use default status bar */ + Bar = InbvGetResourceAddress(IDB_BAR_WKSTA); +#endif + + /* Make sure we have a logo */ + if (BootLogo) + { + /* Save the main image palette for implementing the fade-in effect */ + PBITMAPINFOHEADER BitmapInfoHeader = BootLogo; + LPRGBQUAD Palette = (LPRGBQUAD)((PUCHAR)BootLogo + BitmapInfoHeader->biSize); + RtlCopyMemory(MainPalette, Palette, sizeof(MainPalette)); + + /* Draw the logo at the center of the screen */ + BitBltAligned(BootLogo, + TRUE, + AL_HORIZONTAL_CENTER, + AL_VERTICAL_CENTER, + 0, 0, 0, 34); + +#ifdef INBV_ROTBAR_IMPLEMENTED + /* Choose progress bar */ + TempRotBarSelection = ROT_BAR_DEFAULT_MODE; +#endif + + /* Set progress bar coordinates and display it */ + InbvSetProgressBarCoordinates(VID_PROGRESS_BAR_LEFT, + VID_PROGRESS_BAR_TOP, + VID_PROGRESS_BAR_WIDTH, + VID_PROGRESS_BAR_HEIGHT); + +#ifdef REACTOS_SKUS + /* Check for non-workstation products */ + if (SharedUserData->NtProductType != NtProductWinNt) + { + /* Overwrite part of the logo for a server product */ + InbvScreenToBufferBlt(Buffer, VID_SKU_SAVE_AREA_LEFT, + VID_SKU_SAVE_AREA_TOP, 7, 7, 8); + InbvSolidColorFill(VID_SKU_AREA_LEFT, VID_SKU_AREA_TOP, + VID_SKU_AREA_RIGHT, VID_SKU_AREA_BOTTOM, BV_COLOR_BLACK); + InbvBufferToScreenBlt(Buffer, VID_SKU_SAVE_AREA_LEFT, + VID_SKU_SAVE_AREA_TOP, 7, 7, 8); + + /* In setup mode, you haven't selected a SKU yet */ + if (ExpInTextModeSetup) Text = NULL; + } +#endif + } + + /* Load and draw progress bar bitmap */ + BootProgress = InbvGetResourceAddress(IDB_PROGRESS_BAR); + BitBltAligned(BootProgress, + TRUE, + AL_HORIZONTAL_CENTER, + AL_VERTICAL_CENTER, + 0, 118, 0, 0); + + /* Load and draw copyright text bitmap */ + BootCopy = InbvGetResourceAddress(IDB_COPYRIGHT); + BitBltAligned(BootCopy, + TRUE, + AL_HORIZONTAL_LEFT, + AL_VERTICAL_BOTTOM, + 22, 0, 0, 20); + +#ifdef REACTOS_SKUS + /* Draw the SKU text if it exits */ + if (Text) + BitBltPalette(Text, TRUE, VID_SKU_TEXT_LEFT, VID_SKU_TEXT_TOP); +#endif + +#ifdef INBV_ROTBAR_IMPLEMENTED + if ((TempRotBarSelection == RB_SQUARE_CELLS) && Bar) + { + /* Save previous screen pixels to buffer */ + InbvScreenToBufferBlt(Buffer, 0, 0, 22, 9, 24); + /* Draw the progress bar bit */ + BitBltPalette(Bar, TRUE, 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 */ + BitBltPalette(LineBmp, TRUE, 0, SCREEN_HEIGHT-6); + InbvScreenToBufferBlt(RotLineBuffer, 0, SCREEN_HEIGHT-6, SCREEN_WIDTH, 6, SCREEN_WIDTH); + } + } + else + { + /* Hide the simple progress bar if not used */ + ShowProgressBar = FALSE; + } +#endif + + /* Restore the kernel resource section protection to be read-only */ + MmChangeKernelResourceSectionProtection(MM_READONLY); + + /* 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); + } + +#ifdef INBV_ROTBAR_IMPLEMENTED + /* Do we have the animation thread? */ + if (RotBarThreadActive) + { + /* We do, initialize the progress bar */ + InbvAcquireLock(); + RotBarSelection = TempRotBarSelection; + InbvRotBarInit(); + InbvReleaseLock(); + } +#endif +} + +CODE_SEG("INIT") +VOID +NTAPI +DisplayFilter(PCHAR *String) +{ + /* Windows hack to skip first dots */ + static BOOLEAN DotHack = TRUE; + + /* If "." is given set *String to empty string */ + if (DotHack && strcmp(*String, ".") == 0) + *String = ""; + + if (**String) + { + /* Remove the filter */ + InbvInstallDisplayStringFilter(NULL); + + DotHack = FALSE; + + /* Draw text screen */ + DisplayBootBitmap(TRUE); + } +} + +CODE_SEG("INIT") +VOID +NTAPI +FinalizeBootLogo(VOID) +{ + /* Acquire lock and check the display state */ + InbvAcquireLock(); + if (InbvGetDisplayState() == INBV_DISPLAY_STATE_OWNED) + { + /* Clear the screen */ + VidSolidColorFill(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1, BV_COLOR_BLACK); + } + + /* Reset progress bar and lock */ +#ifdef INBV_ROTBAR_IMPLEMENTED + PltRotBarStatus = RBS_STOP_ANIMATE; + RotBarThreadActive = FALSE; +#endif + InbvReleaseLock(); +}