diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bcc7a68b38..02697a671a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -622,6 +622,7 @@ file( GLOB HEADER_FILES common/platform/win32/*.h common/models/*.h common/textures/*.h + common/startscreen/*.h common/textures/hires/hqnx/*.h common/textures/hires/hqnx_asm/*.h common/textures/hires/xbr/*.h @@ -1042,6 +1043,12 @@ set (PCH_SOURCES common/2d/wipe.cpp common/thirdparty/gain_analysis.cpp common/thirdparty/sfmt/SFMT.cpp + common/startscreen/startscreen.cpp + common/startscreen/startscreen_heretic.cpp + common/startscreen/startscreen_hexen.cpp + common/startscreen/startscreen_strife.cpp + common/startscreen/startscreen_generic.cpp + common/startscreen/endoom.cpp common/fonts/singlelumpfont.cpp common/fonts/singlepicfont.cpp common/fonts/specialfont.cpp @@ -1313,6 +1320,7 @@ include_directories( . common/statusbar common/fonts common/objects + common/startscreen common/rendering common/rendering/hwrenderer/data common/rendering/gl_load diff --git a/src/common/startscreen/endoom.cpp b/src/common/startscreen/endoom.cpp new file mode 100644 index 0000000000..090c90dbcb --- /dev/null +++ b/src/common/startscreen/endoom.cpp @@ -0,0 +1,201 @@ + +/* +** endoom.cpp +** Handles the startup screen. +** +**--------------------------------------------------------------------------- +** Copyright 2006-2007 Randy Heit +** Copyright 2006-2022 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include "startscreen.h" +#include "cmdlib.h" + +#include "i_system.h" +#include "gstrings.h" +#include "filesystem.h" +#include "m_argv.h" +#include "engineerrors.h" +#include "s_music.h" +#include "printf.h" +#include "startupinfo.h" +#include "i_interface.h" +#include "texturemanager.h" +#include "c_cvars.h" +#include "i_time.h" +#include "g_input.h" +#include "d_eventbase.h" + +// MACROS ------------------------------------------------------------------ + +// How many ms elapse between blinking text flips. On a standard VGA +// adapter, the characters are on for 16 frames and then off for another 16. +// The number here therefore corresponds roughly to the blink rate on a +// 60 Hz display. +#define BLINK_PERIOD 267 + + +// TYPES ------------------------------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- +#if 0 +CUSTOM_CVAR(Int, showendoom, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) +{ + if (self < 0) self = 0; + else if (self > 2) self=2; +} +#endif +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// CODE -------------------------------------------------------------------- + +class FEndoomScreen : public FStartScreen +{ + uint64_t lastUpdateTime; + bool blinkstate = false; + bool blinking = true; + uint8_t endoom_screen[4000]; + +public: + FEndoomScreen(int); + void Update(); +}; + + +//========================================================================== +// +// FHereticStartScreen Constructor +// +// Shows the Heretic startup screen. If the screen doesn't appear to be +// valid, it returns a failure code in hr. +// +// The loading screen is an 80x25 text screen with character data and +// attributes intermixed, which means it must be exactly 4000 bytes long. +// +//========================================================================== + +FEndoomScreen::FEndoomScreen(int loading_lump) + : FStartScreen(0) +{ + fileSystem.ReadFile(loading_lump, endoom_screen); + + // Draw the loading screen to a bitmap. + StartupBitmap.Create(80 * 8, 26 * 16); // line 26 is for our own 'press any key to quit' message. + DrawTextScreen(StartupBitmap, endoom_screen); + ClearBlock(StartupBitmap, {0, 0, 0, 255}, 0, 25*16, 640, 16); + DrawString(StartupBitmap, 0, 25, GStrings("TXT_QUITENDOOM"), { 128, 128, 128 ,255}, { 0, 0, 0, 255}); + lastUpdateTime = I_msTime(); + + // Does this screen need blinking? + for (int i = 0; i < 80*25; ++i) + { + if (endoom_screen[1+i*2] & 0x80) + { + blinking = true; + break; + } + } +} + +void FEndoomScreen::Update() +{ + if (blinking && I_msTime() > lastUpdateTime + BLINK_PERIOD) + { + lastUpdateTime = I_msTime(); + UpdateTextBlink (StartupBitmap, endoom_screen, blinkstate); + blinkstate = !blinkstate; + StartupTexture->CleanHardwareData(); + Render(true); + } +} + + +#if 0 // this part is not ready yet. +//========================================================================== +// +// ST_Endoom +// +// Shows an ENDOOM text screen +// +//========================================================================== + +int RunEndoom() +{ + if (showendoom == 0 || endoomName.Len() == 0) + { + return 0; + } + + int endoom_lump = fileSystem.CheckNumForFullName (endoomName, true); + + if (endoom_lump < 0 || fileSystem.FileLength (endoom_lump) != 4000) + { + return 0; + } + + if (fileSystem.GetFileContainer(endoom_lump) == fileSystem.GetMaxIwadNum() && showendoom == 2) + { + // showendoom==2 means to show only lumps from PWADs. + return 0; + } + + S_StopMusic(true); + auto endoom = new FEndoomScreen(endoom_lump); + endoom->Render(true); + + while(true) + { + I_GetEvent(); + endoom->Update(); + while (eventtail != eventhead) + { + event_t *ev = &events[eventtail]; + eventtail = (eventtail + 1) & (MAXEVENTS - 1); + + if (ev->type == EV_KeyDown || ev->type == EV_KeyUp) + { + return 0; + } + if (ev->type == EV_GUI_Event && (ev->subtype == EV_GUI_KeyDown || ev->subtype == EV_GUI_LButtonDown || ev->subtype == EV_GUI_RButtonDown || ev->subtype == EV_GUI_MButtonDown)) + { + return 0; + } + } + } + return 0; +} + +void ST_Endoom() +{ + int code = RunEndoom(); + throw CExitEvent(code); +} +#endif \ No newline at end of file diff --git a/src/common/startscreen/startscreen.cpp b/src/common/startscreen/startscreen.cpp new file mode 100644 index 0000000000..e69aa3f236 --- /dev/null +++ b/src/common/startscreen/startscreen.cpp @@ -0,0 +1,715 @@ +/* +** st_start.cpp +** Handles the startup screen. +** +**--------------------------------------------------------------------------- +** Copyright 2006-2007 Randy Heit +** Copyright 2006-2022 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "startscreen.h" +#include "filesystem.h" +#include "palutil.h" +#include "v_font.h" +#include "i_interface.h" +#include "startupinfo.h" +#include "m_argv.h" +#include "engineerrors.h" +#include "utf8.h" +#include "gstrings.h" +#include "printf.h" +#include "i_time.h" +#include "v_video.h" +#include "v_draw.h" +#include "g_input.h" +#include "texturemanager.h" +#include "gi.h" + +// Text mode color values +enum{ + LO = 85, + MD = 170, + HI = 255, +}; + +const RgbQuad TextModePalette[16] = +{ + { 0, 0, 0, 255 }, // 0 black + { MD, 0, 0, 255 }, // 1 blue + { 0, MD, 0, 255 }, // 2 green + { MD, MD, 0, 255 }, // 3 cyan + { 0, 0, MD, 255 }, // 4 red + { MD, 0, MD, 255 }, // 5 magenta + { 0, LO, MD, 255 }, // 6 brown + { MD, MD, MD, 255 }, // 7 light gray + + { LO, LO, LO, 255 }, // 8 dark gray + { HI, LO, LO, 255 }, // 9 light blue + { LO, HI, LO, 255 }, // A light green + { HI, HI, LO, 255 }, // B light cyan + { LO, LO, HI, 255 }, // C light red + { HI, LO, HI, 255 }, // D light magenta + { LO, HI, HI, 255 }, // E yellow + { HI, HI, HI, 255 }, // F white +}; + +static const uint16_t IBM437ToUnicode[] = { + 0x0000, //#NULL + 0x0001, //#START OF HEADING + 0x0002, //#START OF TEXT + 0x0003, //#END OF TEXT + 0x0004, //#END OF TRANSMISSION + 0x0005, //#ENQUIRY + 0x0006, //#ACKNOWLEDGE + 0x0007, //#BELL + 0x0008, //#BACKSPACE + 0x0009, //#HORIZONTAL TABULATION + 0x000a, //#LINE FEED + 0x000b, //#VERTICAL TABULATION + 0x000c, //#FORM FEED + 0x000d, //#CARRIAGE RETURN + 0x000e, //#SHIFT OUT + 0x000f, //#SHIFT IN + 0x0010, //#DATA LINK ESCAPE + 0x0011, //#DEVICE CONTROL ONE + 0x0012, //#DEVICE CONTROL TWO + 0x0013, //#DEVICE CONTROL THREE + 0x0014, //#DEVICE CONTROL FOUR + 0x0015, //#NEGATIVE ACKNOWLEDGE + 0x0016, //#SYNCHRONOUS IDLE + 0x0017, //#END OF TRANSMISSION BLOCK + 0x0018, //#CANCEL + 0x0019, //#END OF MEDIUM + 0x001a, //#SUBSTITUTE + 0x001b, //#ESCAPE + 0x001c, //#FILE SEPARATOR + 0x001d, //#GROUP SEPARATOR + 0x001e, //#RECORD SEPARATOR + 0x001f, //#UNIT SEPARATOR + 0x0020, //#SPACE + 0x0021, //#EXCLAMATION MARK + 0x0022, //#QUOTATION MARK + 0x0023, //#NUMBER SIGN + 0x0024, //#DOLLAR SIGN + 0x0025, //#PERCENT SIGN + 0x0026, //#AMPERSAND + 0x0027, //#APOSTROPHE + 0x0028, //#LEFT PARENTHESIS + 0x0029, //#RIGHT PARENTHESIS + 0x002a, //#ASTERISK + 0x002b, //#PLUS SIGN + 0x002c, //#COMMA + 0x002d, //#HYPHEN-MINUS + 0x002e, //#FULL STOP + 0x002f, //#SOLIDUS + 0x0030, //#DIGIT ZERO + 0x0031, //#DIGIT ONE + 0x0032, //#DIGIT TWO + 0x0033, //#DIGIT THREE + 0x0034, //#DIGIT FOUR + 0x0035, //#DIGIT FIVE + 0x0036, //#DIGIT SIX + 0x0037, //#DIGIT SEVEN + 0x0038, //#DIGIT EIGHT + 0x0039, //#DIGIT NINE + 0x003a, //#COLON + 0x003b, //#SEMICOLON + 0x003c, //#LESS-THAN SIGN + 0x003d, //#EQUALS SIGN + 0x003e, //#GREATER-THAN SIGN + 0x003f, //#QUESTION MARK + 0x0040, //#COMMERCIAL AT + 0x0041, //#LATIN CAPITAL LETTER A + 0x0042, //#LATIN CAPITAL LETTER B + 0x0043, //#LATIN CAPITAL LETTER C + 0x0044, //#LATIN CAPITAL LETTER D + 0x0045, //#LATIN CAPITAL LETTER E + 0x0046, //#LATIN CAPITAL LETTER F + 0x0047, //#LATIN CAPITAL LETTER G + 0x0048, //#LATIN CAPITAL LETTER H + 0x0049, //#LATIN CAPITAL LETTER I + 0x004a, //#LATIN CAPITAL LETTER J + 0x004b, //#LATIN CAPITAL LETTER K + 0x004c, //#LATIN CAPITAL LETTER L + 0x004d, //#LATIN CAPITAL LETTER M + 0x004e, //#LATIN CAPITAL LETTER N + 0x004f, //#LATIN CAPITAL LETTER O + 0x0050, //#LATIN CAPITAL LETTER P + 0x0051, //#LATIN CAPITAL LETTER Q + 0x0052, //#LATIN CAPITAL LETTER R + 0x0053, //#LATIN CAPITAL LETTER S + 0x0054, //#LATIN CAPITAL LETTER T + 0x0055, //#LATIN CAPITAL LETTER U + 0x0056, //#LATIN CAPITAL LETTER V + 0x0057, //#LATIN CAPITAL LETTER W + 0x0058, //#LATIN CAPITAL LETTER X + 0x0059, //#LATIN CAPITAL LETTER Y + 0x005a, //#LATIN CAPITAL LETTER Z + 0x005b, //#LEFT SQUARE BRACKET + 0x005c, //#REVERSE SOLIDUS + 0x005d, //#RIGHT SQUARE BRACKET + 0x005e, //#CIRCUMFLEX ACCENT + 0x005f, //#LOW LINE + 0x0060, //#GRAVE ACCENT + 0x0061, //#LATIN SMALL LETTER A + 0x0062, //#LATIN SMALL LETTER B + 0x0063, //#LATIN SMALL LETTER C + 0x0064, //#LATIN SMALL LETTER D + 0x0065, //#LATIN SMALL LETTER E + 0x0066, //#LATIN SMALL LETTER F + 0x0067, //#LATIN SMALL LETTER G + 0x0068, //#LATIN SMALL LETTER H + 0x0069, //#LATIN SMALL LETTER I + 0x006a, //#LATIN SMALL LETTER J + 0x006b, //#LATIN SMALL LETTER K + 0x006c, //#LATIN SMALL LETTER L + 0x006d, //#LATIN SMALL LETTER M + 0x006e, //#LATIN SMALL LETTER N + 0x006f, //#LATIN SMALL LETTER O + 0x0070, //#LATIN SMALL LETTER P + 0x0071, //#LATIN SMALL LETTER Q + 0x0072, //#LATIN SMALL LETTER R + 0x0073, //#LATIN SMALL LETTER S + 0x0074, //#LATIN SMALL LETTER T + 0x0075, //#LATIN SMALL LETTER U + 0x0076, //#LATIN SMALL LETTER V + 0x0077, //#LATIN SMALL LETTER W + 0x0078, //#LATIN SMALL LETTER X + 0x0079, //#LATIN SMALL LETTER Y + 0x007a, //#LATIN SMALL LETTER Z + 0x007b, //#LEFT CURLY BRACKET + 0x007c, //#VERTICAL LINE + 0x007d, //#RIGHT CURLY BRACKET + 0x007e, //#TILDE + 0x007f, //#DELETE + 0x00c7, //#LATIN CAPITAL LETTER C WITH CEDILLA + 0x00fc, //#LATIN SMALL LETTER U WITH DIAERESIS + 0x00e9, //#LATIN SMALL LETTER E WITH ACUTE + 0x00e2, //#LATIN SMALL LETTER A WITH CIRCUMFLEX + 0x00e4, //#LATIN SMALL LETTER A WITH DIAERESIS + 0x00e0, //#LATIN SMALL LETTER A WITH GRAVE + 0x00e5, //#LATIN SMALL LETTER A WITH RING ABOVE + 0x00e7, //#LATIN SMALL LETTER C WITH CEDILLA + 0x00ea, //#LATIN SMALL LETTER E WITH CIRCUMFLEX + 0x00eb, //#LATIN SMALL LETTER E WITH DIAERESIS + 0x00e8, //#LATIN SMALL LETTER E WITH GRAVE + 0x00ef, //#LATIN SMALL LETTER I WITH DIAERESIS + 0x00ee, //#LATIN SMALL LETTER I WITH CIRCUMFLEX + 0x00ec, //#LATIN SMALL LETTER I WITH GRAVE + 0x00c4, //#LATIN CAPITAL LETTER A WITH DIAERESIS + 0x00c5, //#LATIN CAPITAL LETTER A WITH RING ABOVE + 0x00c9, //#LATIN CAPITAL LETTER E WITH ACUTE + 0x00e6, //#LATIN SMALL LIGATURE AE + 0x00c6, //#LATIN CAPITAL LIGATURE AE + 0x00f4, //#LATIN SMALL LETTER O WITH CIRCUMFLEX + 0x00f6, //#LATIN SMALL LETTER O WITH DIAERESIS + 0x00f2, //#LATIN SMALL LETTER O WITH GRAVE + 0x00fb, //#LATIN SMALL LETTER U WITH CIRCUMFLEX + 0x00f9, //#LATIN SMALL LETTER U WITH GRAVE + 0x00ff, //#LATIN SMALL LETTER Y WITH DIAERESIS + 0x00d6, //#LATIN CAPITAL LETTER O WITH DIAERESIS + 0x00dc, //#LATIN CAPITAL LETTER U WITH DIAERESIS + 0x00a2, //#CENT SIGN + 0x00a3, //#POUND SIGN + 0x00a5, //#YEN SIGN + 0x20a7, //#PESETA SIGN + 0x0192, //#LATIN SMALL LETTER F WITH HOOK + 0x00e1, //#LATIN SMALL LETTER A WITH ACUTE + 0x00ed, //#LATIN SMALL LETTER I WITH ACUTE + 0x00f3, //#LATIN SMALL LETTER O WITH ACUTE + 0x00fa, //#LATIN SMALL LETTER U WITH ACUTE + 0x00f1, //#LATIN SMALL LETTER N WITH TILDE + 0x00d1, //#LATIN CAPITAL LETTER N WITH TILDE + 0x00aa, //#FEMININE ORDINAL INDICATOR + 0x00ba, //#MASCULINE ORDINAL INDICATOR + 0x00bf, //#INVERTED QUESTION MARK + 0x2310, //#REVERSED NOT SIGN + 0x00ac, //#NOT SIGN + 0x00bd, //#VULGAR FRACTION ONE HALF + 0x00bc, //#VULGAR FRACTION ONE QUARTER + 0x00a1, //#INVERTED EXCLAMATION MARK + 0x00ab, //#LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + 0x00bb, //#RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + 0x2591, //#LIGHT SHADE + 0x2592, //#MEDIUM SHADE + 0x2593, //#DARK SHADE + 0x2502, //#BOX DRAWINGS LIGHT VERTICAL + 0x2524, //#BOX DRAWINGS LIGHT VERTICAL AND LEFT + 0x2561, //#BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE + 0x2562, //#BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE + 0x2556, //#BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE + 0x2555, //#BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE + 0x2563, //#BOX DRAWINGS DOUBLE VERTICAL AND LEFT + 0x2551, //#BOX DRAWINGS DOUBLE VERTICAL + 0x2557, //#BOX DRAWINGS DOUBLE DOWN AND LEFT + 0x255d, //#BOX DRAWINGS DOUBLE UP AND LEFT + 0x255c, //#BOX DRAWINGS UP DOUBLE AND LEFT SINGLE + 0x255b, //#BOX DRAWINGS UP SINGLE AND LEFT DOUBLE + 0x2510, //#BOX DRAWINGS LIGHT DOWN AND LEFT + 0x2514, //#BOX DRAWINGS LIGHT UP AND RIGHT + 0x2534, //#BOX DRAWINGS LIGHT UP AND HORIZONTAL + 0x252c, //#BOX DRAWINGS LIGHT DOWN AND HORIZONTAL + 0x251c, //#BOX DRAWINGS LIGHT VERTICAL AND RIGHT + 0x2500, //#BOX DRAWINGS LIGHT HORIZONTAL + 0x253c, //#BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL + 0x255e, //#BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE + 0x255f, //#BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE + 0x255a, //#BOX DRAWINGS DOUBLE UP AND RIGHT + 0x2554, //#BOX DRAWINGS DOUBLE DOWN AND RIGHT + 0x2569, //#BOX DRAWINGS DOUBLE UP AND HORIZONTAL + 0x2566, //#BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL + 0x2560, //#BOX DRAWINGS DOUBLE VERTICAL AND RIGHT + 0x2550, //#BOX DRAWINGS DOUBLE HORIZONTAL + 0x256c, //#BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL + 0x2567, //#BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE + 0x2568, //#BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE + 0x2564, //#BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE + 0x2565, //#BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE + 0x2559, //#BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE + 0x2558, //#BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE + 0x2552, //#BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE + 0x2553, //#BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE + 0x256b, //#BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE + 0x256a, //#BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE + 0x2518, //#BOX DRAWINGS LIGHT UP AND LEFT + 0x250c, //#BOX DRAWINGS LIGHT DOWN AND RIGHT + 0x2588, //#FULL BLOCK + 0x2584, //#LOWER HALF BLOCK + 0x258c, //#LEFT HALF BLOCK + 0x2590, //#RIGHT HALF BLOCK + 0x2580, //#UPPER HALF BLOCK + 0x03b1, //#GREEK SMALL LETTER ALPHA + 0x00df, //#LATIN SMALL LETTER SHARP S + 0x0393, //#GREEK CAPITAL LETTER GAMMA + 0x03c0, //#GREEK SMALL LETTER PI + 0x03a3, //#GREEK CAPITAL LETTER SIGMA + 0x03c3, //#GREEK SMALL LETTER SIGMA + 0x00b5, //#MICRO SIGN + 0x03c4, //#GREEK SMALL LETTER TAU + 0x03a6, //#GREEK CAPITAL LETTER PHI + 0x0398, //#GREEK CAPITAL LETTER THETA + 0x03a9, //#GREEK CAPITAL LETTER OMEGA + 0x03b4, //#GREEK SMALL LETTER DELTA + 0x221e, //#INFINITY + 0x03c6, //#GREEK SMALL LETTER PHI + 0x03b5, //#GREEK SMALL LETTER EPSILON + 0x2229, //#INTERSECTION + 0x2261, //#IDENTICAL TO + 0x00b1, //#PLUS-MINUS SIGN + 0x2265, //#GREATER-THAN OR EQUAL TO + 0x2264, //#LESS-THAN OR EQUAL TO + 0x2320, //#TOP HALF INTEGRAL + 0x2321, //#BOTTOM HALF INTEGRAL + 0x00f7, //#DIVISION SIGN + 0x2248, //#ALMOST EQUAL TO + 0x00b0, //#DEGREE SIGN + 0x2219, //#BULLET OPERATOR + 0x00b7, //#MIDDLE DOT + 0x221a, //#SQUARE ROOT + 0x207f, //#SUPERSCRIPT LATIN SMALL LETTER N + 0x00b2, //#SUPERSCRIPT TWO + 0x25a0, //#BLACK SQUARE + 0x00a0, //#NO-BREAK SPACE +}; + +FStartScreen* CreateHexenStartScreen(int max_progress); +FStartScreen* CreateHereticStartScreen(int max_progress); +FStartScreen* CreateStrifeStartScreen(int max_progress); +FStartScreen* CreateGenericStartScreen(int max_progress); + + +FStartScreen* GetGameStartScreen(int max_progress) +{ + if (!Args->CheckParm("-nostartup")) + { + try + { + if (GameStartupInfo.Type == FStartupInfo::HexenStartup || (gameinfo.gametype == GAME_Hexen && GameStartupInfo.Type == FStartupInfo::DefaultStartup)) + { + return CreateHexenStartScreen(max_progress); + } + else if (GameStartupInfo.Type == FStartupInfo::HereticStartup || (gameinfo.gametype == GAME_Heretic && GameStartupInfo.Type == FStartupInfo::DefaultStartup)) + { + return CreateHereticStartScreen(max_progress); + } + else if (GameStartupInfo.Type == FStartupInfo::StrifeStartup || (gameinfo.gametype == GAME_Strife && GameStartupInfo.Type == FStartupInfo::DefaultStartup)) + { + return CreateStrifeStartScreen(max_progress); + } + } + catch(const CRecoverableError& err) + { + Printf("Error creating start screen: %s\n", err.what()); + // fall through to the generic startup screen + } + } + return CreateGenericStartScreen(max_progress); +} + +//========================================================================== +// +// ST_Util_ClearBlock +// +//========================================================================== + +void FStartScreen::ClearBlock(FBitmap& bitmap_info, RgbQuad fill, int x, int y, int bytewidth, int height) +{ + int destpitch = bitmap_info.GetWidth(); + auto dest = (RgbQuad*)(bitmap_info.GetPixels()) + x + y * destpitch; + + while (height > 0) + { + for(int i = 0; i < bytewidth; i++) + { + dest[i] = fill; + } + dest += destpitch; + height--; + } +} + +//========================================================================== +// +// ST_Util_DrawTextScreen +// +// Draws the text screen to the bitmap. The bitmap must be the proper size +// for the font. +// +//========================================================================== + +void FStartScreen::DrawTextScreen(FBitmap& bitmap_info, const uint8_t* text_screen) +{ + int x, y; + + for (y = 0; y < 25; ++y) + { + for (x = 0; x < 80; ++x) + { + DrawChar(bitmap_info, x, y, IBM437ToUnicode[text_screen[0]], text_screen[1]); + text_screen += 2; + } + } +} + +//========================================================================== +// +// ST_Util_DrawChar +// +// Draws a character on the bitmap. X and Y specify the character cell, +// and fg and bg are 4-bit colors. +// +//========================================================================== +uint8_t* GetHexChar(int codepoint); + +int FStartScreen::DrawChar(FBitmap& screen, double x, double y, unsigned charnum, RgbQuad fg, RgbQuad bg) +{ + if (x < 0 || y < 0 || y >= screen.GetHeight() - 16) return 1; + static const uint8_t space[17] = { 16 }; + const RgbQuad color_array[4] = { bg, fg }; + const uint8_t* src = GetHexChar(charnum); + if (!src) src = space; + int size = (*src++) == 32? 2 : 1; + if (x >= (screen.GetWidth() >> 3) - size) return size; + int pitch = screen.GetWidth(); + RgbQuad* dest = (RgbQuad*)screen.GetPixels() + int(x * 8) + int(y * 16) * pitch; + + for (y = 0; y <16; ++y) + { + uint8_t srcbyte = *src++; + + dest[0] = color_array[(srcbyte >> 7) & 1]; + dest[1] = color_array[(srcbyte >> 6) & 1]; + dest[2] = color_array[(srcbyte >> 5) & 1]; + dest[3] = color_array[(srcbyte >> 4) & 1]; + dest[4] = color_array[(srcbyte >> 3) & 1]; + dest[5] = color_array[(srcbyte >> 2) & 1]; + dest[6] = color_array[(srcbyte >> 1) & 1]; + dest[7] = color_array[(srcbyte) & 1]; + if (size == 16) + { + srcbyte = *src++; + + dest[8] = color_array[(srcbyte >> 7) & 1]; + dest[9] = color_array[(srcbyte >> 6) & 1]; + dest[10] = color_array[(srcbyte >> 5) & 1]; + dest[11] = color_array[(srcbyte >> 4) & 1]; + dest[12] = color_array[(srcbyte >> 3) & 1]; + dest[13] = color_array[(srcbyte >> 2) & 1]; + dest[14] = color_array[(srcbyte >> 1) & 1]; + dest[15] = color_array[(srcbyte) & 1]; + } + dest += pitch; + } + return size; +} + +int FStartScreen::DrawChar(FBitmap& screen, double x, double y, unsigned charnum, uint8_t attrib) +{ + const uint8_t bg = (attrib & 0x70) >> 4; + const uint8_t fg = attrib & 0x0F; + auto bgc = TextModePalette[bg], fgc = TextModePalette[fg]; + return DrawChar(screen, x, y, charnum, fgc, bgc); +} + +int FStartScreen::DrawString(FBitmap& screen, double x, double y, const char* text, RgbQuad fg, RgbQuad bg) +{ + double oldx = x; + auto str = (const uint8_t*)text; + while (auto chr = GetCharFromString(str)) + { + x += DrawChar(screen, x, y, chr, fg, bg); + } + return int(x - oldx); +} + +//========================================================================== +// +// SizeOfText +// +// returns width in pixels +// +//========================================================================== + +int FStartScreen::SizeOfText(const char* text) +{ + int len = 0; + const uint8_t* utext = (uint8_t*)text; + while(auto code = GetCharFromString(utext)) + { + const uint8_t* src = GetHexChar(code); + if (src && *src == 32) len += 2; + else len ++; + } + return len; +} + +//========================================================================== +// +// ST_Util_UpdateTextBlink +// +// Draws the parts of the text screen that blink to the bitmap. The bitmap +// must be the proper size for the font. +// +//========================================================================== + +void FStartScreen::UpdateTextBlink(FBitmap& bitmap_info, const uint8_t* text_screen, bool on) +{ + int x, y; + + for (y = 0; y < 25; ++y) + { + for (x = 0; x < 80; ++x) + { + if (text_screen[1] & 0x80) + { + DrawChar(bitmap_info, x, y, on ? IBM437ToUnicode[text_screen[0]] : ' ', text_screen[1]); + } + text_screen += 2; + } + } +} + +//========================================================================== +// +// ST_Sound +// +// plays a sound on the start screen +// +//========================================================================== + +void FStartScreen::ST_Sound(const char* sndname) +{ + if (sysCallbacks.PlayStartupSound) + sysCallbacks.PlayStartupSound(sndname); +} + +//========================================================================== +// +// CreateHeader +// +// creates a bitmap for the title header +// +//========================================================================== + +void FStartScreen::CreateHeader() +{ + HeaderBitmap.Create(StartupBitmap.GetWidth() * Scale, 2 * 16); + RgbQuad bcolor, fcolor; + bcolor.rgbRed = RPART(GameStartupInfo.BkColor); + bcolor.rgbGreen = GPART(GameStartupInfo.BkColor); + bcolor.rgbBlue = BPART(GameStartupInfo.BkColor); + bcolor.rgbReserved = 255; + fcolor.rgbRed = RPART(GameStartupInfo.FgColor); + fcolor.rgbGreen = GPART(GameStartupInfo.FgColor); + fcolor.rgbBlue = BPART(GameStartupInfo.FgColor); + fcolor.rgbReserved = 255; + ClearBlock(HeaderBitmap, bcolor, 0, 0, HeaderBitmap.GetWidth(), HeaderBitmap.GetHeight()); + int textlen = SizeOfText(GameStartupInfo.Name); + DrawString(HeaderBitmap, (HeaderBitmap.GetWidth() >> 4) - (textlen >> 1), 0.5, GameStartupInfo.Name, fcolor, bcolor); + NetBitmap.Create(StartupBitmap.GetWidth() * Scale, 16); +} + +//========================================================================== +// +// DrawNetStatus +// +// Draws network status into the last line of the startup screen. +// +//========================================================================== + +void FStartScreen::DrawNetStatus(int found, int total) +{ + RgbQuad black = { 0, 0, 0, 255 }; + RgbQuad gray = { 100, 100, 100, 255 }; + ClearBlock(NetBitmap, black, 0, NetBitmap.GetHeight() - 16, NetBitmap.GetWidth(), 16); + DrawString(NetBitmap, 0, 0, NetMessageString, gray, black); + char of[10]; + mysnprintf(of, 10, "%d/%d", found, total); + int siz = SizeOfText(of); + DrawString(NetBitmap, (NetBitmap.GetWidth() >> 3) - siz, 0, of, gray, black); +} + +//========================================================================== +// +// NetInit +// +// sets network status message +// +//========================================================================== + +bool FStartScreen::NetInit(const char* message, int numplayers) +{ + NetMaxPos = numplayers; + NetCurPos = 0; + NetMessageString.Format("%s %s", message, GStrings("TXT_NET_PRESSESC")); + NetProgress(1); // You always know about yourself + return true; +} + +//========================================================================== +// +// Progress +// +// advances the progress bar +// +//========================================================================== + +bool FStartScreen::DoProgress(int advance) +{ + CurPos = min(CurPos + advance, MaxPos); + return true; +} + +void FStartScreen::DoNetProgress(int count) +{ + if (count == 0) + { + NetCurPos++; + } + else if (count > 0) + { + NetCurPos = count; + } + NetTexture->CleanHardwareData(); +} + +bool FStartScreen::Progress(int advance) +{ + bool done = DoProgress(advance); + Render(); + return done; +} + +void FStartScreen::NetProgress(int count) +{ + DoNetProgress(count); + Render(); +} + +void FStartScreen::Render(bool force) +{ + auto nowtime = I_msTime(); + // Do not refresh too often. This function gets called a lot more frequently than the screen can update. + if (nowtime - screen->FrameTime > 100 || force) + { + screen->FrameTime = nowtime; + screen->BeginFrame(); + twod->ClearClipRect(); + I_GetEvent(); + ValidateTexture(); + float displaywidth; + float displayheight; + twod->Begin(screen->GetWidth(), screen->GetHeight()); + + // Weird shit moment: Vulkan does not render the screen clear if there isn't something textured in the buffer before it. + DrawTexture(twod, StartupTexture, 0, 0, TAG_END); + ClearRect(twod, 0, 0, twod->GetWidth(), twod->GetHeight(), GPalette.BlackIndex, 0); + + if (HeaderTexture) + { + displaywidth = HeaderTexture->GetDisplayWidth(); + displayheight = HeaderTexture->GetDisplayHeight() + StartupTexture->GetDisplayHeight(); + DrawTexture(twod, HeaderTexture, 0, 0, DTA_VirtualWidthF, displaywidth, DTA_VirtualHeightF, displayheight, TAG_END); + DrawTexture(twod, StartupTexture, 0, 32, DTA_VirtualWidthF, displaywidth, DTA_VirtualHeightF, displayheight, TAG_END); + if (NetMaxPos >= 0) DrawTexture(twod, NetTexture, 0, displayheight - 16, DTA_VirtualWidthF, displaywidth, DTA_VirtualHeightF, displayheight, TAG_END); + } + else + { + displaywidth = StartupTexture->GetDisplayWidth(); + displayheight = StartupTexture->GetDisplayHeight(); + DrawTexture(twod, StartupTexture, 0, 0, DTA_VirtualWidthF, displaywidth, DTA_VirtualHeightF, displayheight, TAG_END); + } + twod->End(); + screen->Update(); + twod->OnFrameDone(); + } +} + +FImageSource* CreateStartScreenTexture(FBitmap& srcdata); + +void FStartScreen::ValidateTexture() +{ + if (StartupTexture == nullptr) + { + auto imgsource = CreateStartScreenTexture(StartupBitmap); + StartupTexture = MakeGameTexture(new FImageTexture(imgsource), nullptr, ETextureType::Override); + StartupTexture->SetScale(1.f / Scale, 1.f / Scale); + } + if (HeaderTexture == nullptr && HeaderBitmap.GetWidth() > 0) + { + auto imgsource = CreateStartScreenTexture(HeaderBitmap); + HeaderTexture = MakeGameTexture(new FImageTexture(imgsource), nullptr, ETextureType::Override); + } + if (NetTexture == nullptr && NetBitmap.GetWidth() > 0) + { + auto imgsource = CreateStartScreenTexture(NetBitmap); + NetTexture = MakeGameTexture(new FImageTexture(imgsource), nullptr, ETextureType::Override); + } +} + diff --git a/src/common/startscreen/startscreen.h b/src/common/startscreen/startscreen.h new file mode 100644 index 0000000000..2700767cbc --- /dev/null +++ b/src/common/startscreen/startscreen.h @@ -0,0 +1,105 @@ +#pragma once +/* +** st_start.h +** Interface for the startup screen. +** +**--------------------------------------------------------------------------- +** Copyright 2006-2007 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +#pragma once +** The startup screen interface is based on a mix of Heretic and Hexen. +** Actual implementation is system-specific. +*/ +#include +#include +#include "bitmap.h" + +class FGameTexture; + +struct RgbQuad +{ + uint8_t rgbBlue; + uint8_t rgbGreen; + uint8_t rgbRed; + uint8_t rgbReserved; +}; + + +extern const RgbQuad TextModePalette[16]; + +class FStartScreen +{ +protected: + int CurPos = 0; + int MaxPos; + int Scale = 1; + int NetMaxPos = -1; + int NetCurPos = 0; + FBitmap StartupBitmap; + FBitmap HeaderBitmap; + FBitmap NetBitmap; + FString NetMessageString; + FGameTexture* StartupTexture = nullptr; + FGameTexture* HeaderTexture = nullptr; + FGameTexture* NetTexture = nullptr; +public: + FStartScreen(int maxp) { MaxPos = maxp; } + virtual ~FStartScreen() = default; + void Render(bool force = false); + bool Progress(int); + void NetProgress(int count); + virtual void LoadingStatus(const char *message, int colors) {} + virtual void AppendStatusLine(const char *status) {} + virtual bool NetInit(const char* message, int numplayers); + virtual void NetDone() {} + virtual void NetTick() {} + FBitmap& GetBitmap() { return StartupBitmap; } + int GetScale() const { return Scale; } + + +protected: + void ClearBlock(FBitmap& bitmap_info, RgbQuad fill, int x, int y, int bytewidth, int height); + FBitmap AllocTextBitmap(); + void DrawTextScreen(FBitmap& bitmap_info, const uint8_t* text_screen); + int DrawChar(FBitmap& screen, double x, double y, unsigned charnum, uint8_t attrib); + int DrawChar(FBitmap& screen, double x, double y, unsigned charnum, RgbQuad fg, RgbQuad bg); + int DrawString(FBitmap& screen, double x, double y, const char* text, RgbQuad fg, RgbQuad bg); + void UpdateTextBlink(FBitmap& bitmap_info, const uint8_t* text_screen, bool on); + void ST_Sound(const char* sndname); + int SizeOfText(const char* text); + void CreateHeader(); + void DrawNetStatus(int found, int total); + void ValidateTexture(); + virtual bool DoProgress(int); + virtual void DoNetProgress(int count); +}; + +FStartScreen* GetGameStartScreen(int max_progress); +extern FStartScreen* StartScreen; + +extern void ST_Endoom(); diff --git a/src/common/startscreen/startscreen_generic.cpp b/src/common/startscreen/startscreen_generic.cpp new file mode 100644 index 0000000000..0f89e30324 --- /dev/null +++ b/src/common/startscreen/startscreen_generic.cpp @@ -0,0 +1,127 @@ +/* +** st_start.cpp +** Generic startup screen +** +**--------------------------------------------------------------------------- +** Copyright 2022 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "startscreen.h" +#include "filesystem.h" +#include "printf.h" +#include "startupinfo.h" +#include "image.h" +#include "texturemanager.h" + +// Hexen startup screen +#define ST_PROGRESS_X 64 // Start of notches x screen pos. +#define ST_PROGRESS_Y 441 // Start of notches y screen pos. + + +class FGenericStartScreen : public FStartScreen +{ + FBitmap Background; + int NotchPos = 0; + +public: + FGenericStartScreen(int max_progress); + + bool DoProgress(int) override; +}; + + +//========================================================================== +// +// FGenericStartScreen Constructor +// +// Shows the Hexen startup screen. If the screen doesn't appear to be +// valid, it sets hr for a failure. +// +// The startup graphic is a planar, 4-bit 640x480 graphic preceded by a +// 16 entry (48 byte) VGA palette. +// +//========================================================================== + +FGenericStartScreen::FGenericStartScreen(int max_progress) + : FStartScreen(max_progress) +{ + // at this point we do not have a working texture manager yet, so we have to do the lookup via the file system + int startup_lump = fileSystem.CheckNumForName("GZDOOM", ns_graphics); + + StartupBitmap.Create(640, 480); + ClearBlock(StartupBitmap, { 0, 0, 0, 255 }, 0, 0, 640, 480); + // This also needs to work if the lump turns out to be unusable. + if (startup_lump != -1) + { + auto iBackground = FImageSource::GetImage(startup_lump, false); + if (iBackground) + { + Background = iBackground->GetCachedBitmap(nullptr, FImageSource::normal); + if (Background.GetWidth() < 640 || Background.GetHeight() < 480) + StartupBitmap.Blit(320 - Background.GetWidth()/2, 220 - Background.GetHeight() /2, Background); + else + StartupBitmap.Blit(0, 0, Background, 640, 480); + + } + } + CreateHeader(); +} + +//========================================================================== +// +// FGenericStartScreen :: Progress +// +// Bumps the progress meter one notch. +// +//========================================================================== + +bool FGenericStartScreen::DoProgress(int advance) +{ + int notch_pos, x, y; + + if (CurPos < MaxPos) + { + RgbQuad bcolor = { 128, 0, 0, 255 }; // todo: make configurable + int numnotches = 512; + notch_pos = ((CurPos + 1) * 512) / MaxPos; + if (notch_pos != NotchPos) + { // Time to draw another notch. + ClearBlock(StartupBitmap, bcolor, ST_PROGRESS_X, ST_PROGRESS_Y, notch_pos, 16); + NotchPos = notch_pos; + StartupTexture->CleanHardwareData(true); + } + } + return FStartScreen::DoProgress(advance); +} + + +FStartScreen* CreateGenericStartScreen(int max_progress) +{ + return new FGenericStartScreen(max_progress); +} diff --git a/src/common/startscreen/startscreen_heretic.cpp b/src/common/startscreen/startscreen_heretic.cpp new file mode 100644 index 0000000000..6829436f9b --- /dev/null +++ b/src/common/startscreen/startscreen_heretic.cpp @@ -0,0 +1,181 @@ +/* +** st_start.cpp +** Handles the startup screen. +** +**--------------------------------------------------------------------------- +** Copyright 2006-2007 Randy Heit +** Copyright 2006-2022 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "startscreen.h" +#include "filesystem.h" +#include "printf.h" +#include "texturemanager.h" + + +// Heretic startup screen +#define HERETIC_MINOR_VERSION '3' // Since we're based on Heretic 1.3 +#define THERM_X 14 +#define THERM_Y 14 +#define THERM_LEN 51 +#define THERM_COLOR 0xA // light green + + +class FHereticStartScreen : public FStartScreen +{ + int NotchPos; + int ThermX, ThermY, ThermWidth, ThermHeight; + int HMsgY, SMsgX; +public: + FHereticStartScreen(int max_progress); + + bool DoProgress(int) override; + void LoadingStatus(const char *message, int colors) override; + void AppendStatusLine(const char *status) override; +}; + + +//========================================================================== +// +// FHereticStartScreen Constructor +// +// Shows the Heretic startup screen. If the screen doesn't appear to be +// valid, it returns a failure code in hr. +// +// The loading screen is an 80x25 text screen with character data and +// attributes intermixed, which means it must be exactly 4000 bytes long. +// +//========================================================================== + +FHereticStartScreen::FHereticStartScreen(int max_progress) + : FStartScreen(max_progress) +{ + int loading_lump = fileSystem.CheckNumForName("LOADING"); + uint8_t loading_screen[4000]; + + if (loading_lump < 0 || fileSystem.FileLength(loading_lump) != 4000) + { + I_Error("'LOADING' not found"); + } + + fileSystem.ReadFile(loading_lump, loading_screen); + + // Slap the Heretic minor version on the loading screen. Heretic + // did this inside the executable rather than coming with modified + // LOADING screens, so we need to do the same. + loading_screen[2 * 160 + 49 * 2] = HERETIC_MINOR_VERSION; + + // Draw the loading screen to a bitmap. + StartupBitmap.Create(80 * 8, 25 * 16); + DrawTextScreen(StartupBitmap, loading_screen); + + ThermX = THERM_X * 8; + ThermY = THERM_Y * 16; + ThermWidth = THERM_LEN * 8 - 4; + ThermHeight = 16; + HMsgY = 7; + SMsgX = 1; + NotchPos = 0; + CreateHeader(); +} + +//========================================================================== +// +// FHereticStartScreen::Progress +// +// Bumps the progress meter one notch. +// +//========================================================================== + +bool FHereticStartScreen::DoProgress(int advance) +{ + if (CurPos < MaxPos) + { + int notch_pos = ((CurPos + 1) * ThermWidth) / MaxPos; + if (notch_pos != NotchPos && !(notch_pos & 3)) + { // Time to draw another notch. + int left = NotchPos + ThermX; + int top = ThermY; + int right = notch_pos + ThermX; + int bottom = top + ThermHeight; + ClearBlock(StartupBitmap, TextModePalette[THERM_COLOR], left, top, right - left, bottom - top); + NotchPos = notch_pos; + StartupTexture->CleanHardwareData(true); + } + } + return FStartScreen::DoProgress(advance); +} + +//========================================================================== +// +// FHereticStartScreen :: LoadingStatus +// +// Prints text in the center box of the startup screen. +// +//========================================================================== + +void FHereticStartScreen::LoadingStatus(const char* message, int colors) +{ + int x; + + for (x = 0; message[x] != '\0'; ++x) + { + DrawChar(StartupBitmap, 17 + x, HMsgY, message[x], colors); + } + HMsgY++; + StartupTexture->CleanHardwareData(true); + Render(); +} + +//========================================================================== +// +// FHereticStartScreen :: AppendStatusLine +// +// Appends text to Heretic's status line. +// +//========================================================================== + +void FHereticStartScreen::AppendStatusLine(const char* status) +{ + int x; + + for (x = 0; status[x] != '\0'; ++x) + { + DrawChar(StartupBitmap, SMsgX + x, 24, status[x], 0x1f); + } + SMsgX += x; + StartupTexture->CleanHardwareData(true); + Render(); +} + + +FStartScreen* CreateHereticStartScreen(int max_progress) +{ + return new FHereticStartScreen(max_progress); +} diff --git a/src/common/startscreen/startscreen_hexen.cpp b/src/common/startscreen/startscreen_hexen.cpp new file mode 100644 index 0000000000..122b6f3624 --- /dev/null +++ b/src/common/startscreen/startscreen_hexen.cpp @@ -0,0 +1,212 @@ +/* +** st_start.cpp +** Handles the startup screen. +** +**--------------------------------------------------------------------------- +** Copyright 2006-2007 Randy Heit +** Copyright 2006-2022 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "startscreen.h" +#include "filesystem.h" +#include "printf.h" +#include "startupinfo.h" +#include "s_music.h" +#include "image.h" +#include "texturemanager.h" + +// Hexen startup screen +#define ST_PROGRESS_X 64 // Start of notches x screen pos. +#define ST_PROGRESS_Y 441 // Start of notches y screen pos. + +#define ST_NETPROGRESS_X 288 +#define ST_NETPROGRESS_Y 32 + +class FHexenStartScreen : public FStartScreen +{ + // Hexen's notch graphics, converted to chunky pixels. + FBitmap Background; + FBitmap NotchBits; + FBitmap NetNotchBits; + int NotchPos = 0; + +public: + FHexenStartScreen(int max_progress); + + bool DoProgress(int) override; + void DoNetProgress(int count) override; + void NetDone() override; +}; + + +//========================================================================== +// +// FHexenStartScreen Constructor +// +// Shows the Hexen startup screen. If the screen doesn't appear to be +// valid, it sets hr for a failure. +// +// The startup graphic is a planar, 4-bit 640x480 graphic preceded by a +// 16 entry (48 byte) VGA palette. +// +//========================================================================== + +FHexenStartScreen::FHexenStartScreen(int max_progress) + : FStartScreen(max_progress) +{ + // at this point we do not have a working texture manager yet, so we have to do the lookup via the file system + int startup_lump = fileSystem.CheckNumForName("STARTUP", ns_graphics); + int netnotch_lump = fileSystem.CheckNumForName("NETNOTCH", ns_graphics); + int notch_lump = fileSystem.CheckNumForName("NOTCH", ns_graphics); + + // For backwards compatibility we also need to look in the default namespace, because these were previously not handled as graphics. + if (startup_lump == -1) startup_lump = fileSystem.CheckNumForName("STARTUP"); + if (netnotch_lump == -1)netnotch_lump = fileSystem.CheckNumForName("NETNOTCH"); + if (notch_lump == -1)notch_lump = fileSystem.CheckNumForName("NOTCH"); + + + if (startup_lump < 0 || netnotch_lump < 0 || notch_lump < 0) + { + I_Error("Start screen assets missing"); + } + + auto iBackground = FImageSource::GetImage(startup_lump, false); + auto iNetNotchBits = FImageSource::GetImage(netnotch_lump, false); + auto iNotchBits = FImageSource::GetImage(notch_lump, false); + if (!iBackground || !iNetNotchBits || !iNotchBits || iBackground->GetWidth() != 640 || iBackground->GetHeight() != 480) + { + I_Error("Start screen assets missing"); + } + NetNotchBits = iNetNotchBits->GetCachedBitmap(nullptr, FImageSource::normal); + NotchBits = iNotchBits->GetCachedBitmap(nullptr, FImageSource::normal); + Background = iBackground->GetCachedBitmap(nullptr, FImageSource::normal); + + StartupBitmap.Create(640, 480); + StartupBitmap.Blit(0, 0, Background, 640, 480); + + // Fill in the bitmap data. Convert to chunky, because I can't figure out + // if Windows actually supports planar images or not, despite the presence + // of biPlanes in the BITMAPINFOHEADER. + + + if (!batchrun) + { + if (GameStartupInfo.Song.IsNotEmpty()) + { + S_ChangeMusic(GameStartupInfo.Song.GetChars(), true, true); + } + else + { + S_ChangeMusic("orb", true, true); + } + } + CreateHeader(); +} + +//========================================================================== +// +// FHexenStartScreen :: Progress +// +// Bumps the progress meter one notch. +// +//========================================================================== + +bool FHexenStartScreen::DoProgress(int advance) +{ + int notch_pos, x, y; + + if (CurPos < MaxPos) + { + int numnotches = (16 * 32) / NotchBits.GetWidth(); + notch_pos = ((CurPos + 1) * numnotches) / MaxPos; + if (notch_pos != NotchPos) + { // Time to draw another notch. + for (; NotchPos < notch_pos; NotchPos++) + { + x = ST_PROGRESS_X + NotchBits.GetWidth() * NotchPos; + y = ST_PROGRESS_Y; + StartupBitmap.Blit(x, y, NotchBits); + } + StartupTexture->CleanHardwareData(true); + ST_Sound("StartupTick"); + } + } + return FStartScreen::DoProgress(advance); +} + +//========================================================================== +// +// FHexenStartScreen :: NetProgress +// +// Draws the red net noches in addition to the normal progress bar. +// +//========================================================================== + +void FHexenStartScreen::DoNetProgress(int count) +{ + int oldpos = NetCurPos; + int x, y; + + FStartScreen::NetProgress(count); + + if (NetMaxPos != 0 && NetCurPos > oldpos) + { + int numnotches = (4 * 8) / NetNotchBits.GetWidth(); + int notch_pos = (NetCurPos * numnotches) / NetMaxPos; + + for (; oldpos < NetCurPos && oldpos < numnotches; ++oldpos) + { + x = ST_NETPROGRESS_X + NetNotchBits.GetWidth() * oldpos; + y = ST_NETPROGRESS_Y; + StartupBitmap.Blit(x, y, NetNotchBits); + } + ST_Sound("misc/netnotch"); + StartupTexture->CleanHardwareData(true); + } +} + +//========================================================================== +// +// FHexenStartScreen :: NetDone +// +// Aside from the standard processing, also plays a sound. +// +//========================================================================== + +void FHexenStartScreen::NetDone() +{ + ST_Sound("PickupWeapon"); + FStartScreen::NetDone(); +} + + +FStartScreen* CreateHexenStartScreen(int max_progress) +{ + return new FHexenStartScreen(max_progress); +} diff --git a/src/common/startscreen/startscreen_strife.cpp b/src/common/startscreen/startscreen_strife.cpp new file mode 100644 index 0000000000..d4da521b52 --- /dev/null +++ b/src/common/startscreen/startscreen_strife.cpp @@ -0,0 +1,200 @@ +/* +** st_start.cpp +** Handles the startup screen. +** +**--------------------------------------------------------------------------- +** Copyright 2006-2007 Randy Heit +** Copyright 2006-2022 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "startscreen.h" +#include "filesystem.h" +#include "printf.h" +#include "image.h" +#include "palettecontainer.h" + +// Strife startup screen +#define PEASANT_INDEX 0 +#define LASER_INDEX 4 +#define BOT_INDEX 6 + +#define ST_LASERSPACE_X 60 +#define ST_LASERSPACE_Y 156 +#define ST_LASERSPACE_WIDTH 200 +#define ST_LASER_WIDTH 16 +#define ST_LASER_HEIGHT 16 + +#define ST_BOT_X 14 +#define ST_BOT_Y 138 +#define ST_BOT_WIDTH 48 +#define ST_BOT_HEIGHT 48 + +#define ST_PEASANT_X 262 +#define ST_PEASANT_Y 136 +#define ST_PEASANT_WIDTH 32 +#define ST_PEASANT_HEIGHT 64 + +static const char* StrifeStartupPicNames[] = +{ + "STRTPA1", "STRTPB1", "STRTPC1", "STRTPD1", + "STRTLZ1", "STRTLZ2", + "STRTBOT", + "STARTUP0" +}; + + + +class FStrifeStartScreen : public FStartScreen +{ +public: + FStrifeStartScreen(int max_progress); + + bool DoProgress(int) override; +protected: + void DrawStuff(int old_laser, int new_laser); + + FBitmap StartupPics[4+2+1+1]; + int NotchPos = 0; +}; + + +//========================================================================== +// +// FStrifeStartScreen Constructor +// +// Shows the Strife startup screen. If the screen doesn't appear to be +// valid, it returns a failure code in hr. +// +// The startup background is a raw 320x200 image, however Strife only +// actually uses 95 rows from it, starting at row 57. The rest of the image +// is discarded. (What a shame.) +// +// The peasants are raw 32x64 images. The laser dots are raw 16x16 images. +// The bot is a raw 48x48 image. All use the standard PLAYPAL. +// +//========================================================================== + +FStrifeStartScreen::FStrifeStartScreen(int max_progress) + : FStartScreen(max_progress) +{ + // at this point we do not have a working texture manager yet, so we have to do the lookup via the file system + + int startup_lump = fileSystem.CheckNumForName("STARTUP0"); + + if (startup_lump < 0) + { + I_Error("bad startscreen assets"); + } + + StartupBitmap.Create(320, 200); + + // Load the animated overlays. + for (size_t i = 0; i < countof(StrifeStartupPicNames); ++i) + { + int lumpnum = fileSystem.CheckNumForName(StrifeStartupPicNames[i], ns_graphics); + if (lumpnum < 0) lumpnum = fileSystem.CheckNumForName(StrifeStartupPicNames[i]); + + if (lumpnum >= 0) + { + auto lumpr1 = FImageSource::GetImage(lumpnum, false); + if (lumpr1) StartupPics[i] = lumpr1->GetCachedBitmap(nullptr, FImageSource::normal); + } + } + if (StartupPics[7].GetWidth() != 320 || StartupPics[7].GetHeight() != 200) + { + I_Error("bad startscreen assets"); + } + + // Make the startup image appear. + DrawStuff(0, 0); + Scale = 2; + CreateHeader(); +} + +//========================================================================== +// +// FStrifeStartScreen :: Progress +// +// Bumps the progress meter one notch. +// +//========================================================================== + +bool FStrifeStartScreen::DoProgress(int advance) +{ + int notch_pos; + + if (CurPos < MaxPos) + { + notch_pos = ((CurPos + 1) * (ST_LASERSPACE_WIDTH - ST_LASER_WIDTH)) / MaxPos; + if (notch_pos != NotchPos && !(notch_pos & 1)) + { // Time to update. + DrawStuff(NotchPos, notch_pos); + NotchPos = notch_pos; + } + } + return FStartScreen::DoProgress(advance); +} + +//========================================================================== +// +// FStrifeStartScreen :: DrawStuff +// +// Draws all the moving parts of Strife's startup screen. If you're +// running off a slow drive, it can look kind of good. Otherwise, it +// borders on crazy insane fast. +// +//========================================================================== + +void FStrifeStartScreen::DrawStuff(int old_laser, int new_laser) +{ + int y; + + // Clear old laser + StartupBitmap.Blit(0, 0, StartupPics[7]); + + // Draw new laser + auto& lp = StartupPics[LASER_INDEX + (new_laser & 1)]; + StartupBitmap.Blit(ST_LASERSPACE_X + new_laser, ST_LASERSPACE_Y, lp); + + // The bot jumps up and down like crazy. + y = max(0, (new_laser >> 1) % 5 - 2); + + StartupBitmap.Blit(ST_BOT_X, ST_BOT_Y + y, StartupPics[BOT_INDEX]); + + // The peasant desperately runs in place, trying to get away from the laser. + // Yet, despite all his limb flailing, he never manages to get anywhere. + auto& pp = StartupPics[PEASANT_INDEX + ((new_laser >> 1) & 3)]; + StartupBitmap.Blit(ST_PEASANT_X, ST_PEASANT_Y, pp); +} + + +FStartScreen* CreateStrifeStartScreen(int max_progress) +{ + return new FStrifeStartScreen(max_progress); +} diff --git a/src/gamedata/textures/formats/startscreentexture.cpp b/src/common/textures/formats/startscreentexture.cpp similarity index 63% rename from src/gamedata/textures/formats/startscreentexture.cpp rename to src/common/textures/formats/startscreentexture.cpp index b3305808c6..75b110a57a 100644 --- a/src/gamedata/textures/formats/startscreentexture.cpp +++ b/src/common/textures/formats/startscreentexture.cpp @@ -36,13 +36,12 @@ #include "doomtype.h" #include "files.h" -#include "w_wad.h" #include "gi.h" #include "bitmap.h" -#include "textures/textures.h" +#include "textures.h" #include "imagehelpers.h" #include "image.h" -#include "st_start.h" +#include "startscreen.h" //========================================================================== @@ -53,11 +52,21 @@ class FStartScreenTexture : public FImageSource { - BitmapInfo *info; // This must remain constant for the lifetime of this texture + FBitmap& info; // This must remain constant for the lifetime of this texture public: - FStartScreenTexture (BitmapInfo *srcdata); - int CopyPixels(FBitmap *bmp, int conversion) override; + FStartScreenTexture(FBitmap& srcdata) + : FImageSource(-1), info(srcdata) + { + Width = srcdata.GetWidth(); + Height = srcdata.GetHeight(); + bUseGamePalette = false; + } + int CopyPixels(FBitmap* bmp, int conversion) + { + bmp->Blit(0, 0, info); + return 0; + } }; //========================================================================== @@ -66,48 +75,9 @@ public: // //========================================================================== -FImageSource *CreateStartScreenTexture(BitmapInfo *srcdata) +FImageSource *CreateStartScreenTexture(FBitmap& srcdata) { return new FStartScreenTexture(srcdata); } -//========================================================================== -// -// -// -//========================================================================== - -FStartScreenTexture::FStartScreenTexture (BitmapInfo *srcdata) -: FImageSource(-1) -{ - Width = srcdata->bmiHeader.biWidth; - Height = srcdata->bmiHeader.biHeight; - info = srcdata; - bUseGamePalette = false; - -} - -//========================================================================== -// -// -// -//========================================================================== - -int FStartScreenTexture::CopyPixels(FBitmap *bmp, int conversion) -{ - const RgbQuad *psource = info->bmiColors; - PalEntry paldata[256] = {}; - auto pixels = ST_Util_BitsForBitmap(info); - for (uint32_t i = 0; i < info->bmiHeader.biClrUsed; i++) - { - PalEntry &pe = paldata[i]; - pe.r = psource[i].rgbRed; - pe.g = psource[i].rgbGreen; - pe.b = psource[i].rgbBlue; - pe.a = 255; - } - bmp->CopyPixelData(0, 0, pixels, Width, Height, 1, (Width + 3) & ~3, 0, paldata); - - return 0; -}