- backend update from GZDoom.

The main bulk of this is the new start screen code. To make this work in Raze some more work on the startup procedure is needed.
What this does provide is support for the DOS end-of-game text screens in Duke and SW on non-Windows systems.
This commit is contained in:
Christoph Oelckers 2022-06-06 11:45:02 +02:00
parent 47aa110441
commit 84173ee09b
95 changed files with 4001 additions and 3209 deletions

View file

@ -414,7 +414,10 @@ if( MSVC AND NOT VPX_FOUND )
# Use prebuilt library
set( VPX_ROOT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../bin/Windows/vpx" )
set( VPX_INCLUDE_DIR ${VPX_ROOT_PATH}/include )
set( VPX_LIBRARIES libvpx libcompat-to-msvc )
set( VPX_LIBRARIES libvpx )
if ( NOT ARM64 )
set (VPX_LIBRARIES ${VPX_LIBRARIES} libcompat-to-msvc )
endif()
if( ARM64 )
link_directories( ${VPX_ROOT_PATH}/lib/arm64 )
elseif( X64 )
@ -460,10 +463,10 @@ set( PLAT_WIN32_SOURCES
common/platform/win32/i_rawps2.cpp
common/platform/win32/i_xinput.cpp
common/platform/win32/i_main.cpp
common/platform/win32/i_mainwindow.cpp
common/platform/win32/i_system.cpp
common/platform/win32/i_specialpaths.cpp
common/platform/win32/st_start.cpp
common/platform/win32/st_start_util.cpp
common/platform/win32/gl_sysfb.cpp
common/platform/win32/base_sysfb.cpp
common/platform/win32/win32basevideo.cpp
@ -628,6 +631,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
@ -1104,6 +1108,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
@ -1139,11 +1149,13 @@ set (PCH_SOURCES
common/textures/formats/pcxtexture.cpp
common/textures/formats/pngtexture.cpp
common/textures/formats/rawpagetexture.cpp
common/textures/formats/startuptexture.cpp
common/textures/formats/emptytexture.cpp
common/textures/formats/shadertexture.cpp
common/textures/formats/tgatexture.cpp
common/textures/formats/stbtexture.cpp
common/textures/formats/anmtexture.cpp
common/textures/formats/startscreentexture.cpp
common/textures/hires/hqresize.cpp
common/models/models_md3.cpp
common/models/models_md2.cpp
@ -1230,7 +1242,7 @@ set (PCH_SOURCES
common/menu/menudef.cpp
common/menu/savegamemanager.cpp
common/statusbar/base_sbar.cpp
common/rendering/v_framebuffer.cpp
common/rendering/v_video.cpp
common/rendering/r_thread.cpp
@ -1409,6 +1421,7 @@ include_directories(
common/statusbar
common/fonts
common/objects
common/startscreen
common/rendering
common/rendering/hwrenderer
common/rendering/hwrenderer/data

View file

@ -247,11 +247,13 @@ int GetUIScale(F2DDrawer* drawer, int altval);
int GetConScale(F2DDrawer* drawer, int altval);
EXTERN_CVAR(Int, uiscale);
EXTERN_CVAR(Int, con_scale);
inline int active_con_scale(F2DDrawer *drawer)
{
return GetConScale(drawer, con_scale);
// this sets the threshold for upscaling the console font to 2560 x 1440.
int vscale = drawer->GetHeight() / 720;
int hscale = drawer->GetWidth() / 1280;
return max(1, min(vscale, hscale));
}
#ifdef DrawText

View file

@ -128,11 +128,6 @@ static GameAtExit *ExitCmdList;
static char *work = NULL;
static int worklen = 0;
CUSTOM_CVAR(Int, con_scale, 0, CVAR_ARCHIVE)
{
if (self < 0) self = 0;
}
CUSTOM_CVAR(Float, con_alpha, 0.75f, CVAR_ARCHIVE)
{
if (self < 0.f) self = 0.f;

View file

@ -520,7 +520,6 @@ void FBaseCVar::EnableNoSet ()
void FBaseCVar::EnableCallbacks ()
{
m_inEnable = true;
m_UseCallback = true;
FBaseCVar *cvar = CVars;
@ -532,7 +531,6 @@ void FBaseCVar::EnableCallbacks ()
}
cvar = cvar->m_Next;
}
m_inEnable = false;
}
void FBaseCVar::DisableCallbacks ()

View file

@ -210,9 +210,6 @@ protected:
uint32_t Flags;
bool inCallback = false;
public:
static inline bool m_inEnable = false;
private:
FBaseCVar (const FBaseCVar &var) = delete;
FBaseCVar (const char *name, uint32_t flags);

View file

@ -382,23 +382,17 @@ public:
const int ustride = img->stride[VPX_PLANE_U];
const int vstride = img->stride[VPX_PLANE_V];
for (unsigned int y = 0; y < height; y += 2)
for (unsigned int y = 0; y < height; y++)
{
unsigned int y1 = y + 1;
unsigned int wy = width * y;
unsigned int wy1 = width * y1;
for (unsigned int x = 0; x < width; x += 2)
for (unsigned int x = 0; x < width; x++)
{
uint8_t u = uplane[ustride * (y >> 1) + (x >> 1)];
uint8_t v = vplane[vstride * (y >> 1) + (x >> 1)];
SetPixel(&Pic[(wy + x) << 2], yplane[ystride * y + x], u, v);
SetPixel(&Pic[(wy + x + 1) << 2], yplane[ystride * y + x + 1], u, v);
SetPixel(&Pic[(wy1 + x) << 2], yplane[ystride * y1 + x], u, v);
SetPixel(&Pic[(wy1 + x + 1) << 2], yplane[ystride * y1 + x + 1], u, v);
SetPixel(&Pic[(x + y * width) << 2], yplane[ystride * y + x], u, v);
}
}
return true;
}

View file

@ -52,7 +52,6 @@ event_t events[MAXEVENTS];
CVAR(Float, m_sensitivity_x, 4, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Float, m_sensitivity_y, 2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
CVAR(Bool, m_filter, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
//==========================================================================
@ -165,24 +164,11 @@ void D_PostEvent(event_t* ev)
void PostMouseMove(int xx, int yy)
{
static float lastx = 0, lasty = 0;
event_t ev{};
float x = float(xx) * m_sensitivity_x;
float y = -float(yy) * m_sensitivity_y;
ev.x = float(xx) * m_sensitivity_x;
ev.y = -float(yy) * m_sensitivity_y;
if (m_filter)
{
ev.x = (x + lastx) / 2;
ev.y = (y + lasty) / 2;
}
else
{
ev.x = x;
ev.y = y;
}
lastx = x;
lasty = y;
if (ev.x || ev.y)
{
ev.type = EV_Mouse;

View file

@ -1,6 +1,8 @@
#include "i_interface.h"
#include "st_start.h"
// Some global engine variables taken out of the backend code.
FStartupScreen* StartWindow;
SystemCallbacks sysCallbacks;
FString endoomName;
bool batchrun;

View file

@ -292,9 +292,9 @@ void PacketGet (void)
if (err == WSAECONNRESET)
{ // The remote node aborted unexpectedly, so pretend it sent an exit packet
if (StartScreen != NULL)
if (StartWindow != NULL)
{
StartScreen->NetMessage ("The connection from %s was dropped.\n",
I_NetMessage ("The connection from %s was dropped.\n",
GetPlayerName(node).GetChars());
}
else
@ -511,7 +511,7 @@ static void SendConAck (int num_connected, int num_needed)
{
PreSend (&packet, 4, &sendaddress[node]);
}
StartScreen->NetProgress (doomcom.numnodes);
I_NetProgress (doomcom.numnodes);
}
bool Host_CheckForConnects (void *userdata)
@ -536,7 +536,7 @@ bool Host_CheckForConnects (void *userdata)
if (node == -1)
{
const uint8_t *s_addr_bytes = (const uint8_t *)&from->sin_addr;
StartScreen->NetMessage ("Got extra connect from %d.%d.%d.%d:%d",
I_NetMessage ("Got extra connect from %d.%d.%d.%d:%d",
s_addr_bytes[0], s_addr_bytes[1], s_addr_bytes[2], s_addr_bytes[3],
from->sin_port);
packet.Message = PRE_ALLFULL;
@ -549,7 +549,7 @@ bool Host_CheckForConnects (void *userdata)
{
node = doomcom.numnodes++;
sendaddress[node] = *from;
StartScreen->NetMessage ("Got connect from node %d.", node);
I_NetMessage ("Got connect from node %d.", node);
}
// Let the new guest (and everyone else) know we got their message.
@ -561,7 +561,7 @@ bool Host_CheckForConnects (void *userdata)
node = FindNode (from);
if (node >= 0)
{
StartScreen->NetMessage ("Got disconnect from node %d.", node);
I_NetMessage ("Got disconnect from node %d.", node);
doomcom.numnodes--;
while (node < doomcom.numnodes)
{
@ -708,10 +708,10 @@ bool HostGame (int i)
doomcom.numnodes = 1;
StartScreen->NetInit ("Waiting for players", numplayers);
I_NetInit ("Waiting for players", numplayers);
// Wait for numplayers-1 different connections
if (!StartScreen->NetLoop (Host_CheckForConnects, (void *)(intptr_t)numplayers))
if (!I_NetLoop (Host_CheckForConnects, (void *)(intptr_t)numplayers))
{
SendAbort();
return false;
@ -719,17 +719,17 @@ bool HostGame (int i)
// Now inform everyone of all machines involved in the game
memset (gotack, 0, sizeof(gotack));
StartScreen->NetMessage ("Sending all here.");
StartScreen->NetInit ("Done waiting", 1);
I_NetMessage ("Sending all here.");
I_NetInit ("Done waiting", 1);
if (!StartScreen->NetLoop (Host_SendAllHere, (void *)gotack))
if (!I_NetLoop (Host_SendAllHere, (void *)gotack))
{
SendAbort();
return false;
}
// Now go
StartScreen->NetMessage ("Go");
I_NetMessage ("Go");
packet.Fake = PRE_FAKE;
packet.Message = PRE_GO;
for (node = 1; node < doomcom.numnodes; node++)
@ -742,7 +742,7 @@ bool HostGame (int i)
}
}
StartScreen->NetMessage ("Total players: %d", doomcom.numnodes);
I_NetMessage ("Total players: %d", doomcom.numnodes);
doomcom.id = DOOMCOM_ID;
doomcom.numplayers = doomcom.numnodes;
@ -776,9 +776,9 @@ bool Guest_ContactHost (void *userdata)
{
if (packet.Message == PRE_CONACK)
{
StartScreen->NetMessage ("Total players: %d", packet.NumNodes);
StartScreen->NetInit ("Waiting for other players", packet.NumNodes);
StartScreen->NetProgress (packet.NumPresent);
I_NetMessage ("Total players: %d", packet.NumNodes);
I_NetInit ("Waiting for other players", packet.NumNodes);
I_NetProgress (packet.NumPresent);
return true;
}
else if (packet.Message == PRE_DISCONNECT)
@ -795,7 +795,7 @@ bool Guest_ContactHost (void *userdata)
}
// In case the progress bar could not be marqueed, bump it.
StartScreen->NetProgress (0);
I_NetProgress (0);
return false;
}
@ -814,7 +814,7 @@ bool Guest_WaitForOthers (void *userdata)
switch (packet.Message)
{
case PRE_CONACK:
StartScreen->NetProgress (packet.NumPresent);
I_NetProgress (packet.NumPresent);
break;
case PRE_ALLHERE:
@ -825,7 +825,7 @@ bool Guest_WaitForOthers (void *userdata)
doomcom.numnodes = packet.NumNodes + 2;
sendplayer[0] = packet.ConsoleNum; // My player number
doomcom.consoleplayer = packet.ConsoleNum;
StartScreen->NetMessage ("Console player number: %d", doomcom.consoleplayer);
I_NetMessage ("Console player number: %d", doomcom.consoleplayer);
for (node = 0; node < packet.NumNodes; node++)
{
sendaddress[node+2].sin_addr.s_addr = packet.machines[node].address;
@ -839,14 +839,14 @@ bool Guest_WaitForOthers (void *userdata)
}
}
StartScreen->NetMessage ("Received All Here, sending ACK.");
I_NetMessage ("Received All Here, sending ACK.");
packet.Fake = PRE_FAKE;
packet.Message = PRE_ALLHEREACK;
PreSend (&packet, 2, &sendaddress[1]);
break;
case PRE_GO:
StartScreen->NetMessage ("Received \"Go.\"");
I_NetMessage ("Received \"Go.\"");
return true;
case PRE_DISCONNECT:
@ -878,22 +878,22 @@ bool JoinGame (int i)
// Let host know we are here
StartScreen->NetInit ("Contacting host", 0);
I_NetInit ("Contacting host", 0);
if (!StartScreen->NetLoop (Guest_ContactHost, NULL))
if (!I_NetLoop (Guest_ContactHost, NULL))
{
SendAbort();
return false;
}
// Wait for everyone else to connect
if (!StartScreen->NetLoop (Guest_WaitForOthers, 0))
if (!I_NetLoop (Guest_WaitForOthers, 0))
{
SendAbort();
return false;
}
StartScreen->NetMessage ("Total players: %d", doomcom.numnodes);
I_NetMessage ("Total players: %d", doomcom.numnodes);
doomcom.id = DOOMCOM_ID;
doomcom.numplayers = doomcom.numnodes;
@ -1026,6 +1026,43 @@ void I_NetCmd (void)
I_Error ("Bad net cmd: %i\n",doomcom.command);
}
void I_NetMessage(const char* text, ...)
{
// todo: use better abstraction once everything is migrated to in-game start screens.
#if defined _WIN32 || defined __APPLE__
va_list ap;
va_start(ap, text);
VPrintf(PRINT_HIGH, text, ap);
Printf("\n");
va_end(ap);
#else
FString str;
va_list argptr;
va_start(argptr, text);
str.VFormat(text, argptr);
va_end(argptr);
fprintf(stderr, "\r%-40s\n", str.GetChars());
#endif
}
// todo: later these must be dispatched by the main menu, not the start screen.
void I_NetProgress(int val)
{
StartWindow->NetProgress(val);
}
void I_NetInit(const char* msg, int num)
{
StartWindow->NetInit(msg, num);
}
bool I_NetLoop(bool (*timer_callback)(void*), void* userdata)
{
return StartWindow->NetLoop(timer_callback, userdata);
}
void I_NetDone()
{
StartWindow->NetDone();
}
#ifdef __WIN32__
const char *neterror (void)
{

View file

@ -6,6 +6,11 @@
// Called by D_DoomMain.
int I_InitNetwork (void);
void I_NetCmd (void);
void I_NetMessage(const char*, ...);
void I_NetProgress(int val);
void I_NetInit(const char* msg, int num);
bool I_NetLoop(bool (*timer_callback)(void*), void* userdata);
void I_NetDone();
enum ENetConstants
{

View file

@ -135,16 +135,19 @@ public:
FRemapTable* GetTranslation(int slot, int index)
{
if (TranslationTables.Size() <= (unsigned)slot) return nullptr;
return TranslationTables[slot].GetVal(index);
}
void ClearTranslationSlot(int slot)
{
if (TranslationTables.Size() <= (unsigned)slot) return;
TranslationTables[slot].Clear();
}
unsigned NumTranslations(int slot) const
{
if (TranslationTables.Size() <= (unsigned)slot) return 0;
return TranslationTables[slot].Size();
}

View file

@ -39,7 +39,7 @@
class FStartupScreen
{
public:
static FStartupScreen *CreateInstance(int max_progress);
static FStartupScreen *CreateInstance(int max_progress, bool showprogress);
FStartupScreen(int max_progress)
{
@ -54,7 +54,6 @@ public:
virtual void NetInit(const char *message, int num_players) {}
virtual void NetProgress(int count) {}
virtual void NetMessage(const char *format, ...) {} // cover for printf
virtual void NetDone() {}
virtual bool NetLoop(bool (*timer_callback)(void *), void *userdata) { return false; }
virtual void AppendStatusLine(const char* status) {}
@ -77,118 +76,12 @@ public:
void NetDone();
bool NetLoop(bool (*timer_callback)(void*), void* userdata);
protected:
long long NetMarqueeMode;
int NetMaxPos, NetCurPos;
};
class FGraphicalStartupScreen : public FBasicStartupScreen
{
public:
FGraphicalStartupScreen(int max_progress);
~FGraphicalStartupScreen();
};
class FHereticStartupScreen : public FGraphicalStartupScreen
{
public:
FHereticStartupScreen(int max_progress, long &hr);
void Progress();
void LoadingStatus(const char *message, int colors);
void AppendStatusLine(const char *status);
protected:
void SetWindowSize();
int ThermX, ThermY, ThermWidth, ThermHeight;
int HMsgY, SMsgX;
};
class FHexenStartupScreen : public FGraphicalStartupScreen
{
public:
FHexenStartupScreen(int max_progress, long &hr);
~FHexenStartupScreen();
void Progress();
void NetProgress(int count);
void NetDone();
void SetWindowSize();
// Hexen's notch graphics, converted to chunky pixels.
uint8_t * NotchBits;
uint8_t * NetNotchBits;
};
class FStrifeStartupScreen : public FGraphicalStartupScreen
{
public:
FStrifeStartupScreen(int max_progress, long &hr);
~FStrifeStartupScreen();
void Progress();
protected:
void DrawStuff(int old_laser, int new_laser);
void SetWindowSize();
uint8_t *StartupPics[4+2+1];
};
extern FStartupScreen *StartScreen;
void DeleteStartupScreen();
extern void ST_Endoom();
// The entire set of functions here uses native Windows types. These are recreations of those types so that the code doesn't need to be changed more than necessary
struct BitmapInfoHeader
{
uint32_t biSize;
int32_t biWidth;
int32_t biHeight;
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
uint32_t biSizeImage;
int32_t biXPelsPerMeter;
int32_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
};
struct RgbQuad
{
uint8_t rgbBlue;
uint8_t rgbGreen;
uint8_t rgbRed;
uint8_t rgbReserved;
};
struct BitmapInfo
{
BitmapInfoHeader bmiHeader;
RgbQuad bmiColors[1];
};
extern BitmapInfo* StartupBitmap;
void ST_Util_PlanarToChunky4(uint8_t* dest, const uint8_t* src, int width, int height);
void ST_Util_DrawBlock(BitmapInfo* bitmap_info, const uint8_t* src, int x, int y, int bytewidth, int height);
void ST_Util_ClearBlock(BitmapInfo* bitmap_info, uint8_t fill, int x, int y, int bytewidth, int height);
BitmapInfo* ST_Util_CreateBitmap(int width, int height, int color_bits);
uint8_t* ST_Util_BitsForBitmap(BitmapInfo* bitmap_info);
void ST_Util_FreeBitmap(BitmapInfo* bitmap_info);
void ST_Util_BitmapColorsFromPlaypal(BitmapInfo* bitmap_info);
uint8_t* ST_Util_LoadFont(const char* filename);
void ST_Util_FreeFont(uint8_t* font);
BitmapInfo* ST_Util_AllocTextBitmap(const uint8_t* font);
void ST_Util_DrawTextScreen(BitmapInfo* bitmap_info, const uint8_t* text_screen, const uint8_t* font);
void ST_Util_DrawChar(BitmapInfo* screen, const uint8_t* font, int x, int y, uint8_t charnum, uint8_t attrib);
void ST_Util_UpdateTextBlink(BitmapInfo* bitmap_info, const uint8_t* text_screen, const uint8_t* font, bool on);
extern FStartupScreen *StartWindow;
//===========================================================================
//
@ -200,10 +93,10 @@ void ST_Util_UpdateTextBlink(BitmapInfo* bitmap_info, const uint8_t* text_screen
inline void DeleteStartupScreen()
{
if (StartScreen != nullptr)
if (StartWindow != nullptr)
{
delete StartScreen;
StartScreen = nullptr;
delete StartWindow;
StartWindow = nullptr;
}
}

View file

@ -222,7 +222,7 @@ bool FZipFile::Open(bool quiet, LumpFilterInfo* filter)
return false;
}
NumLumps = info.NumEntries;
NumLumps = (uint32_t)info.NumEntries;
dirsize = info.DirectorySize;
DirectoryOffset = info.DirectoryOffset;
}
@ -521,7 +521,7 @@ int FZipLump::GetFileOffset()
{
if (Method != METHOD_STORED) return -1;
if (NeedFileStart) SetLumpAddress();
return Position;
return (int)Position;
}
//==========================================================================

View file

@ -47,8 +47,6 @@
#include "printf.h"
#include "md5.h"
extern FILE* hashfile;
// MACROS ------------------------------------------------------------------
#define NULL_INDEX (0xffffffff)
@ -208,7 +206,7 @@ void FileSystem::InitSingleFile(const char* filename, bool quiet)
InitMultipleFiles(filenames, true);
}
void FileSystem::InitMultipleFiles (TArray<FString> &filenames, bool quiet, LumpFilterInfo* filter)
void FileSystem::InitMultipleFiles (TArray<FString> &filenames, bool quiet, LumpFilterInfo* filter, bool allowduplicates, FILE* hashfile)
{
int numfiles;
@ -217,21 +215,24 @@ void FileSystem::InitMultipleFiles (TArray<FString> &filenames, bool quiet, Lump
numfiles = 0;
// first, check for duplicates
if (!Args->CheckParm("-allowduplicates"))
if (allowduplicates)
{
for (unsigned i=0;i<filenames.Size(); i++)
{
for (unsigned j=i+1;j<filenames.Size(); j++)
{
if (strcmp(filenames[i], filenames[j]) == 0)
{
filenames.Delete(j);
j--;
}
}
}
}
for(unsigned i=0;i<filenames.Size(); i++)
{
AddFile (filenames[i], nullptr, quiet, filter);
AddFile (filenames[i], nullptr, quiet, filter, hashfile);
if (i == (unsigned)MaxIwadIndex) MoveLumpsInFolder("after_iwad/");
FStringf path("filter/%s", Files.Last()->GetHash().GetChars());
@ -307,7 +308,7 @@ int FileSystem::AddFromBuffer(const char* name, const char* type, char* data, in
// [RH] Removed reload hack
//==========================================================================
void FileSystem::AddFile (const char *filename, FileReader *filer, bool quiet, LumpFilterInfo* filter)
void FileSystem::AddFile (const char *filename, FileReader *filer, bool quiet, LumpFilterInfo* filter, FILE* hashfile)
{
int startlump;
bool isdir = false;
@ -375,7 +376,7 @@ void FileSystem::AddFile (const char *filename, FileReader *filer, bool quiet, L
FString path;
path.Format("%s:%s", filename, lump->getName());
auto embedded = lump->NewReader();
AddFile(path, &embedded, quiet, filter);
AddFile(path, &embedded, quiet, filter, hashfile);
}
}

View file

@ -68,8 +68,8 @@ public:
void SetMaxIwadNum(int x) { MaxIwadIndex = x; }
void InitSingleFile(const char *filename, bool quiet = false);
void InitMultipleFiles (TArray<FString> &filenames, bool quiet = false, LumpFilterInfo* filter = nullptr);
void AddFile (const char *filename, FileReader *wadinfo, bool quiet, LumpFilterInfo* filter);
void InitMultipleFiles (TArray<FString> &filenames, bool quiet = false, LumpFilterInfo* filter = nullptr, bool allowduplicates = false, FILE* hashfile = nullptr);
void AddFile (const char *filename, FileReader *wadinfo, bool quiet, LumpFilterInfo* filter, FILE* hashfile);
int CheckIfResourceFileLoaded (const char *name) noexcept;
void AddAdditionalFile(const char* filename, FileReader* wadinfo = NULL) {}

View file

@ -58,11 +58,12 @@ struct HexDataSource
//
//==========================================================================
void ParseDefinition(int lumpnum)
void ParseDefinition(FResourceLump* font)
{
FScanner sc;
sc.OpenLumpNum(lumpnum);
auto data = font->Lock();
sc.OpenMem("newconsolefont.hex", (const char*)data, font->Size());
sc.SetCMode(true);
glyphdata.Push(0); // ensure that index 0 can be used as 'not present'.
while (sc.GetString())
@ -96,6 +97,7 @@ struct HexDataSource
lumb = i * 255 / 17;
SmallPal[i] = PalEntry(255, lumb, lumb, lumb);
}
font->Unlock();
}
};
@ -400,7 +402,7 @@ public:
FFont *CreateHexLumpFont (const char *fontname, int lump)
{
if (hexdata.FirstChar == INT_MAX) hexdata.ParseDefinition(lump);
assert(hexdata.FirstChar != INT_MAX);
return new FHexFont(fontname, lump);
}
@ -412,6 +414,33 @@ FFont *CreateHexLumpFont (const char *fontname, int lump)
FFont *CreateHexLumpFont2(const char *fontname, int lump)
{
if (hexdata.FirstChar == INT_MAX) hexdata.ParseDefinition(lump);
assert(hexdata.FirstChar != INT_MAX);
return new FHexFont2(fontname, lump);
}
//==========================================================================
//
//
//
//==========================================================================
uint8_t* GetHexChar(int codepoint)
{
assert(hexdata.FirstChar != INT_MAX);
if (hexdata.glyphmap[codepoint] > 0)
{
auto offset = hexdata.glyphmap[codepoint];
return &hexdata.glyphdata[offset];
}
return nullptr;
}
void LoadHexFont(const char* filename)
{
auto resf = FResourceFile::OpenResourceFile(filename);
if (resf == nullptr) I_FatalError("Unable to open %s", filename);
auto hexfont = resf->FindLump("newconsolefont.hex");
if (hexfont == nullptr) I_FatalError("Unable to find newconsolefont.hex in %s", filename);
hexdata.ParseDefinition(hexfont);
}

View file

@ -623,13 +623,16 @@ int FOBJModel::FindFrame(const char* name)
*
* @param renderer The model renderer
* @param skin The loaded skin for the surface
* @param frameno Unused
* @param frameno2 Unused
* @param inter Unused
* @param frameno The first frame to interpolate between. Only prevents the model from rendering if it is < 0, since OBJ models are static.
* @param frameno2 The second frame to interpolate between.
* @param inter The amount to interpolate the two frames.
* @param translation The translation for the skin
*/
void FOBJModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation)
{
// Prevent the model from rendering if the frame number is < 0
if (frameno < 0 || frameno2 < 0) return;
for (unsigned int i = 0; i < surfaces.Size(); i++)
{
OBJSurface *surf = &surfaces[i];

View file

@ -493,6 +493,11 @@ DEFINE_ACTION_FUNCTION(DObject, MSTime)
ACTION_RETURN_INT((uint32_t)I_msTime());
}
DEFINE_ACTION_FUNCTION_NATIVE(DObject, MSTimef, I_msTimeF)
{
ACTION_RETURN_FLOAT(I_msTimeF());
}
void *DObject::ScriptVar(FName field, PType *type)
{
auto cls = GetClass();

View file

@ -277,7 +277,7 @@ void MarkArray(DObject **obj, size_t count)
static size_t CalcStepSize()
{
int time_passed = CheckTime - LastCollectTime;
int time_passed = int(CheckTime - LastCollectTime);
auto alloc = min(LastCollectAlloc, Estimate);
size_t bytes_gained = AllocBytes > alloc ? AllocBytes - alloc : 0;
return (StepMul > 0 && time_passed > 0)
@ -390,7 +390,7 @@ static size_t SingleStep()
case GCS_Finalize:
State = GCS_Pause; // end collection
LastCollectAlloc = AllocBytes;
LastCollectTime = CheckTime;
LastCollectTime = (int)CheckTime;
return 0;
default:

View file

@ -183,6 +183,10 @@ public:
TObjPtr() = default;
TObjPtr(const TObjPtr<T> &q) = default;
TObjPtr(T q) noexcept
: pp(q)
{
}
T operator=(T q)
{
pp = q;

View file

@ -42,22 +42,6 @@
#include "engineerrors.h"
FStartupScreen *StartScreen;
CUSTOM_CVAR(Int, showendoom, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < 0)
{
self = 0;
}
else if (self > 2)
{
self = 2;
}
}
// ---------------------------------------------------------------------------
@ -121,18 +105,6 @@ void FBasicStartupScreen::NetProgress(const int count)
FConsoleWindow::GetInstance().NetProgress(count);
}
void FBasicStartupScreen::NetMessage(const char* const format, ...)
{
va_list args;
va_start(args, format);
FString message;
message.VFormat(format, args);
va_end(args);
Printf("%s\n", message.GetChars());
}
void FBasicStartupScreen::NetDone()
{
FConsoleWindow::GetInstance().NetDone();
@ -160,16 +132,7 @@ bool FBasicStartupScreen::NetLoop(bool (*timerCallback)(void*), void* const user
// ---------------------------------------------------------------------------
FStartupScreen *FStartupScreen::CreateInstance(const int maxProgress)
FStartupScreen *FStartupScreen::CreateInstance(const int maxProgress, bool showprogress)
{
return new FBasicStartupScreen(maxProgress, true);
}
// ---------------------------------------------------------------------------
void ST_Endoom()
{
throw CExitEvent(0);
return new FBasicStartupScreen(maxProgress, showprogress);
}

View file

@ -73,8 +73,8 @@ static const SDL_Keycode DIKToKeySym[256] =
SDLK_KP_8, SDLK_KP_9, SDLK_KP_MINUS, SDLK_KP_4, SDLK_KP_5, SDLK_KP_6, SDLK_KP_PLUS, SDLK_KP_1,
SDLK_KP_2, SDLK_KP_3, SDLK_KP_0, SDLK_KP_PERIOD, 0, 0, 0, SDLK_F11,
SDLK_F12, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, SDLK_F13, SDLK_F14, SDLK_F15, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, SDLK_F13, SDLK_F14, SDLK_F15, SDLK_F16,
SDLK_F17, SDLK_F18, SDLK_F19, SDLK_F20, SDLK_F21, SDLK_F22, SDLK_F23, SDLK_F24,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@ -109,8 +109,8 @@ static const SDL_Scancode DIKToKeyScan[256] =
SDL_SCANCODE_KP_8, SDL_SCANCODE_KP_9, SDL_SCANCODE_KP_MINUS, SDL_SCANCODE_KP_4, SDL_SCANCODE_KP_5, SDL_SCANCODE_KP_6, SDL_SCANCODE_KP_PLUS, SDL_SCANCODE_KP_1,
SDL_SCANCODE_KP_2, SDL_SCANCODE_KP_3, SDL_SCANCODE_KP_0, SDL_SCANCODE_KP_PERIOD, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F11,
SDL_SCANCODE_F12, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F13, SDL_SCANCODE_F14, SDL_SCANCODE_F15, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_F13, SDL_SCANCODE_F14, SDL_SCANCODE_F15, SDL_SCANCODE_F16,
SDL_SCANCODE_F17, SDL_SCANCODE_F18, SDL_SCANCODE_F19, SDL_SCANCODE_F20, SDL_SCANCODE_F21, SDL_SCANCODE_F22, SDL_SCANCODE_F23, SDL_SCANCODE_F24,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,
SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN, SDL_SCANCODE_UNKNOWN,

View file

@ -79,7 +79,7 @@ double PerfToSec, PerfToMillisec;
CVAR(Bool, con_printansi, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE);
CVAR(Bool, con_4bitansi, false, CVAR_GLOBALCONFIG|CVAR_ARCHIVE);
extern FStartupScreen *StartScreen;
extern FStartupScreen *StartWindow;
void I_SetIWADInfo()
{
@ -278,10 +278,10 @@ void I_PrintStr(const char *cp)
}
}
if (StartScreen) CleanProgressBar();
if (StartWindow) CleanProgressBar();
fputs(printData.GetChars(),stdout);
if (terminal) fputs("\033[0m",stdout);
if (StartScreen) RedrawProgressBar(ProgressBarCurPos,ProgressBarMaxPos);
if (StartWindow) RedrawProgressBar(ProgressBarCurPos,ProgressBarMaxPos);
}
int I_PickIWad (WadStuff *wads, int numwads, bool showwin, int defaultiwad)

View file

@ -394,9 +394,7 @@ SDLVideo::SDLVideo ()
}
// Fail gracefully if we somehow reach here after linking against a SDL2 library older than 2.0.6.
SDL_version sdlver;
SDL_GetVersion(&sdlver);
if (!(sdlver.patch >= 6))
if (!SDL_VERSION_ATLEAST(2, 0, 6))
{
I_FatalError("Only SDL 2.0.6 or later is supported.");
}

View file

@ -56,7 +56,6 @@ class FTTYStartupScreen : public FStartupScreen
void Progress();
void NetInit(const char *message, int num_players);
void NetProgress(int count);
void NetMessage(const char *format, ...); // cover for printf
void NetDone();
bool NetLoop(bool (*timer_callback)(void *), void *userdata);
protected:
@ -71,22 +70,6 @@ class FTTYStartupScreen : public FStartupScreen
extern void RedrawProgressBar(int CurPos, int MaxPos);
extern void CleanProgressBar();
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
// PUBLIC DATA DEFINITIONS -------------------------------------------------
FStartupScreen *StartScreen;
CUSTOM_CVAR(Int, showendoom, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
if (self < 0) self = 0;
else if (self > 2) self=2;
}
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static const char SpinnyProgressChars[4] = { '|', '/', '-', '\\' };
@ -102,7 +85,7 @@ static const char SpinnyProgressChars[4] = { '|', '/', '-', '\\' };
//
//==========================================================================
FStartupScreen *FStartupScreen::CreateInstance(int max_progress)
FStartupScreen *FStartupScreen::CreateInstance(int max_progress, bool)
{
return new FTTYStartupScreen(max_progress);
}
@ -214,27 +197,6 @@ void FTTYStartupScreen::NetDone()
}
}
//===========================================================================
//
// FTTYStartupScreen :: NetMessage
//
// Call this between NetInit() and NetDone() instead of Printf() to
// display messages, because the progress meter is mixed in the same output
// stream as normal messages.
//
//===========================================================================
void FTTYStartupScreen::NetMessage(const char *format, ...)
{
FString str;
va_list argptr;
va_start (argptr, format);
str.VFormat (format, argptr);
va_end (argptr);
fprintf (stderr, "\r%-40s\n", str.GetChars());
}
//===========================================================================
//
// FTTYStartupScreen :: NetProgress
@ -331,7 +293,3 @@ bool FTTYStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata)
}
}
void ST_Endoom()
{
throw CExitEvent(0);
}

View file

@ -50,9 +50,7 @@
#include "base_sysfb.h"
#include "win32basevideo.h"
#include "c_dispatch.h"
extern HWND Window;
#include "i_mainwindow.h"
extern "C" {
__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001;
@ -85,7 +83,7 @@ EXTERN_CVAR(Int, vid_defheight)
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &displaysettings);
scrwidth = (int)displaysettings.dmPelsWidth;
scrheight = (int)displaysettings.dmPelsHeight;
GetWindowRect(Window, &rect);
GetWindowRect(mainwindow.GetHandle(), &rect);
cx = scrwidth / 2;
cy = scrheight / 2;
if (in_w > 0) winw = in_w;
@ -139,11 +137,11 @@ void SystemBaseFrameBuffer::SaveWindowedPos()
}
// Make sure we only save the window position if it's not fullscreen.
static const int WINDOW_STYLE = WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX;
if ((GetWindowLong(Window, GWL_STYLE) & WINDOW_STYLE) == WINDOW_STYLE)
if ((GetWindowLong(mainwindow.GetHandle(), GWL_STYLE) & WINDOW_STYLE) == WINDOW_STYLE)
{
RECT wrect;
if (GetWindowRect(Window, &wrect))
if (GetWindowRect(mainwindow.GetHandle(), &wrect))
{
// If (win_x,win_y) specify to center the window, don't change them
// if the window is still centered.
@ -171,7 +169,7 @@ void SystemBaseFrameBuffer::SaveWindowedPos()
win_h = wrect.bottom - wrect.top;
}
win_maximized = IsZoomed(Window) == TRUE;
win_maximized = IsZoomed(mainwindow.GetHandle()) == TRUE;
}
}
@ -204,10 +202,10 @@ void SystemBaseFrameBuffer::RestoreWindowedPos()
}
KeepWindowOnScreen(winx, winy, winw, winh, scrwidth, scrheight);
}
SetWindowPos(Window, nullptr, winx, winy, winw, winh, SWP_NOZORDER | SWP_FRAMECHANGED);
SetWindowPos(mainwindow.GetHandle(), nullptr, winx, winy, winw, winh, SWP_NOZORDER | SWP_FRAMECHANGED);
if (win_maximized && !Args->CheckParm("-0"))
ShowWindow(Window, SW_MAXIMIZE);
ShowWindow(mainwindow.GetHandle(), SW_MAXIMIZE);
}
//==========================================================================
@ -226,8 +224,8 @@ void SystemBaseFrameBuffer::SetWindowSize(int w, int h)
{
LONG style = WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW;
LONG exStyle = WS_EX_WINDOWEDGE;
SetWindowLong(Window, GWL_STYLE, style);
SetWindowLong(Window, GWL_EXSTYLE, exStyle);
SetWindowLong(mainwindow.GetHandle(), GWL_STYLE, style);
SetWindowLong(mainwindow.GetHandle(), GWL_EXSTYLE, exStyle);
int winx, winy, winw, winh, scrwidth, scrheight;
@ -250,8 +248,8 @@ void SystemBaseFrameBuffer::SetWindowSize(int w, int h)
if (!vid_fullscreen)
{
ShowWindow(Window, SW_SHOWNORMAL);
SetWindowPos(Window, nullptr, winx, winy, winw, winh, SWP_NOZORDER | SWP_FRAMECHANGED);
ShowWindow(mainwindow.GetHandle(), SW_SHOWNORMAL);
SetWindowPos(mainwindow.GetHandle(), nullptr, winx, winy, winw, winh, SWP_NOZORDER | SWP_FRAMECHANGED);
win_maximized = false;
SetSize(GetClientWidth(), GetClientHeight());
SaveWindowedPos();
@ -302,9 +300,9 @@ void SystemBaseFrameBuffer::PositionWindow(bool fullscreen, bool initialcall)
}
}
ShowWindow(Window, SW_SHOW);
ShowWindow(mainwindow.GetHandle(), SW_SHOW);
GetWindowRect(Window, &r);
GetWindowRect(mainwindow.GetHandle(), &r);
style = WS_VISIBLE | WS_CLIPSIBLINGS;
exStyle = 0;
@ -316,13 +314,13 @@ void SystemBaseFrameBuffer::PositionWindow(bool fullscreen, bool initialcall)
exStyle |= WS_EX_WINDOWEDGE;
}
SetWindowLong(Window, GWL_STYLE, style);
SetWindowLong(Window, GWL_EXSTYLE, exStyle);
SetWindowLong(mainwindow.GetHandle(), GWL_STYLE, style);
SetWindowLong(mainwindow.GetHandle(), GWL_EXSTYLE, exStyle);
if (fullscreen)
{
SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
MoveWindow(Window, monRect.left, monRect.top, monRect.right-monRect.left, monRect.bottom-monRect.top, FALSE);
SetWindowPos(mainwindow.GetHandle(), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
MoveWindow(mainwindow.GetHandle(), monRect.left, monRect.top, monRect.right-monRect.left, monRect.bottom-monRect.top, FALSE);
// And now, seriously, it IS in the right place. Promise.
}
@ -352,9 +350,9 @@ SystemBaseFrameBuffer::SystemBaseFrameBuffer(void *hMonitor, bool fullscreen) :
m_displayDeviceName = 0;
PositionWindow(fullscreen, true);
HDC hDC = GetDC(Window);
HDC hDC = GetDC(mainwindow.GetHandle());
ReleaseDC(Window, hDC);
ReleaseDC(mainwindow.GetHandle(), hDC);
}
//==========================================================================
@ -367,10 +365,10 @@ SystemBaseFrameBuffer::~SystemBaseFrameBuffer()
{
if (!m_Fullscreen) SaveWindowedPos();
ShowWindow (Window, SW_SHOW);
SetWindowLong(Window, GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW);
SetWindowLong(Window, GWL_EXSTYLE, WS_EX_WINDOWEDGE);
SetWindowPos(Window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
ShowWindow (mainwindow.GetHandle(), SW_SHOW);
SetWindowLong(mainwindow.GetHandle(), GWL_STYLE, WS_VISIBLE | WS_CLIPSIBLINGS | WS_OVERLAPPEDWINDOW);
SetWindowLong(mainwindow.GetHandle(), GWL_EXSTYLE, WS_EX_WINDOWEDGE);
SetWindowPos(mainwindow.GetHandle(), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
static_cast<Win32BaseVideo *>(Video)->Shutdown();
}
@ -407,13 +405,13 @@ void SystemBaseFrameBuffer::ToggleFullscreen(bool yes)
int SystemBaseFrameBuffer::GetClientWidth()
{
RECT rect = { 0 };
GetClientRect(Window, &rect);
GetClientRect(mainwindow.GetHandle(), &rect);
return rect.right - rect.left;
}
int SystemBaseFrameBuffer::GetClientHeight()
{
RECT rect = { 0 };
GetClientRect(Window, &rect);
GetClientRect(mainwindow.GetHandle(), &rect);
return rect.bottom - rect.top;
}

View file

@ -49,8 +49,7 @@
#include "m_argv.h"
#include "engineerrors.h"
#include "win32glvideo.h"
extern HWND Window;
#include "i_mainwindow.h"
extern "C" PROC zd_wglGetProcAddress(LPCSTR name);
@ -71,13 +70,13 @@ PFNWGLSWAPINTERVALEXTPROC myWglSwapIntervalExtProc;
SystemGLFrameBuffer::SystemGLFrameBuffer(void *hMonitor, bool fullscreen) : SystemBaseFrameBuffer(hMonitor, fullscreen)
{
if (!static_cast<Win32GLVideo*>(Video)->InitHardware(Window, 0))
if (!static_cast<Win32GLVideo*>(Video)->InitHardware(mainwindow.GetHandle(), 0))
{
I_FatalError("Unable to initialize OpenGL");
return;
}
HDC hDC = GetDC(Window);
HDC hDC = GetDC(mainwindow.GetHandle());
const char *wglext = nullptr;
myWglSwapIntervalExtProc = (PFNWGLSWAPINTERVALEXTPROC)zd_wglGetProcAddress("wglSwapIntervalEXT");
@ -102,7 +101,7 @@ SystemGLFrameBuffer::SystemGLFrameBuffer(void *hMonitor, bool fullscreen) : Syst
SwapInterval = -1;
}
}
ReleaseDC(Window, hDC);
ReleaseDC(mainwindow.GetHandle(), hDC);
}
//==========================================================================

View file

@ -52,11 +52,10 @@
#endif
#include "engineerrors.h"
#include "i_system.h"
#include "i_mainwindow.h"
EXTERN_CVAR(Int, vid_preferbackend)
extern HWND Window;
IVideo *Video;
// do not include GL headers here, only declare the necessary functions.
@ -117,12 +116,12 @@ void I_InitGraphics ()
// If the focus window is destroyed, it doesn't go back to the active window.
// (e.g. because the net pane was up, and a button on it had focus)
if (GetFocus() == NULL && GetActiveWindow() == Window)
if (GetFocus() == NULL && GetActiveWindow() == mainwindow.GetHandle())
{
// Make sure it's in the foreground and focused. (It probably is
// already foregrounded but may not be focused.)
SetForegroundWindow(Window);
SetFocus(Window);
SetForegroundWindow(mainwindow.GetHandle());
SetFocus(mainwindow.GetHandle());
// Note that when I start a 2-player game on the same machine, the
// window for the game that isn't focused, active, or foregrounded
// still receives a WM_ACTIVATEAPP message telling it that it's the

View file

@ -65,6 +65,7 @@
#include "zstring.h"
#include "printf.h"
#include "cmdlib.h"
#include "i_mainwindow.h"
#include <time.h>
#include <zlib.h>
@ -212,9 +213,6 @@ struct MiniDumpThreadData
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
void I_FlushBufferedConsoleStuff();
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static void AddFile (HANDLE file, const char *filename);
@ -445,41 +443,6 @@ void Writef (HANDLE file, const char *format, ...)
//
//==========================================================================
static DWORD CALLBACK WriteLogFileStreamer(DWORD_PTR cookie, LPBYTE buffer, LONG cb, LONG *pcb)
{
DWORD didwrite;
LONG p, pp;
// Replace gray foreground color with black.
static const char *badfg = "\\red223\\green223\\blue223;";
// 4321098 765432109 876543210
// 2 1 0
for (p = pp = 0; p < cb; ++p)
{
if (buffer[p] == badfg[pp])
{
++pp;
if (pp == 25)
{
buffer[p - 1] = buffer[p - 2] = buffer[p - 3] =
buffer[p - 9] = buffer[p -10] = buffer[p -11] =
buffer[p -18] = buffer[p -19] = buffer[p -20] = '0';
break;
}
}
else
{
pp = 0;
}
}
if (!WriteFile((HANDLE)cookie, buffer, cb, &didwrite, NULL))
{
return 1;
}
*pcb = didwrite;
return 0;
}
//==========================================================================
//
@ -489,15 +452,21 @@ static DWORD CALLBACK WriteLogFileStreamer(DWORD_PTR cookie, LPBYTE buffer, LONG
//
//==========================================================================
HANDLE WriteLogFile(HWND edit)
HANDLE WriteLogFile()
{
HANDLE file;
file = CreateTempFile();
if (file != INVALID_HANDLE_VALUE)
{
EDITSTREAM streamer = { (DWORD_PTR)file, 0, WriteLogFileStreamer };
SendMessage(edit, EM_STREAMOUT, SF_RTF, (LPARAM)&streamer);
auto writeFile = [&](const void* data, uint32_t size, uint32_t& written) -> bool
{
DWORD tmp = 0;
BOOL result = WriteFile(file, data, size, &tmp, nullptr);
written = tmp;
return result == TRUE;
};
mainwindow.GetLog(writeFile);
}
return file;
}
@ -510,7 +479,7 @@ HANDLE WriteLogFile(HWND edit)
//
//==========================================================================
void CreateCrashLog (const char *custominfo, DWORD customsize, HWND richlog)
void CreateCrashLog (const char *custominfo, DWORD customsize)
{
// Do not collect information more than once.
if (NumFiles != 0)
@ -560,11 +529,7 @@ void CreateCrashLog (const char *custominfo, DWORD customsize, HWND richlog)
AddFile (file, "local.txt");
}
}
if (richlog != NULL)
{
I_FlushBufferedConsoleStuff();
AddFile (WriteLogFile(richlog), "log.rtf");
}
AddFile (WriteLogFile(), "log.rtf");
CloseHandle (DbgProcess);
}

View file

@ -53,6 +53,8 @@
#include "keydef.h"
#include "printf.h"
#include "i_mainwindow.h"
#define SAFE_RELEASE(x) { if (x != NULL) { x->Release(); x = NULL; } }
// WBEMIDL BITS -- because w32api doesn't have this, either -----------------
@ -266,7 +268,6 @@ protected:
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern LPDIRECTINPUT8 g_pdi;
extern HWND Window;
// PUBLIC DATA DEFINITIONS -------------------------------------------------
@ -372,7 +373,7 @@ bool FDInputJoystick::GetDevice()
Printf(TEXTCOLOR_ORANGE "Setting data format for %s failed.\n", Name.GetChars());
return false;
}
hr = Device->SetCooperativeLevel(Window, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
hr = Device->SetCooperativeLevel(mainwindow.GetHandle(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr))
{
Printf(TEXTCOLOR_ORANGE "Setting cooperative level for %s failed.\n", Name.GetChars());

View file

@ -50,18 +50,6 @@
#pragma warning(disable:4244)
#endif
// Compensate for w32api's lack
#ifndef WM_WTSSESSION_CHANGE
#define WM_WTSSESSION_CHANGE 0x02B1
#define WTS_CONSOLE_CONNECT 1
#define WTS_CONSOLE_DISCONNECT 2
#define WTS_SESSION_LOCK 7
#define WTS_SESSION_UNLOCK 8
#endif
#ifndef PBT_APMSUSPEND
// w32api does not #define the PBT_ macros in winuser.h like the PSDK does
#include <pbt.h>
#endif
#ifndef GET_RAWINPUT_CODE_WPARAM
#define GET_RAWINPUT_CODE_WPARAM(wParam) ((wParam) & 0xff)
#endif
@ -86,6 +74,7 @@
#include "printf.h"
#include "c_buttons.h"
#include "cmdlib.h"
#include "i_mainwindow.h"
// Compensate for w32api's lack
#ifndef GET_XBUTTON_WPARAM
@ -104,8 +93,6 @@ FJoystickCollection *JoyDevices[NUM_JOYDEVICES];
extern HINSTANCE g_hInst;
extern DWORD SessionID;
static HMODULE DInputDLL;
bool GUICapture;
@ -116,7 +103,6 @@ extern bool ToggleFullscreen;
bool VidResizing;
extern BOOL vidactive;
extern HWND Window, ConWindow;
EXTERN_CVAR (String, language)
EXTERN_CVAR (Bool, lookstrafe)
@ -134,7 +120,6 @@ LPDIRECTINPUT g_pdi3;
extern bool AppActive;
int SessionState = 0;
int BlockMouseMove;
static bool EventHandlerResultForNativeMouse;
@ -160,7 +145,7 @@ static void I_CheckGUICapture ()
void I_SetMouseCapture()
{
SetCapture(Window);
SetCapture(mainwindow.GetHandle());
}
void I_ReleaseMouseCapture()
@ -444,7 +429,7 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
break;
case WM_SIZE:
InvalidateRect (Window, NULL, FALSE);
InvalidateRect (hWnd, NULL, FALSE);
break;
case WM_KEYDOWN:
@ -510,65 +495,6 @@ LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
S_SetSoundPaused (wParam);
break;
case WM_WTSSESSION_CHANGE:
case WM_POWERBROADCAST:
{
if (message == WM_WTSSESSION_CHANGE && lParam == (LPARAM)SessionID)
{
#ifdef _DEBUG
OutputDebugStringA ("SessionID matched\n");
#endif
// When using fast user switching, XP will lock a session before
// disconnecting it, and the session will be unlocked before reconnecting it.
// For our purposes, video output will only happen when the session is
// both unlocked and connected (that is, SessionState is 0).
switch (wParam)
{
case WTS_SESSION_LOCK:
SessionState |= 1;
break;
case WTS_SESSION_UNLOCK:
SessionState &= ~1;
break;
case WTS_CONSOLE_DISCONNECT:
SessionState |= 2;
break;
case WTS_CONSOLE_CONNECT:
SessionState &= ~2;
break;
}
}
else if (message == WM_POWERBROADCAST)
{
switch (wParam)
{
case PBT_APMSUSPEND:
SessionState |= 4;
break;
case PBT_APMRESUMESUSPEND:
SessionState &= ~4;
break;
}
}
if (GSnd != NULL)
{
#if 0
// Do we actually need this here?
if (!oldstate && SessionState)
{
GSnd->SuspendSound ();
}
#endif
}
#ifdef _DEBUG
char foo[256];
mysnprintf (foo, countof(foo), "Session Change: %ld %d\n", (long)lParam, (int)wParam);
OutputDebugStringA (foo);
#endif
}
break;
case WM_ERASEBKGND:
return true;
@ -820,7 +746,7 @@ IJoystickConfig *I_UpdateDeviceList()
void I_PutInClipboard (const char *str)
{
if (str == NULL || !OpenClipboard (Window))
if (str == NULL || !OpenClipboard (mainwindow.GetHandle()))
return;
EmptyClipboard ();
@ -842,7 +768,7 @@ FString I_GetFromClipboard (bool return_nothing)
HGLOBAL cliphandle;
wchar_t *clipstr;
if (return_nothing || !IsClipboardFormatAvailable (CF_UNICODETEXT) || !OpenClipboard (Window))
if (return_nothing || !IsClipboardFormatAvailable (CF_UNICODETEXT) || !OpenClipboard (mainwindow.GetHandle()))
return retstr;
cliphandle = GetClipboardData (CF_UNICODETEXT);

View file

@ -82,7 +82,6 @@ protected:
void ClearButtonState();
int WheelMove[2];
int LastX, LastY; // for m_filter
int ButtonState; // bit mask of current button states (1=down, 0=up)
};

View file

@ -40,6 +40,7 @@
#include "i_input.h"
#include "d_eventbase.h"
#include "i_mainwindow.h"
// MACROS ------------------------------------------------------------------
@ -87,7 +88,6 @@ protected:
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern HWND Window;
extern LPDIRECTINPUT8 g_pdi;
extern LPDIRECTINPUT g_pdi3;
extern bool GUICapture;
@ -347,7 +347,7 @@ ufailit:
return false;
}
hr = Device->SetCooperativeLevel(Window, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
hr = Device->SetCooperativeLevel(mainwindow.GetHandle(), DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);
if (FAILED(hr))
{
goto ufailit;
@ -379,7 +379,7 @@ void FDInputKeyboard::ProcessInput()
DIDEVICEOBJECTDATA od;
DWORD dwElements;
HRESULT hr;
bool foreground = (GetForegroundWindow() == Window);
bool foreground = (GetForegroundWindow() == mainwindow.GetHandle());
for (;;)
{
@ -448,7 +448,7 @@ bool FRawKeyboard::GetDevice()
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_KEYBOARD;
rid.dwFlags = RIDEV_INPUTSINK;
rid.hwndTarget = Window;
rid.hwndTarget = mainwindow.GetHandle();
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
return false;

View file

@ -48,9 +48,6 @@
#pragma warning(disable:4244)
#endif
//#include <wtsapi32.h>
#define NOTIFY_FOR_THIS_SESSION 0
#ifdef _MSC_VER
#include <eh.h>
#include <new.h>
@ -80,6 +77,8 @@
#include "startupinfo.h"
#include "printf.h"
#include "i_mainwindow.h"
// MACROS ------------------------------------------------------------------
// The main window's title.
@ -94,9 +93,8 @@
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
void CreateCrashLog (const char *custominfo, DWORD customsize, HWND richedit);
void CreateCrashLog (const char *custominfo, DWORD customsize);
void DisplayCrashLog ();
void I_FlushBufferedConsoleStuff();
void DestroyCustomCursor();
int GameMain();
@ -115,213 +113,13 @@ extern UINT TimerPeriod;
FArgs *Args;
HINSTANCE g_hInst;
DWORD SessionID;
HANDLE MainThread;
DWORD MainThreadID;
HANDLE StdOut;
bool FancyStdOut, AttachedStdOut;
bool ConWindowHidden;
// The main window
HWND Window;
// The subwindows used for startup and error output
HWND ConWindow, GameTitleWindow;
HWND ErrorPane, ProgressBar, NetStartPane, StartupScreen, ErrorIcon;
HFONT GameTitleFont;
LONG GameTitleFontHeight;
LONG DefaultGUIFontHeight;
LONG ErrorIconChar;
FModule Kernel32Module{"Kernel32"};
FModule Shell32Module{"Shell32"};
FModule User32Module{"User32"};
// PRIVATE DATA DEFINITIONS ------------------------------------------------
static const WCHAR WinClassName[] = WGAMENAME "MainWindow";
static HMODULE hwtsapi32; // handle to wtsapi32.dll
// CODE --------------------------------------------------------------------
//==========================================================================
//
// UnCOM
//
// Called by atexit if CoInitialize() succeeded.
//
//==========================================================================
static void UnCOM (void)
{
CoUninitialize ();
}
//==========================================================================
//
// UnWTS
//
// Called by atexit if RegisterSessionNotification() succeeded.
//
//==========================================================================
static void UnWTS (void)
{
if (hwtsapi32 != 0)
{
typedef BOOL (WINAPI *ursn)(HWND);
ursn unreg = (ursn)GetProcAddress (hwtsapi32, "WTSUnRegisterSessionNotification");
if (unreg != 0)
{
unreg (Window);
}
FreeLibrary (hwtsapi32);
hwtsapi32 = 0;
}
}
//==========================================================================
//
// LayoutErrorPane
//
// Lays out the error pane to the desired width, returning the required
// height.
//
//==========================================================================
static int LayoutErrorPane (HWND pane, int w)
{
HWND ctl, ctl_two;
RECT rectc, rectc_two;
// Right-align the Quit button.
ctl = GetDlgItem (pane, IDOK);
GetClientRect (ctl, &rectc); // Find out how big it is.
MoveWindow (ctl, w - rectc.right - 1, 1, rectc.right, rectc.bottom, TRUE);
// Second-right-align the Restart button
ctl_two = GetDlgItem (pane, IDC_BUTTON1);
GetClientRect (ctl_two, &rectc_two); // Find out how big it is.
MoveWindow (ctl_two, w - rectc.right - rectc_two.right - 2, 1, rectc.right, rectc.bottom, TRUE);
InvalidateRect (ctl, NULL, TRUE);
InvalidateRect (ctl_two, NULL, TRUE);
// Return the needed height for this layout
return rectc.bottom + 2;
}
//==========================================================================
//
// LayoutNetStartPane
//
// Lays out the network startup pane to the specified width, returning
// its required height.
//
//==========================================================================
int LayoutNetStartPane (HWND pane, int w)
{
HWND ctl;
RECT margin, rectc;
int staticheight, barheight;
// Determine margin sizes.
SetRect (&margin, 7, 7, 0, 0);
MapDialogRect (pane, &margin);
// Stick the message text in the upper left corner.
ctl = GetDlgItem (pane, IDC_NETSTARTMESSAGE);
GetClientRect (ctl, &rectc);
MoveWindow (ctl, margin.left, margin.top, rectc.right, rectc.bottom, TRUE);
// Stick the count text in the upper right corner.
ctl = GetDlgItem (pane, IDC_NETSTARTCOUNT);
GetClientRect (ctl, &rectc);
MoveWindow (ctl, w - rectc.right - margin.left, margin.top, rectc.right, rectc.bottom, TRUE);
staticheight = rectc.bottom;
// Stretch the progress bar to fill the entire width.
ctl = GetDlgItem (pane, IDC_NETSTARTPROGRESS);
barheight = GetSystemMetrics (SM_CYVSCROLL);
MoveWindow (ctl, margin.left, margin.top*2 + staticheight, w - margin.left*2, barheight, TRUE);
// Center the abort button underneath the progress bar.
ctl = GetDlgItem (pane, IDCANCEL);
GetClientRect (ctl, &rectc);
MoveWindow (ctl, (w - rectc.right) / 2, margin.top*3 + staticheight + barheight, rectc.right, rectc.bottom, TRUE);
return margin.top*4 + staticheight + barheight + rectc.bottom;
}
//==========================================================================
//
// LayoutMainWindow
//
// Lays out the main window with the game title and log controls and
// possibly the error pane and progress bar.
//
//==========================================================================
void LayoutMainWindow (HWND hWnd, HWND pane)
{
RECT rect;
int errorpaneheight = 0;
int bannerheight = 0;
int progressheight = 0;
int netpaneheight = 0;
int leftside = 0;
int w, h;
GetClientRect (hWnd, &rect);
w = rect.right;
h = rect.bottom;
if (GameStartupInfo.Name.IsNotEmpty() && GameTitleWindow != NULL)
{
bannerheight = GameTitleFontHeight + 5;
MoveWindow (GameTitleWindow, 0, 0, w, bannerheight, TRUE);
InvalidateRect (GameTitleWindow, NULL, FALSE);
}
if (ProgressBar != NULL)
{
// Base the height of the progress bar on the height of a scroll bar
// arrow, just as in the progress bar example.
progressheight = GetSystemMetrics (SM_CYVSCROLL);
MoveWindow (ProgressBar, 0, h - progressheight, w, progressheight, TRUE);
}
if (NetStartPane != NULL)
{
netpaneheight = LayoutNetStartPane (NetStartPane, w);
SetWindowPos (NetStartPane, HWND_TOP, 0, h - progressheight - netpaneheight, w, netpaneheight, SWP_SHOWWINDOW);
}
if (pane != NULL)
{
errorpaneheight = LayoutErrorPane (pane, w);
SetWindowPos (pane, HWND_TOP, 0, h - progressheight - netpaneheight - errorpaneheight, w, errorpaneheight, 0);
}
if (ErrorIcon != NULL)
{
leftside = GetSystemMetrics (SM_CXICON) + 6;
MoveWindow (ErrorIcon, 0, bannerheight, leftside, h - bannerheight - errorpaneheight - progressheight - netpaneheight, TRUE);
}
// If there is a startup screen, it covers the log window
if (StartupScreen != NULL)
{
SetWindowPos (StartupScreen, HWND_TOP, leftside, bannerheight, w - leftside,
h - bannerheight - errorpaneheight - progressheight - netpaneheight, SWP_SHOWWINDOW);
InvalidateRect (StartupScreen, NULL, FALSE);
MoveWindow (ConWindow, 0, 0, 0, 0, TRUE);
}
else
{
// The log window uses whatever space is left.
MoveWindow (ConWindow, leftside, bannerheight, w - leftside,
h - bannerheight - errorpaneheight - progressheight - netpaneheight, TRUE);
}
}
//==========================================================================
//
@ -332,436 +130,7 @@ void LayoutMainWindow (HWND hWnd, HWND pane)
void I_SetIWADInfo()
{
// Make the startup banner show itself
LayoutMainWindow(Window, NULL);
}
//==========================================================================
//
// LConProc
//
// The main window's WndProc during startup. During gameplay, the WndProc
// in i_input.cpp is used instead.
//
//==========================================================================
LRESULT CALLBACK LConProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND view;
HDC hdc;
HBRUSH hbr;
HGDIOBJ oldfont;
RECT rect;
SIZE size;
LOGFONT lf;
TEXTMETRIC tm;
HINSTANCE inst = (HINSTANCE)(LONG_PTR)GetWindowLongPtr(hWnd, GWLP_HINSTANCE);
DRAWITEMSTRUCT *drawitem;
CHARFORMAT2W format;
switch (msg)
{
case WM_CREATE:
// Create game title static control
memset (&lf, 0, sizeof(lf));
hdc = GetDC (hWnd);
lf.lfHeight = -MulDiv(12, GetDeviceCaps(hdc, LOGPIXELSY), 72);
lf.lfCharSet = ANSI_CHARSET;
lf.lfWeight = FW_BOLD;
lf.lfPitchAndFamily = VARIABLE_PITCH | FF_ROMAN;
wcscpy (lf.lfFaceName, L"Trebuchet MS");
GameTitleFont = CreateFontIndirect (&lf);
oldfont = SelectObject (hdc, GetStockObject (DEFAULT_GUI_FONT));
GetTextMetrics (hdc, &tm);
DefaultGUIFontHeight = tm.tmHeight;
if (GameTitleFont == NULL)
{
GameTitleFontHeight = DefaultGUIFontHeight;
}
else
{
SelectObject (hdc, GameTitleFont);
GetTextMetrics (hdc, &tm);
GameTitleFontHeight = tm.tmHeight;
}
SelectObject (hdc, oldfont);
// Create log read-only edit control
view = CreateWindowExW (WS_EX_NOPARENTNOTIFY, L"RichEdit20W", nullptr,
WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | WS_CLIPSIBLINGS,
0, 0, 0, 0,
hWnd, NULL, inst, NULL);
HRESULT hr;
hr = GetLastError();
if (view == NULL)
{
ReleaseDC (hWnd, hdc);
return -1;
}
SendMessage (view, EM_SETREADONLY, TRUE, 0);
SendMessage (view, EM_EXLIMITTEXT, 0, 0x7FFFFFFE);
SendMessage (view, EM_SETBKGNDCOLOR, 0, RGB(70,70,70));
// Setup default font for the log.
//SendMessage (view, WM_SETFONT, (WPARAM)GetStockObject (DEFAULT_GUI_FONT), FALSE);
format.cbSize = sizeof(format);
format.dwMask = CFM_BOLD | CFM_COLOR | CFM_FACE | CFM_SIZE | CFM_CHARSET;
format.dwEffects = 0;
format.yHeight = 200;
format.crTextColor = RGB(223,223,223);
format.bCharSet = ANSI_CHARSET;
format.bPitchAndFamily = FF_SWISS | VARIABLE_PITCH;
wcscpy(format.szFaceName, L"DejaVu Sans"); // At least I have it. :p
SendMessageW(view, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&format);
ConWindow = view;
ReleaseDC (hWnd, hdc);
view = CreateWindowExW (WS_EX_NOPARENTNOTIFY, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_OWNERDRAW, 0, 0, 0, 0, hWnd, nullptr, inst, nullptr);
if (view == nullptr)
{
return -1;
}
SetWindowLong (view, GWL_ID, IDC_STATIC_TITLE);
GameTitleWindow = view;
return 0;
case WM_SIZE:
if (wParam != SIZE_MAXHIDE && wParam != SIZE_MAXSHOW)
{
LayoutMainWindow (hWnd, ErrorPane);
}
return 0;
case WM_DRAWITEM:
// Draw title banner.
if (wParam == IDC_STATIC_TITLE && GameStartupInfo.Name.IsNotEmpty())
{
const PalEntry *c;
// Draw the game title strip at the top of the window.
drawitem = (LPDRAWITEMSTRUCT)lParam;
// Draw the background.
rect = drawitem->rcItem;
rect.bottom -= 1;
c = (const PalEntry *)&GameStartupInfo.BkColor;
hbr = CreateSolidBrush (RGB(c->r,c->g,c->b));
FillRect (drawitem->hDC, &drawitem->rcItem, hbr);
DeleteObject (hbr);
// Calculate width of the title string.
SetTextAlign (drawitem->hDC, TA_TOP);
oldfont = SelectObject (drawitem->hDC, GameTitleFont != NULL ? GameTitleFont : (HFONT)GetStockObject (DEFAULT_GUI_FONT));
auto widename = GameStartupInfo.Name.WideString();
GetTextExtentPoint32W (drawitem->hDC, widename.c_str(), (int)widename.length(), &size);
// Draw the title.
c = (const PalEntry *)&GameStartupInfo.FgColor;
SetTextColor (drawitem->hDC, RGB(c->r,c->g,c->b));
SetBkMode (drawitem->hDC, TRANSPARENT);
TextOutW (drawitem->hDC, rect.left + (rect.right - rect.left - size.cx) / 2, 2, widename.c_str(), (int)widename.length());
SelectObject (drawitem->hDC, oldfont);
return TRUE;
}
// Draw startup screen
else if (wParam == IDC_STATIC_STARTUP)
{
if (StartupScreen != NULL)
{
drawitem = (LPDRAWITEMSTRUCT)lParam;
rect = drawitem->rcItem;
// Windows expects DIBs to be bottom-up but ours is top-down,
// so flip it vertically while drawing it.
StretchDIBits (drawitem->hDC, rect.left, rect.bottom - 1, rect.right - rect.left, rect.top - rect.bottom,
0, 0, StartupBitmap->bmiHeader.biWidth, StartupBitmap->bmiHeader.biHeight,
ST_Util_BitsForBitmap(StartupBitmap), reinterpret_cast<const BITMAPINFO*>(StartupBitmap), DIB_RGB_COLORS, SRCCOPY);
// If the title banner is gone, then this is an ENDOOM screen, so draw a short prompt
// where the command prompt would have been in DOS.
if (GameTitleWindow == NULL)
{
auto quitmsg = WideString(GStrings("TXT_QUITENDOOM"));
SetTextColor (drawitem->hDC, RGB(240,240,240));
SetBkMode (drawitem->hDC, TRANSPARENT);
oldfont = SelectObject (drawitem->hDC, (HFONT)GetStockObject (DEFAULT_GUI_FONT));
TextOutW (drawitem->hDC, 3, drawitem->rcItem.bottom - DefaultGUIFontHeight - 3, quitmsg.c_str(), (int)quitmsg.length());
SelectObject (drawitem->hDC, oldfont);
}
return TRUE;
}
}
// Draw stop icon.
else if (wParam == IDC_ICONPIC)
{
HICON icon;
POINTL char_pos;
drawitem = (LPDRAWITEMSTRUCT)lParam;
// This background color should match the edit control's.
hbr = CreateSolidBrush (RGB(70,70,70));
FillRect (drawitem->hDC, &drawitem->rcItem, hbr);
DeleteObject (hbr);
// Draw the icon aligned with the first line of error text.
SendMessage (ConWindow, EM_POSFROMCHAR, (WPARAM)&char_pos, ErrorIconChar);
icon = (HICON)LoadImage (0, IDI_ERROR, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
DrawIcon (drawitem->hDC, 6, char_pos.y, icon);
return TRUE;
}
return FALSE;
case WM_COMMAND:
if (ErrorIcon != NULL && (HWND)lParam == ConWindow && HIWORD(wParam) == EN_UPDATE)
{
// Be sure to redraw the error icon if the edit control changes.
InvalidateRect (ErrorIcon, NULL, TRUE);
return 0;
}
break;
case WM_CLOSE:
PostQuitMessage (0);
break;
case WM_DESTROY:
if (GameTitleFont != NULL)
{
DeleteObject (GameTitleFont);
}
break;
}
return DefWindowProc (hWnd, msg, wParam, lParam);
}
//==========================================================================
//
// ErrorPaneProc
//
// DialogProc for the error pane.
//
//==========================================================================
bool restartrequest;
void CheckForRestart()
{
if (restartrequest)
{
HMODULE hModule = GetModuleHandleW(NULL);
WCHAR path[MAX_PATH];
GetModuleFileNameW(hModule, path, MAX_PATH);
ShellExecuteW(NULL, L"open", path, GetCommandLineW(), NULL, SW_SHOWNORMAL);
}
restartrequest = false;
}
INT_PTR CALLBACK ErrorPaneProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
// Appear in the main window.
LayoutMainWindow (GetParent (hDlg), hDlg);
return TRUE;
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED)
{
if (LOWORD(wParam) == IDC_BUTTON1) // we pressed the restart button, so run GZDoom again
{
restartrequest = true;
}
PostQuitMessage (0);
return TRUE;
}
break;
}
return FALSE;
}
//==========================================================================
//
// I_SetWndProc
//
// Sets the main WndProc, hides all the child windows, and starts up
// in-game input.
//
//==========================================================================
void I_SetWndProc()
{
if (GetWindowLongPtr (Window, GWLP_USERDATA) == 0)
{
SetWindowLongPtr (Window, GWLP_USERDATA, 1);
SetWindowLongPtr (Window, GWLP_WNDPROC, (WLONG_PTR)WndProc);
ShowWindow (ConWindow, SW_HIDE);
ShowWindow(ProgressBar, SW_HIDE);
ConWindowHidden = true;
ShowWindow (GameTitleWindow, SW_HIDE);
I_InitInput (Window);
}
}
//==========================================================================
//
// RestoreConView
//
// Returns the main window to its startup state.
//
//==========================================================================
void RestoreConView()
{
HDC screenDC = GetDC(0);
int dpi = GetDeviceCaps(screenDC, LOGPIXELSX);
ReleaseDC(0, screenDC);
int width = (512 * dpi + 96 / 2) / 96;
int height = (384 * dpi + 96 / 2) / 96;
// Make sure the window has a frame in case it was fullscreened.
SetWindowLongPtr (Window, GWL_STYLE, WS_VISIBLE|WS_OVERLAPPEDWINDOW);
if (GetWindowLong (Window, GWL_EXSTYLE) & WS_EX_TOPMOST)
{
SetWindowPos (Window, HWND_BOTTOM, 0, 0, width, height,
SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOMOVE);
SetWindowPos (Window, HWND_TOP, 0, 0, 0, 0, SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOSIZE);
}
else
{
SetWindowPos (Window, NULL, 0, 0, width, height,
SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER);
}
SetWindowLongPtr (Window, GWLP_WNDPROC, (WLONG_PTR)LConProc);
ShowWindow (ConWindow, SW_SHOW);
ConWindowHidden = false;
ShowWindow (GameTitleWindow, SW_SHOW);
I_ShutdownInput (); // Make sure the mouse pointer is available.
// Make sure the progress bar isn't visible.
DeleteStartupScreen();
}
//==========================================================================
//
// ShowErrorPane
//
// Shows an error message, preferably in the main window, but it can
// use a normal message box too.
//
//==========================================================================
void ShowErrorPane(const char *text)
{
auto widetext = WideString(text);
if (Window == nullptr || ConWindow == nullptr)
{
if (text != NULL)
{
MessageBoxW (Window, widetext.c_str(),
WGAMENAME " Fatal Error", MB_OK|MB_ICONSTOP|MB_TASKMODAL);
}
return;
}
if (StartScreen != NULL) // Ensure that the network pane is hidden.
{
StartScreen->NetDone();
}
if (text != NULL)
{
FStringf caption("Fatal Error - " GAMENAME " %s " X64 " (%s)", GetVersionString(), GetGitTime());
auto wcaption = caption.WideString();
SetWindowTextW (Window, wcaption.c_str());
ErrorIcon = CreateWindowExW (WS_EX_NOPARENTNOTIFY, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_OWNERDRAW, 0, 0, 0, 0, Window, NULL, g_hInst, NULL);
if (ErrorIcon != NULL)
{
SetWindowLong (ErrorIcon, GWL_ID, IDC_ICONPIC);
}
}
ErrorPane = CreateDialogParam (g_hInst, MAKEINTRESOURCE(IDD_ERRORPANE), Window, ErrorPaneProc, (LONG_PTR)NULL);
if (text != NULL)
{
CHARRANGE end;
CHARFORMAT2 oldformat, newformat;
PARAFORMAT2 paraformat;
// Append the error message to the log.
end.cpMax = end.cpMin = GetWindowTextLength (ConWindow);
SendMessage (ConWindow, EM_EXSETSEL, 0, (LPARAM)&end);
// Remember current charformat.
oldformat.cbSize = sizeof(oldformat);
SendMessage (ConWindow, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&oldformat);
// Use bigger font and standout colors.
newformat.cbSize = sizeof(newformat);
newformat.dwMask = CFM_BOLD | CFM_COLOR | CFM_SIZE;
newformat.dwEffects = CFE_BOLD;
newformat.yHeight = oldformat.yHeight * 5 / 4;
newformat.crTextColor = RGB(255,170,170);
SendMessage (ConWindow, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&newformat);
// Indent the rest of the text to make the error message stand out a little more.
paraformat.cbSize = sizeof(paraformat);
paraformat.dwMask = PFM_STARTINDENT | PFM_OFFSETINDENT | PFM_RIGHTINDENT;
paraformat.dxStartIndent = paraformat.dxOffset = paraformat.dxRightIndent = 120;
SendMessage (ConWindow, EM_SETPARAFORMAT, 0, (LPARAM)&paraformat);
SendMessageW (ConWindow, EM_REPLACESEL, FALSE, (LPARAM)L"\n");
// Find out where the error lines start for the error icon display control.
SendMessage (ConWindow, EM_EXGETSEL, 0, (LPARAM)&end);
ErrorIconChar = end.cpMax;
// Now start adding the actual error message.
SendMessageW (ConWindow, EM_REPLACESEL, FALSE, (LPARAM)L"Execution could not continue.\n\n");
// Restore old charformat but with light yellow text.
oldformat.crTextColor = RGB(255,255,170);
SendMessage (ConWindow, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&oldformat);
// Add the error text.
SendMessageW (ConWindow, EM_REPLACESEL, FALSE, (LPARAM)widetext.c_str());
// Make sure the error text is not scrolled below the window.
SendMessage (ConWindow, EM_LINESCROLL, 0, SendMessage (ConWindow, EM_GETLINECOUNT, 0, 0));
// The above line scrolled everything off the screen, so pretend to move the scroll
// bar thumb, which clamps to not show any extra lines if it doesn't need to.
SendMessage (ConWindow, EM_SCROLL, SB_PAGEDOWN, 0);
}
BOOL bRet;
MSG msg;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
MessageBoxW (Window, widetext.c_str(), WGAMENAME " Fatal Error", MB_OK|MB_ICONSTOP|MB_TASKMODAL);
return;
}
else if (!IsDialogMessage (ErrorPane, &msg))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
}
void PeekThreadedErrorPane()
{
// Allow SendMessage from another thread to call its message handler so that it can display the crash dialog
MSG msg;
PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE);
}
static void UnTbp()
{
timeEndPeriod(TimerPeriod);
mainwindow.UpdateLayout();
}
//==========================================================================
@ -788,16 +157,6 @@ int DoMain (HINSTANCE hInstance)
Args->AppendArg(FString(wargv[i]));
}
// Load Win32 modules
Kernel32Module.Load({"kernel32.dll"});
Shell32Module.Load({"shell32.dll"});
User32Module.Load({"user32.dll"});
// Under XP, get our session ID so we can know when the user changes/locks sessions.
// Since we need to remain binary compatible with older versions of Windows, we
// need to extract the ProcessIdToSessionId function from kernel32.dll manually.
HMODULE kernel = GetModuleHandleA ("kernel32.dll");
if (Args->CheckParm("-stdout"))
{
// As a GUI application, we don't normally get a console when we start.
@ -833,26 +192,17 @@ int DoMain (HINSTANCE hInstance)
StdOut = GetStdHandle(STD_OUTPUT_HANDLE);
}
// These two functions do not exist in Windows XP.
BOOL (WINAPI* p_GetCurrentConsoleFontEx)(HANDLE hConsoleOutput, BOOL bMaximumWindow, PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx);
BOOL (WINAPI* p_SetCurrentConsoleFontEx)(HANDLE hConsoleOutput, BOOL bMaximumWindow, PCONSOLE_FONT_INFOEX lpConsoleCurrentFontEx);
p_SetCurrentConsoleFontEx = (decltype(p_SetCurrentConsoleFontEx))GetProcAddress(kernel, "SetCurrentConsoleFontEx");
p_GetCurrentConsoleFontEx = (decltype(p_GetCurrentConsoleFontEx))GetProcAddress(kernel, "GetCurrentConsoleFontEx");
if (p_SetCurrentConsoleFontEx && p_GetCurrentConsoleFontEx)
// Deprecated stuff for legacy consoles. As of now this is still relevant, but this code can be removed once Windows 7 is no longer relevant.
CONSOLE_FONT_INFOEX cfi;
cfi.cbSize = sizeof(cfi);
if (GetCurrentConsoleFontEx(StdOut, false, &cfi))
{
CONSOLE_FONT_INFOEX cfi;
cfi.cbSize = sizeof(cfi);
if (p_GetCurrentConsoleFontEx(StdOut, false, &cfi))
if (*cfi.FaceName == 0) // If the face name is empty, the default (useless) raster font is actoive.
{
if (*cfi.FaceName == 0) // If the face name is empty, the default (useless) raster font is actoive.
{
//cfi.dwFontSize = { 8, 14 };
wcscpy(cfi.FaceName, L"Lucida Console");
cfi.FontFamily = FF_DONTCARE;
p_SetCurrentConsoleFontEx(StdOut, false, &cfi);
}
//cfi.dwFontSize = { 8, 14 };
wcscpy(cfi.FaceName, L"Lucida Console");
cfi.FontFamily = FF_DONTCARE;
SetCurrentConsoleFontEx(StdOut, false, &cfi);
}
}
FancyStdOut = true;
@ -866,7 +216,7 @@ int DoMain (HINSTANCE hInstance)
TimerPeriod = tc.wPeriodMin;
timeBeginPeriod (TimerPeriod);
atexit(UnTbp);
atexit([](){ timeEndPeriod(TimerPeriod); });
// Figure out what directory the program resides in.
WCHAR progbuff[1024];
@ -904,82 +254,20 @@ int DoMain (HINSTANCE hInstance)
x = y = 0;
}
WNDCLASS WndClass;
WndClass.style = 0;
WndClass.lpfnWndProc = LConProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(IDI_ICON1));
WndClass.hCursor = LoadCursor (NULL, IDC_ARROW);
WndClass.hbrBackground = NULL;
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = WinClassName;
/* register this new class with Windows */
if (!RegisterClass((LPWNDCLASS)&WndClass))
{
MessageBoxA(nullptr, "Could not register window class", "Fatal", MB_ICONEXCLAMATION|MB_OK);
exit(-1);
}
/* create window */
FStringf caption("" GAMENAME " %s " X64 " (%s)", GetVersionString(), GetGitTime());
std::wstring wcaption = caption.WideString();
Window = CreateWindowExW(
WS_EX_APPWINDOW,
WinClassName,
wcaption.c_str(),
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
x, y, width, height,
(HWND) NULL,
(HMENU) NULL,
hInstance,
NULL);
mainwindow.Create(caption, x, y, width, height);
if (!Window)
{
MessageBoxA(nullptr, "Unable to create main window", "Fatal", MB_ICONEXCLAMATION|MB_OK);
exit(-1);
}
if (kernel != NULL)
{
typedef BOOL (WINAPI *pts)(DWORD, DWORD *);
pts pidsid = (pts)GetProcAddress (kernel, "ProcessIdToSessionId");
if (pidsid != 0)
{
if (!pidsid (GetCurrentProcessId(), &SessionID))
{
SessionID = 0;
}
hwtsapi32 = LoadLibraryA ("wtsapi32.dll");
if (hwtsapi32 != 0)
{
FARPROC reg = GetProcAddress (hwtsapi32, "WTSRegisterSessionNotification");
if (reg == 0 || !((BOOL(WINAPI *)(HWND, DWORD))reg) (Window, NOTIFY_FOR_THIS_SESSION))
{
FreeLibrary (hwtsapi32);
hwtsapi32 = 0;
}
else
{
atexit (UnWTS);
}
}
}
}
GetClientRect (Window, &cRect);
GetClientRect (mainwindow.GetHandle(), &cRect);
WinWidth = cRect.right;
WinHeight = cRect.bottom;
CoInitialize (NULL);
atexit (UnCOM);
atexit ([](){ CoUninitialize(); }); // beware of calling convention.
int ret = GameMain ();
CheckForRestart();
mainwindow.CheckForRestart();
DestroyCustomCursor();
if (ret == 1337) // special exit code for 'norun'.
@ -991,7 +279,7 @@ int DoMain (HINSTANCE hInstance)
DWORD bytes;
HANDLE stdinput = GetStdHandle(STD_INPUT_HANDLE);
ShowWindow(Window, SW_HIDE);
ShowWindow(mainwindow.GetHandle(), SW_HIDE);
WriteFile(StdOut, "Press any key to exit...", 24, &bytes, NULL);
FlushConsoleInputBuffer(stdinput);
SetConsoleMode(stdinput, 0);
@ -999,7 +287,7 @@ int DoMain (HINSTANCE hInstance)
}
else if (StdOut == NULL)
{
ShowErrorPane(NULL);
mainwindow.ShowErrorPane(nullptr);
}
}
}
@ -1009,9 +297,8 @@ int DoMain (HINSTANCE hInstance)
void I_ShowFatalError(const char *msg)
{
I_ShutdownGraphics ();
RestoreConView ();
mainwindow.RestoreConView();
S_StopMusic(true);
I_FlushBufferedConsoleStuff();
if (CVMAbortException::stacktrace.IsNotEmpty())
{
@ -1020,7 +307,7 @@ void I_ShowFatalError(const char *msg)
if (!batchrun)
{
ShowErrorPane(msg);
mainwindow.ShowErrorPane(msg);
}
else
{
@ -1087,7 +374,7 @@ void CALLBACK ExitFatally (ULONG_PTR dummy)
{
SetUnhandledExceptionFilter (ExitMessedUp);
I_ShutdownGraphics ();
RestoreConView ();
mainwindow.RestoreConView ();
DisplayCrashLog ();
exit(-1);
}
@ -1122,7 +409,7 @@ LONG WINAPI CatchAllExceptions (LPEXCEPTION_POINTERS info)
CrashPointers = *info;
if (sysCallbacks.CrashInfo && custominfo) sysCallbacks.CrashInfo(custominfo, 16384, "\r\n");
CreateCrashLog (custominfo, (DWORD)strlen(custominfo), ConWindow);
CreateCrashLog (custominfo, (DWORD)strlen(custominfo));
// If the main thread crashed, then make it clean up after itself.
// Otherwise, put the crashing thread to sleep and signal the main thread to clean up.
@ -1208,7 +495,7 @@ int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE nothing, LPWSTR cmdline, int
*(int *)0 = 0;
}
__except(CrashPointers = *GetExceptionInformation(),
CreateCrashLog ("TestCrash", 9, NULL), EXCEPTION_EXECUTE_HANDLER)
CreateCrashLog ("TestCrash", 9), EXCEPTION_EXECUTE_HANDLER)
{
}
DisplayCrashLog ();
@ -1221,7 +508,7 @@ int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE nothing, LPWSTR cmdline, int
infiniterecursion(1);
}
__except(CrashPointers = *GetExceptionInformation(),
CreateCrashLog ("TestStackCrash", 14, NULL), EXCEPTION_EXECUTE_HANDLER)
CreateCrashLog ("TestStackCrash", 14), EXCEPTION_EXECUTE_HANDLER)
{
}
DisplayCrashLog ();
@ -1277,15 +564,5 @@ int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE nothing, LPWSTR cmdline, int
// each platform has its own specific version of this function.
void I_SetWindowTitle(const char* caption)
{
std::wstring widecaption;
if (!caption)
{
FStringf default_caption("" GAMENAME " %s " X64 " (%s)", GetVersionString(), GetGitTime());
widecaption = default_caption.WideString();
}
else
{
widecaption = WideString(caption);
}
SetWindowText(Window, widecaption.c_str());
mainwindow.SetWindowTitle(caption);
}

View file

@ -0,0 +1,881 @@
#include "i_mainwindow.h"
#include "resource.h"
#include "startupinfo.h"
#include "gstrings.h"
#include "palentry.h"
#include "st_start.h"
#include "i_input.h"
#include "version.h"
#include "utf8.h"
#include "v_font.h"
#include "i_net.h"
#include <richedit.h>
#include <shellapi.h>
#include <commctrl.h>
#ifdef _M_X64
#define X64 " 64-bit"
#else
#define X64 ""
#endif
MainWindow mainwindow;
void MainWindow::Create(const FString& caption, int x, int y, int width, int height)
{
static const WCHAR WinClassName[] = L"MainWindow";
HINSTANCE hInstance = GetModuleHandle(0);
WNDCLASS WndClass;
WndClass.style = 0;
WndClass.lpfnWndProc = LConProc;
WndClass.cbClsExtra = 0;
WndClass.cbWndExtra = 0;
WndClass.hInstance = hInstance;
WndClass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
WndClass.hbrBackground = NULL;
WndClass.lpszMenuName = NULL;
WndClass.lpszClassName = WinClassName;
/* register this new class with Windows */
if (!RegisterClass((LPWNDCLASS)&WndClass))
{
MessageBoxA(nullptr, "Could not register window class", "Fatal", MB_ICONEXCLAMATION | MB_OK);
exit(-1);
}
std::wstring wcaption = caption.WideString();
Window = CreateWindowExW(
WS_EX_APPWINDOW,
WinClassName,
wcaption.c_str(),
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
x, y, width, height,
(HWND)NULL,
(HMENU)NULL,
hInstance,
NULL);
if (!Window)
{
MessageBoxA(nullptr, "Unable to create main window", "Fatal", MB_ICONEXCLAMATION | MB_OK);
exit(-1);
}
}
void MainWindow::HideGameTitleWindow()
{
if (GameTitleWindow != 0)
{
DestroyWindow(GameTitleWindow);
GameTitleWindow = 0;
}
}
int MainWindow::GetGameTitleWindowHeight()
{
if (GameTitleWindow != 0)
{
RECT rect;
GetClientRect(GameTitleWindow, &rect);
return rect.bottom;
}
else
{
return 0;
}
}
// Sets the main WndProc, hides all the child windows, and starts up in-game input.
void MainWindow::ShowGameView()
{
if (GetWindowLongPtr(Window, GWLP_USERDATA) == 0)
{
SetWindowLongPtr(Window, GWLP_USERDATA, 1);
SetWindowLongPtr(Window, GWLP_WNDPROC, (LONG_PTR)WndProc);
ShowWindow(ConWindow, SW_HIDE);
ShowWindow(ProgressBar, SW_HIDE);
ConWindowHidden = true;
ShowWindow(GameTitleWindow, SW_HIDE);
I_InitInput(Window);
}
}
// Returns the main window to its startup state.
void MainWindow::RestoreConView()
{
HDC screenDC = GetDC(0);
int dpi = GetDeviceCaps(screenDC, LOGPIXELSX);
ReleaseDC(0, screenDC);
int width = (512 * dpi + 96 / 2) / 96;
int height = (384 * dpi + 96 / 2) / 96;
// Make sure the window has a frame in case it was fullscreened.
SetWindowLongPtr(Window, GWL_STYLE, WS_VISIBLE | WS_OVERLAPPEDWINDOW);
if (GetWindowLong(Window, GWL_EXSTYLE) & WS_EX_TOPMOST)
{
SetWindowPos(Window, HWND_BOTTOM, 0, 0, width, height, SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOMOVE);
SetWindowPos(Window, HWND_TOP, 0, 0, 0, 0, SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOSIZE);
}
else
{
SetWindowPos(Window, NULL, 0, 0, width, height, SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER);
}
SetWindowLongPtr(Window, GWLP_WNDPROC, (LONG_PTR)LConProc);
ShowWindow(ConWindow, SW_SHOW);
ConWindowHidden = false;
ShowWindow(GameTitleWindow, SW_SHOW);
I_ShutdownInput(); // Make sure the mouse pointer is available.
// Make sure the progress bar isn't visible.
DeleteStartupScreen();
FlushBufferedConsoleStuff();
}
// Shows an error message, preferably in the main window, but it can use a normal message box too.
void MainWindow::ShowErrorPane(const char* text)
{
auto widetext = WideString(text);
if (Window == nullptr || ConWindow == nullptr)
{
if (text != NULL)
{
FStringf caption("Fatal Error - " GAMENAME " %s " X64 " (%s)", GetVersionString(), GetGitTime());
MessageBoxW(Window, widetext.c_str(),caption.WideString().c_str(), MB_OK | MB_ICONSTOP | MB_TASKMODAL);
}
return;
}
if (StartWindow != NULL) // Ensure that the network pane is hidden.
{
I_NetDone();
}
if (text != NULL)
{
FStringf caption("Fatal Error - " GAMENAME " %s " X64 " (%s)", GetVersionString(), GetGitTime());
auto wcaption = caption.WideString();
SetWindowTextW(Window, wcaption.c_str());
ErrorIcon = CreateWindowExW(WS_EX_NOPARENTNOTIFY, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_OWNERDRAW, 0, 0, 0, 0, Window, NULL, GetModuleHandle(0), NULL);
if (ErrorIcon != NULL)
{
SetWindowLong(ErrorIcon, GWL_ID, IDC_ICONPIC);
}
}
ErrorPane = CreateDialogParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_ERRORPANE), Window, ErrorPaneProc, (LONG_PTR)NULL);
if (text != NULL)
{
CHARRANGE end;
CHARFORMAT2 oldformat, newformat;
PARAFORMAT2 paraformat;
// Append the error message to the log.
end.cpMax = end.cpMin = GetWindowTextLength(ConWindow);
SendMessage(ConWindow, EM_EXSETSEL, 0, (LPARAM)&end);
// Remember current charformat.
oldformat.cbSize = sizeof(oldformat);
SendMessage(ConWindow, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&oldformat);
// Use bigger font and standout colors.
newformat.cbSize = sizeof(newformat);
newformat.dwMask = CFM_BOLD | CFM_COLOR | CFM_SIZE;
newformat.dwEffects = CFE_BOLD;
newformat.yHeight = oldformat.yHeight * 5 / 4;
newformat.crTextColor = RGB(255, 170, 170);
SendMessage(ConWindow, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&newformat);
// Indent the rest of the text to make the error message stand out a little more.
paraformat.cbSize = sizeof(paraformat);
paraformat.dwMask = PFM_STARTINDENT | PFM_OFFSETINDENT | PFM_RIGHTINDENT;
paraformat.dxStartIndent = paraformat.dxOffset = paraformat.dxRightIndent = 120;
SendMessage(ConWindow, EM_SETPARAFORMAT, 0, (LPARAM)&paraformat);
SendMessageW(ConWindow, EM_REPLACESEL, FALSE, (LPARAM)L"\n");
// Find out where the error lines start for the error icon display control.
SendMessage(ConWindow, EM_EXGETSEL, 0, (LPARAM)&end);
ErrorIconChar = end.cpMax;
// Now start adding the actual error message.
SendMessageW(ConWindow, EM_REPLACESEL, FALSE, (LPARAM)L"Execution could not continue.\n\n");
// Restore old charformat but with light yellow text.
oldformat.crTextColor = RGB(255, 255, 170);
SendMessage(ConWindow, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&oldformat);
// Add the error text.
SendMessageW(ConWindow, EM_REPLACESEL, FALSE, (LPARAM)widetext.c_str());
// Make sure the error text is not scrolled below the window.
SendMessage(ConWindow, EM_LINESCROLL, 0, SendMessage(ConWindow, EM_GETLINECOUNT, 0, 0));
// The above line scrolled everything off the screen, so pretend to move the scroll
// bar thumb, which clamps to not show any extra lines if it doesn't need to.
SendMessage(ConWindow, EM_SCROLL, SB_PAGEDOWN, 0);
}
BOOL bRet;
MSG msg;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
MessageBoxW(Window, widetext.c_str(), WGAMENAME " Fatal Error", MB_OK | MB_ICONSTOP | MB_TASKMODAL);
return;
}
else if (!IsDialogMessage(ErrorPane, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
void MainWindow::ShowProgressBar(int maxpos)
{
if (ProgressBar == 0)
ProgressBar = CreateWindowEx(0, PROGRESS_CLASS, 0, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, 0, 0, 0, 0, Window, 0, GetModuleHandle(0), 0);
SendMessage(ProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0, maxpos));
LayoutMainWindow(Window, 0);
}
void MainWindow::HideProgressBar()
{
if (ProgressBar != 0)
{
DestroyWindow(ProgressBar);
ProgressBar = 0;
LayoutMainWindow(Window, 0);
}
}
void MainWindow::SetProgressPos(int pos)
{
if (ProgressBar != 0)
SendMessage(ProgressBar, PBM_SETPOS, pos, 0);
}
// DialogProc for the network startup pane. It just waits for somebody to click a button, and the only button available is the abort one.
static INT_PTR CALLBACK NetStartPaneProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL)
{
PostQuitMessage(0);
return TRUE;
}
return FALSE;
}
void MainWindow::ShowNetStartPane(const char* message, int maxpos)
{
// Create the dialog child window
if (NetStartPane == NULL)
{
NetStartPane = CreateDialogParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_NETSTARTPANE), Window, NetStartPaneProc, 0);
if (ProgressBar != 0)
{
DestroyWindow(ProgressBar);
ProgressBar = 0;
}
RECT winrect;
GetWindowRect(Window, &winrect);
SetWindowPos(Window, NULL, 0, 0,
winrect.right - winrect.left, winrect.bottom - winrect.top + LayoutNetStartPane(NetStartPane, 0),
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
LayoutMainWindow(Window, NULL);
SetFocus(NetStartPane);
}
// Set the message text
std::wstring wmessage = WideString(message);
SetDlgItemTextW(NetStartPane, IDC_NETSTARTMESSAGE, wmessage.c_str());
// Set the progress bar range
NetStartMaxPos = maxpos;
HWND ctl = GetDlgItem(NetStartPane, IDC_NETSTARTPROGRESS);
if (maxpos == 0)
{
SendMessage(ctl, PBM_SETMARQUEE, TRUE, 100);
SetWindowLong(ctl, GWL_STYLE, GetWindowLong(ctl, GWL_STYLE) | PBS_MARQUEE);
SetDlgItemTextW(NetStartPane, IDC_NETSTARTCOUNT, L"");
}
else
{
SendMessage(ctl, PBM_SETMARQUEE, FALSE, 0);
SetWindowLong(ctl, GWL_STYLE, GetWindowLong(ctl, GWL_STYLE) & (~PBS_MARQUEE));
SendMessage(ctl, PBM_SETRANGE, 0, MAKELPARAM(0, maxpos));
if (maxpos == 1)
{
SendMessage(ctl, PBM_SETPOS, 1, 0);
SetDlgItemTextW(NetStartPane, IDC_NETSTARTCOUNT, L"");
}
}
}
void MainWindow::HideNetStartPane()
{
if (NetStartPane != 0)
{
DestroyWindow(NetStartPane);
NetStartPane = 0;
LayoutMainWindow(Window, 0);
}
}
void MainWindow::SetNetStartProgress(int pos)
{
if (NetStartPane != 0 && NetStartMaxPos > 1)
{
char buf[16];
mysnprintf(buf, countof(buf), "%d/%d", pos, NetStartMaxPos);
SetDlgItemTextA(NetStartPane, IDC_NETSTARTCOUNT, buf);
SendDlgItemMessage(NetStartPane, IDC_NETSTARTPROGRESS, PBM_SETPOS, min(pos, NetStartMaxPos), 0);
}
}
bool MainWindow::RunMessageLoop(bool (*timer_callback)(void*), void* userdata)
{
BOOL bRet;
MSG msg;
if (SetTimer(Window, 1337, 500, NULL) == 0)
{
I_FatalError("Could not set network synchronization timer.");
}
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
KillTimer(Window, 1337);
return false;
}
else
{
if (msg.message == WM_TIMER && msg.hwnd == Window && msg.wParam == 1337)
{
if (timer_callback(userdata))
{
KillTimer(Window, 1337);
return true;
}
}
if (!IsDialogMessage(NetStartPane, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
KillTimer(Window, 1337);
return false;
}
void MainWindow::UpdateLayout()
{
LayoutMainWindow(Window, 0);
}
// Lays out the main window with the game title and log controls and possibly the error pane and progress bar.
void MainWindow::LayoutMainWindow(HWND hWnd, HWND pane)
{
RECT rect;
int errorpaneheight = 0;
int bannerheight = 0;
int progressheight = 0;
int netpaneheight = 0;
int leftside = 0;
int w, h;
GetClientRect(hWnd, &rect);
w = rect.right;
h = rect.bottom;
if (GameStartupInfo.Name.IsNotEmpty() && GameTitleWindow != NULL)
{
bannerheight = GameTitleFontHeight + 5;
MoveWindow(GameTitleWindow, 0, 0, w, bannerheight, TRUE);
InvalidateRect(GameTitleWindow, NULL, FALSE);
}
if (ProgressBar != NULL)
{
// Base the height of the progress bar on the height of a scroll bar
// arrow, just as in the progress bar example.
progressheight = GetSystemMetrics(SM_CYVSCROLL);
MoveWindow(ProgressBar, 0, h - progressheight, w, progressheight, TRUE);
}
if (NetStartPane != NULL)
{
netpaneheight = LayoutNetStartPane(NetStartPane, w);
SetWindowPos(NetStartPane, HWND_TOP, 0, h - progressheight - netpaneheight, w, netpaneheight, SWP_SHOWWINDOW);
}
if (pane != NULL)
{
errorpaneheight = LayoutErrorPane(pane, w);
SetWindowPos(pane, HWND_TOP, 0, h - progressheight - netpaneheight - errorpaneheight, w, errorpaneheight, 0);
}
if (ErrorIcon != NULL)
{
leftside = GetSystemMetrics(SM_CXICON) + 6;
MoveWindow(ErrorIcon, 0, bannerheight, leftside, h - bannerheight - errorpaneheight - progressheight - netpaneheight, TRUE);
}
// The log window uses whatever space is left.
MoveWindow(ConWindow, leftside, bannerheight, w - leftside, h - bannerheight - errorpaneheight - progressheight - netpaneheight, TRUE);
}
// Lays out the error pane to the desired width, returning the required height.
int MainWindow::LayoutErrorPane(HWND pane, int w)
{
HWND ctl, ctl_two;
RECT rectc, rectc_two;
// Right-align the Quit button.
ctl = GetDlgItem(pane, IDOK);
GetClientRect(ctl, &rectc); // Find out how big it is.
MoveWindow(ctl, w - rectc.right - 1, 1, rectc.right, rectc.bottom, TRUE);
// Second-right-align the Restart button
ctl_two = GetDlgItem(pane, IDC_BUTTON1);
GetClientRect(ctl_two, &rectc_two); // Find out how big it is.
MoveWindow(ctl_two, w - rectc.right - rectc_two.right - 2, 1, rectc.right, rectc.bottom, TRUE);
InvalidateRect(ctl, NULL, TRUE);
InvalidateRect(ctl_two, NULL, TRUE);
// Return the needed height for this layout
return rectc.bottom + 2;
}
// Lays out the network startup pane to the specified width, returning its required height.
int MainWindow::LayoutNetStartPane(HWND pane, int w)
{
HWND ctl;
RECT margin, rectc;
int staticheight, barheight;
// Determine margin sizes.
SetRect(&margin, 7, 7, 0, 0);
MapDialogRect(pane, &margin);
// Stick the message text in the upper left corner.
ctl = GetDlgItem(pane, IDC_NETSTARTMESSAGE);
GetClientRect(ctl, &rectc);
MoveWindow(ctl, margin.left, margin.top, rectc.right, rectc.bottom, TRUE);
// Stick the count text in the upper right corner.
ctl = GetDlgItem(pane, IDC_NETSTARTCOUNT);
GetClientRect(ctl, &rectc);
MoveWindow(ctl, w - rectc.right - margin.left, margin.top, rectc.right, rectc.bottom, TRUE);
staticheight = rectc.bottom;
// Stretch the progress bar to fill the entire width.
ctl = GetDlgItem(pane, IDC_NETSTARTPROGRESS);
barheight = GetSystemMetrics(SM_CYVSCROLL);
MoveWindow(ctl, margin.left, margin.top * 2 + staticheight, w - margin.left * 2, barheight, TRUE);
// Center the abort button underneath the progress bar.
ctl = GetDlgItem(pane, IDCANCEL);
GetClientRect(ctl, &rectc);
MoveWindow(ctl, (w - rectc.right) / 2, margin.top * 3 + staticheight + barheight, rectc.right, rectc.bottom, TRUE);
return margin.top * 4 + staticheight + barheight + rectc.bottom;
}
void MainWindow::CheckForRestart()
{
if (restartrequest)
{
HMODULE hModule = GetModuleHandleW(NULL);
WCHAR path[MAX_PATH];
GetModuleFileNameW(hModule, path, MAX_PATH);
ShellExecuteW(NULL, L"open", path, GetCommandLineW(), NULL, SW_SHOWNORMAL);
}
restartrequest = false;
}
// The main window's WndProc during startup. During gameplay, the WndProc in i_input.cpp is used instead.
LRESULT MainWindow::LConProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
return mainwindow.OnMessage(hWnd, msg, wParam, lParam);
}
INT_PTR MainWindow::ErrorPaneProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
// Appear in the main window.
mainwindow.LayoutMainWindow(GetParent(hDlg), hDlg);
return TRUE;
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED)
{
if (LOWORD(wParam) == IDC_BUTTON1) // we pressed the restart button, so run GZDoom again
{
mainwindow.restartrequest = true;
}
PostQuitMessage(0);
return TRUE;
}
break;
}
return FALSE;
}
LRESULT MainWindow::OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE: return OnCreate(hWnd, msg, wParam, lParam);
case WM_SIZE: return OnSize(hWnd, msg, wParam, lParam);
case WM_DRAWITEM: return OnDrawItem(hWnd, msg, wParam, lParam);
case WM_COMMAND: return OnCommand(hWnd, msg, wParam, lParam);
case WM_CLOSE: return OnClose(hWnd, msg, wParam, lParam);
case WM_DESTROY: return OnDestroy(hWnd, msg, wParam, lParam);
default: return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
LRESULT MainWindow::OnCreate(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND view;
HDC hdc;
HGDIOBJ oldfont;
LOGFONT lf;
TEXTMETRIC tm;
CHARFORMAT2W format;
HINSTANCE inst = (HINSTANCE)(LONG_PTR)GetWindowLongPtr(hWnd, GWLP_HINSTANCE);
// Create game title static control
memset(&lf, 0, sizeof(lf));
hdc = GetDC(hWnd);
lf.lfHeight = -MulDiv(12, GetDeviceCaps(hdc, LOGPIXELSY), 72);
lf.lfCharSet = ANSI_CHARSET;
lf.lfWeight = FW_BOLD;
lf.lfPitchAndFamily = VARIABLE_PITCH | FF_ROMAN;
wcscpy(lf.lfFaceName, L"Trebuchet MS");
GameTitleFont = CreateFontIndirect(&lf);
oldfont = SelectObject(hdc, GetStockObject(DEFAULT_GUI_FONT));
GetTextMetrics(hdc, &tm);
DefaultGUIFontHeight = tm.tmHeight;
if (GameTitleFont == NULL)
{
GameTitleFontHeight = DefaultGUIFontHeight;
}
else
{
SelectObject(hdc, GameTitleFont);
GetTextMetrics(hdc, &tm);
GameTitleFontHeight = tm.tmHeight;
}
SelectObject(hdc, oldfont);
// Create log read-only edit control
view = CreateWindowExW(WS_EX_NOPARENTNOTIFY, L"RichEdit20W", nullptr,
WS_CHILD | WS_VISIBLE | WS_VSCROLL |
ES_LEFT | ES_MULTILINE | WS_CLIPSIBLINGS,
0, 0, 0, 0,
hWnd, NULL, inst, NULL);
HRESULT hr;
hr = GetLastError();
if (view == NULL)
{
ReleaseDC(hWnd, hdc);
return -1;
}
SendMessage(view, EM_SETREADONLY, TRUE, 0);
SendMessage(view, EM_EXLIMITTEXT, 0, 0x7FFFFFFE);
SendMessage(view, EM_SETBKGNDCOLOR, 0, RGB(70, 70, 70));
// Setup default font for the log.
//SendMessage (view, WM_SETFONT, (WPARAM)GetStockObject (DEFAULT_GUI_FONT), FALSE);
format.cbSize = sizeof(format);
format.dwMask = CFM_BOLD | CFM_COLOR | CFM_FACE | CFM_SIZE | CFM_CHARSET;
format.dwEffects = 0;
format.yHeight = 200;
format.crTextColor = RGB(223, 223, 223);
format.bCharSet = ANSI_CHARSET;
format.bPitchAndFamily = FF_SWISS | VARIABLE_PITCH;
wcscpy(format.szFaceName, L"DejaVu Sans"); // At least I have it. :p
SendMessageW(view, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&format);
ConWindow = view;
ReleaseDC(hWnd, hdc);
view = CreateWindowExW(WS_EX_NOPARENTNOTIFY, L"STATIC", NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_OWNERDRAW, 0, 0, 0, 0, hWnd, nullptr, inst, nullptr);
if (view == nullptr)
{
return -1;
}
SetWindowLong(view, GWL_ID, IDC_STATIC_TITLE);
GameTitleWindow = view;
return 0;
}
LRESULT MainWindow::OnSize(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (wParam != SIZE_MAXHIDE && wParam != SIZE_MAXSHOW)
{
LayoutMainWindow(hWnd, ErrorPane);
}
return 0;
}
LRESULT MainWindow::OnDrawItem(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HGDIOBJ oldfont;
HBRUSH hbr;
DRAWITEMSTRUCT* drawitem;
RECT rect;
SIZE size;
// Draw title banner.
if (wParam == IDC_STATIC_TITLE && GameStartupInfo.Name.IsNotEmpty())
{
const PalEntry* c;
// Draw the game title strip at the top of the window.
drawitem = (LPDRAWITEMSTRUCT)lParam;
// Draw the background.
rect = drawitem->rcItem;
rect.bottom -= 1;
c = (const PalEntry*)&GameStartupInfo.BkColor;
hbr = CreateSolidBrush(RGB(c->r, c->g, c->b));
FillRect(drawitem->hDC, &drawitem->rcItem, hbr);
DeleteObject(hbr);
// Calculate width of the title string.
SetTextAlign(drawitem->hDC, TA_TOP);
oldfont = SelectObject(drawitem->hDC, GameTitleFont != NULL ? GameTitleFont : (HFONT)GetStockObject(DEFAULT_GUI_FONT));
auto widename = GameStartupInfo.Name.WideString();
GetTextExtentPoint32W(drawitem->hDC, widename.c_str(), (int)widename.length(), &size);
// Draw the title.
c = (const PalEntry*)&GameStartupInfo.FgColor;
SetTextColor(drawitem->hDC, RGB(c->r, c->g, c->b));
SetBkMode(drawitem->hDC, TRANSPARENT);
TextOutW(drawitem->hDC, rect.left + (rect.right - rect.left - size.cx) / 2, 2, widename.c_str(), (int)widename.length());
SelectObject(drawitem->hDC, oldfont);
return TRUE;
}
// Draw stop icon.
else if (wParam == IDC_ICONPIC)
{
HICON icon;
POINTL char_pos;
drawitem = (LPDRAWITEMSTRUCT)lParam;
// This background color should match the edit control's.
hbr = CreateSolidBrush(RGB(70, 70, 70));
FillRect(drawitem->hDC, &drawitem->rcItem, hbr);
DeleteObject(hbr);
// Draw the icon aligned with the first line of error text.
SendMessage(ConWindow, EM_POSFROMCHAR, (WPARAM)&char_pos, ErrorIconChar);
icon = (HICON)LoadImage(0, IDI_ERROR, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED);
DrawIcon(drawitem->hDC, 6, char_pos.y, icon);
return TRUE;
}
return FALSE;
}
LRESULT MainWindow::OnCommand(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (ErrorIcon != NULL && (HWND)lParam == ConWindow && HIWORD(wParam) == EN_UPDATE)
{
// Be sure to redraw the error icon if the edit control changes.
InvalidateRect(ErrorIcon, NULL, TRUE);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
LRESULT MainWindow::OnClose(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
PostQuitMessage(0);
return DefWindowProc(hWnd, msg, wParam, lParam);
}
LRESULT MainWindow::OnDestroy(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (GameTitleFont != NULL)
{
DeleteObject(GameTitleFont);
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
void MainWindow::PrintStr(const char* cp)
{
if (ConWindowHidden)
{
bufferedConsoleStuff.Push(cp);
}
else
{
DoPrintStr(cp);
}
}
void MainWindow::FlushBufferedConsoleStuff()
{
for (unsigned i = 0; i < bufferedConsoleStuff.Size(); i++)
{
DoPrintStr(bufferedConsoleStuff[i]);
}
bufferedConsoleStuff.Clear();
}
void MainWindow::DoPrintStr(const char* cpt)
{
wchar_t wbuf[256];
int bpos = 0;
CHARRANGE selection = {};
CHARRANGE endselection = {};
LONG lines_before = 0, lines_after = 0;
// Store the current selection and set it to the end so we can append text.
SendMessage(ConWindow, EM_EXGETSEL, 0, (LPARAM)&selection);
endselection.cpMax = endselection.cpMin = GetWindowTextLength(ConWindow);
SendMessage(ConWindow, EM_EXSETSEL, 0, (LPARAM)&endselection);
// GetWindowTextLength and EM_EXSETSEL can disagree on where the end of
// the text is. Find out what EM_EXSETSEL thought it was and use that later.
SendMessage(ConWindow, EM_EXGETSEL, 0, (LPARAM)&endselection);
// Remember how many lines there were before we added text.
lines_before = (LONG)SendMessage(ConWindow, EM_GETLINECOUNT, 0, 0);
const uint8_t* cptr = (const uint8_t*)cpt;
auto outputIt = [&]()
{
wbuf[bpos] = 0;
SendMessageW(ConWindow, EM_REPLACESEL, FALSE, (LPARAM)wbuf);
bpos = 0;
};
while (int chr = GetCharFromString(cptr))
{
if ((chr == TEXTCOLOR_ESCAPE && bpos != 0) || bpos == 255)
{
outputIt();
}
if (chr != TEXTCOLOR_ESCAPE)
{
if (chr >= 0x1D && chr <= 0x1F)
{ // The bar characters, most commonly used to indicate map changes
chr = 0x2550; // Box Drawings Double Horizontal
}
wbuf[bpos++] = chr;
}
else
{
EColorRange range = V_ParseFontColor(cptr, CR_UNTRANSLATED, CR_YELLOW);
if (range != CR_UNDEFINED)
{
// Change the color of future text added to the control.
PalEntry color = V_LogColorFromColorRange(range);
// Change the color.
CHARFORMAT format;
format.cbSize = sizeof(format);
format.dwMask = CFM_COLOR;
format.dwEffects = 0;
format.crTextColor = RGB(color.r, color.g, color.b);
SendMessage(ConWindow, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);
}
}
}
if (bpos != 0)
{
outputIt();
}
// If the old selection was at the end of the text, keep it at the end and
// scroll. Don't scroll if the selection is anywhere else.
if (selection.cpMin == endselection.cpMin && selection.cpMax == endselection.cpMax)
{
selection.cpMax = selection.cpMin = GetWindowTextLength(ConWindow);
lines_after = (LONG)SendMessage(ConWindow, EM_GETLINECOUNT, 0, 0);
if (lines_after > lines_before)
{
SendMessage(ConWindow, EM_LINESCROLL, 0, lines_after - lines_before);
}
}
// Restore the previous selection.
SendMessage(ConWindow, EM_EXSETSEL, 0, (LPARAM)&selection);
// Give the edit control a chance to redraw itself.
I_GetEvent();
}
static DWORD CALLBACK WriteLogFileStreamer(DWORD_PTR cookie, LPBYTE buffer, LONG cb, LONG* pcb)
{
uint32_t didwrite;
LONG p, pp;
// Replace gray foreground color with black.
static const char* badfg = "\\red223\\green223\\blue223;";
// 4321098 765432109 876543210
// 2 1 0
for (p = pp = 0; p < cb; ++p)
{
if (buffer[p] == badfg[pp])
{
++pp;
if (pp == 25)
{
buffer[p - 1] = buffer[p - 2] = buffer[p - 3] =
buffer[p - 9] = buffer[p - 10] = buffer[p - 11] =
buffer[p - 18] = buffer[p - 19] = buffer[p - 20] = '0';
break;
}
}
else
{
pp = 0;
}
}
auto& writeData = *reinterpret_cast<std::function<bool(const void* data, uint32_t size, uint32_t& written)>*>(cookie);
if (!writeData((const void*)buffer, cb, didwrite))
{
return 1;
}
*pcb = didwrite;
return 0;
}
void MainWindow::GetLog(std::function<bool(const void* data, uint32_t size, uint32_t& written)> writeData)
{
FlushBufferedConsoleStuff();
EDITSTREAM streamer = { (DWORD_PTR)&writeData, 0, WriteLogFileStreamer };
SendMessage(ConWindow, EM_STREAMOUT, SF_RTF, (LPARAM)&streamer);
}
// each platform has its own specific version of this function.
void MainWindow::SetWindowTitle(const char* caption)
{
std::wstring widecaption;
if (!caption)
{
FStringf default_caption("" GAMENAME " %s " X64 " (%s)", GetVersionString(), GetGitTime());
widecaption = default_caption.WideString();
}
else
{
widecaption = WideString(caption);
}
SetWindowText(Window, widecaption.c_str());
}

View file

@ -0,0 +1,90 @@
#pragma once
#include "zstring.h"
#include "printf.h"
#include <functional>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
// The WndProc used when the game view is active
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
class MainWindow
{
public:
void Create(const FString& title, int x, int y, int width, int height);
void ShowGameView();
void RestoreConView();
void ShowErrorPane(const char* text);
void CheckForRestart();
void HideGameTitleWindow();
int GetGameTitleWindowHeight();
void PrintStr(const char* cp);
void GetLog(std::function<bool(const void* data, uint32_t size, uint32_t& written)> writeFile);
void UpdateLayout();
void ShowProgressBar(int maxpos);
void HideProgressBar();
void SetProgressPos(int pos);
void ShowNetStartPane(const char* message, int maxpos);
void SetNetStartProgress(int pos);
bool RunMessageLoop(bool (*timer_callback)(void*), void* userdata);
void HideNetStartPane();
void SetWindowTitle(const char* caption);
HWND GetHandle() { return Window; }
private:
void LayoutMainWindow(HWND hWnd, HWND pane);
int LayoutErrorPane(HWND pane, int w);
int LayoutNetStartPane(HWND pane, int w);
void DoPrintStr(const char* cpt);
void FlushBufferedConsoleStuff();
LRESULT OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT OnCreate(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT OnSize(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT OnDrawItem(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT OnCommand(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT OnClose(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT OnDestroy(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK LConProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK ErrorPaneProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
HWND Window = 0;
HFONT GameTitleFont = 0;
LONG GameTitleFontHeight = 0;
LONG DefaultGUIFontHeight = 0;
LONG ErrorIconChar = 0;
bool restartrequest = false;
HWND GameTitleWindow = 0;
HWND ErrorPane = 0;
HWND ErrorIcon = 0;
bool ConWindowHidden = false;
HWND ConWindow = 0;
TArray<FString> bufferedConsoleStuff;
HWND ProgressBar = 0;
HWND NetStartPane = 0;
int NetStartMaxPos = 0;
HWND StartupScreen = 0;
};
extern MainWindow mainwindow;

View file

@ -46,6 +46,7 @@
#include "menustate.h"
#include "keydef.h"
#include "i_interface.h"
#include "i_mainwindow.h"
// MACROS ------------------------------------------------------------------
@ -138,7 +139,6 @@ static void CenterMouse(int x, int y, LONG *centx, LONG *centy);
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern HWND Window;
extern LPDIRECTINPUT8 g_pdi;
extern LPDIRECTINPUT g_pdi3;
extern bool GUICapture;
@ -193,11 +193,11 @@ CUSTOM_CVAR (Int, in_mouse, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_NOINITCALL)
static void SetCursorState(bool visible)
{
CursorState = visible || !m_hidepointer;
if (GetForegroundWindow() == Window)
if (GetForegroundWindow() == mainwindow.GetHandle())
{
if (CursorState)
{
SetCursor((HCURSOR)(intptr_t)GetClassLongPtr(Window, GCLP_HCURSOR));
SetCursor((HCURSOR)(intptr_t)GetClassLongPtr(mainwindow.GetHandle(), GCLP_HCURSOR));
}
else
{
@ -219,7 +219,7 @@ static void CenterMouse(int curx, int cury, LONG *centxp, LONG *centyp)
{
RECT rect;
GetWindowRect(Window, &rect);
GetWindowRect(mainwindow.GetHandle(), &rect);
int centx = (rect.left + rect.right) >> 1;
int centy = (rect.top + rect.bottom) >> 1;
@ -258,7 +258,7 @@ void I_CheckNativeMouse(bool preferNative, bool eventhandlerresult)
}
else
{
if ((GetForegroundWindow() != Window) || preferNative || !use_mouse)
if ((GetForegroundWindow() != mainwindow.GetHandle()) || preferNative || !use_mouse)
{
want_native = true;
}
@ -305,7 +305,6 @@ void I_CheckNativeMouse(bool preferNative, bool eventhandlerresult)
FMouse::FMouse()
{
LastX = LastY = 0;
ButtonState = 0;
WheelMove[0] = 0;
WheelMove[1] = 0;
@ -490,7 +489,7 @@ bool FRawMouse::GetDevice()
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_MOUSE;
rid.dwFlags = 0;
rid.hwndTarget = Window;
rid.hwndTarget = mainwindow.GetHandle();
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
return false;
@ -516,7 +515,7 @@ void FRawMouse::Grab()
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_MOUSE;
rid.dwFlags = RIDEV_CAPTUREMOUSE | RIDEV_NOLEGACY;
rid.hwndTarget = Window;
rid.hwndTarget = mainwindow.GetHandle();
if (RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
GetCursorPos(&UngrabbedPointerPos);
@ -718,7 +717,7 @@ ufailit:
return false;
}
hr = Device->SetCooperativeLevel(Window, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
hr = Device->SetCooperativeLevel(mainwindow.GetHandle(), DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (FAILED(hr))
{
goto ufailit;
@ -1066,11 +1065,11 @@ void FWin32Mouse::Grab()
GetCursorPos(&UngrabbedPointerPos);
ClipCursor(NULL); // helps with Win95?
GetClientRect(Window, &rect);
GetClientRect(mainwindow.GetHandle(), &rect);
// Reposition the rect so that it only covers the client area.
ClientToScreen(Window, (LPPOINT)&rect.left);
ClientToScreen(Window, (LPPOINT)&rect.right);
ClientToScreen(mainwindow.GetHandle(), (LPPOINT)&rect.left);
ClientToScreen(mainwindow.GetHandle(), (LPPOINT)&rect.right);
ClipCursor(&rect);
SetCursorState(false);

View file

@ -44,6 +44,8 @@
#include "cmdlib.h"
#include "keydef.h"
#include "i_mainwindow.h"
// MACROS ------------------------------------------------------------------
#define DEFAULT_DEADZONE 0.25f
@ -214,8 +216,6 @@ struct PS2Descriptor
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern HWND Window;
// PUBLIC DATA DEFINITIONS -------------------------------------------------
CUSTOM_CVAR(Bool, joy_ps2raw, true, CVAR_GLOBALCONFIG|CVAR_ARCHIVE|CVAR_NOINITCALL)
@ -905,7 +905,7 @@ bool FRawPS2Manager::GetDevice()
rid.usUsagePage = HID_GENERIC_DESKTOP_PAGE;
rid.usUsage = HID_GDP_JOYSTICK;
rid.dwFlags = RIDEV_INPUTSINK;
rid.hwndTarget = Window;
rid.hwndTarget = mainwindow.GetHandle();
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
return false;
@ -1273,7 +1273,7 @@ void FRawPS2Manager::DoRegister()
if (!Registered)
{
rid.dwFlags = RIDEV_INPUTSINK;
rid.hwndTarget = Window;
rid.hwndTarget = mainwindow.GetHandle();
if (RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
Registered = true;

View file

@ -81,6 +81,7 @@
#include "bitmap.h"
#include "cmdlib.h"
#include "i_interface.h"
#include "i_mainwindow.h"
// MACROS ------------------------------------------------------------------
@ -92,10 +93,6 @@
// TYPES -------------------------------------------------------------------
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
extern void LayoutMainWindow(HWND hWnd, HWND pane);
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
void DestroyCustomCursor();
@ -116,13 +113,11 @@ EXTERN_CVAR (Bool, autoloadbrightmaps)
EXTERN_CVAR (Bool, autoloadwidescreen)
EXTERN_CVAR (Int, vid_preferbackend)
extern HWND Window, ConWindow, GameTitleWindow;
extern HANDLE StdOut;
extern bool FancyStdOut;
extern HINSTANCE g_hInst;
extern FILE *Logfile;
extern bool NativeMouse;
extern bool ConWindowHidden;
// PUBLIC DATA DEFINITIONS -------------------------------------------------
@ -281,42 +276,19 @@ void CalculateCPUSpeed()
//
//==========================================================================
static void DoPrintStr(const char *cpt, HWND edit, HANDLE StdOut)
static void PrintToStdOut(const char *cpt, HANDLE StdOut)
{
if (edit == nullptr && StdOut == nullptr && !con_debugoutput)
if (StdOut == nullptr && !con_debugoutput)
return;
wchar_t wbuf[256];
int bpos = 0;
CHARRANGE selection = {};
CHARRANGE endselection = {};
LONG lines_before = 0, lines_after;
CHARFORMAT format;
if (edit != NULL)
{
// Store the current selection and set it to the end so we can append text.
SendMessage(edit, EM_EXGETSEL, 0, (LPARAM)&selection);
endselection.cpMax = endselection.cpMin = GetWindowTextLength(edit);
SendMessage(edit, EM_EXSETSEL, 0, (LPARAM)&endselection);
// GetWindowTextLength and EM_EXSETSEL can disagree on where the end of
// the text is. Find out what EM_EXSETSEL thought it was and use that later.
SendMessage(edit, EM_EXGETSEL, 0, (LPARAM)&endselection);
// Remember how many lines there were before we added text.
lines_before = (LONG)SendMessage(edit, EM_GETLINECOUNT, 0, 0);
}
const uint8_t *cptr = (const uint8_t*)cpt;
auto outputIt = [&]()
{
wbuf[bpos] = 0;
if (edit != nullptr)
{
SendMessageW(edit, EM_REPLACESEL, FALSE, (LPARAM)wbuf);
}
if (con_debugoutput)
{
OutputDebugStringW(wbuf);
@ -385,17 +357,6 @@ static void DoPrintStr(const char *cpt, HWND edit, HANDLE StdOut)
}
SetConsoleTextAttribute(StdOut, (WORD)attrib);
}
if (edit != NULL)
{
// GDI uses BGR colors, but color is RGB, so swap the R and the B.
std::swap(color.r, color.b);
// Change the color.
format.cbSize = sizeof(format);
format.dwMask = CFM_COLOR;
format.dwEffects = 0;
format.crTextColor = color;
SendMessage(edit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&format);
}
}
}
}
@ -404,52 +365,16 @@ static void DoPrintStr(const char *cpt, HWND edit, HANDLE StdOut)
outputIt();
}
if (edit != NULL)
{
// If the old selection was at the end of the text, keep it at the end and
// scroll. Don't scroll if the selection is anywhere else.
if (selection.cpMin == endselection.cpMin && selection.cpMax == endselection.cpMax)
{
selection.cpMax = selection.cpMin = GetWindowTextLength (edit);
lines_after = (LONG)SendMessage(edit, EM_GETLINECOUNT, 0, 0);
if (lines_after > lines_before)
{
SendMessage(edit, EM_LINESCROLL, 0, lines_after - lines_before);
}
}
// Restore the previous selection.
SendMessage(edit, EM_EXSETSEL, 0, (LPARAM)&selection);
// Give the edit control a chance to redraw itself.
I_GetEvent();
}
if (StdOut != NULL && FancyStdOut)
{ // Set text back to gray, in case it was changed.
SetConsoleTextAttribute(StdOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}
}
static TArray<FString> bufferedConsoleStuff;
void I_PrintStr(const char *cp)
{
if (ConWindowHidden)
{
bufferedConsoleStuff.Push(cp);
DoPrintStr(cp, NULL, StdOut);
}
else
{
DoPrintStr(cp, ConWindow, StdOut);
}
}
void I_FlushBufferedConsoleStuff()
{
for (unsigned i = 0; i < bufferedConsoleStuff.Size(); i++)
{
DoPrintStr(bufferedConsoleStuff[i], ConWindow, NULL);
}
bufferedConsoleStuff.Clear();
mainwindow.PrintStr(cp);
PrintToStdOut(cp, StdOut);
}
//==========================================================================
@ -635,7 +560,7 @@ int I_PickIWad(WadStuff *wads, int numwads, bool showwin, int defaultiwad)
DefaultWad = defaultiwad;
return (int)DialogBox(g_hInst, MAKEINTRESOURCE(IDD_IWADDIALOG),
(HWND)Window, (DLGPROC)IWADBoxCallback);
(HWND)mainwindow.GetHandle(), (DLGPROC)IWADBoxCallback);
}
return defaultiwad;
}
@ -682,16 +607,16 @@ bool I_SetCursor(FGameTexture *cursorpic)
DestroyCustomCursor();
cursor = LoadCursor(NULL, IDC_ARROW);
}
SetClassLongPtr(Window, GCLP_HCURSOR, (LONG_PTR)cursor);
SetClassLongPtr(mainwindow.GetHandle(), GCLP_HCURSOR, (LONG_PTR)cursor);
if (NativeMouse)
{
POINT pt;
RECT client;
// If the mouse pointer is within the window's client rect, set it now.
if (GetCursorPos(&pt) && GetClientRect(Window, &client) &&
ClientToScreen(Window, (LPPOINT)&client.left) &&
ClientToScreen(Window, (LPPOINT)&client.right))
if (GetCursorPos(&pt) && GetClientRect(mainwindow.GetHandle(), &client) &&
ClientToScreen(mainwindow.GetHandle(), (LPPOINT)&client.left) &&
ClientToScreen(mainwindow.GetHandle(), (LPPOINT)&client.right))
{
if (pt.x >= client.left && pt.x < client.right &&
pt.y >= client.top && pt.y < client.bottom)
@ -912,7 +837,7 @@ bool I_WriteIniFailed()
);
errortext.Format ("The config file %s could not be written:\n%s", GameConfig->GetPathName(), lpMsgBuf);
LocalFree (lpMsgBuf);
return MessageBoxA(Window, errortext.GetChars(), GAMENAME " configuration not saved", MB_ICONEXCLAMATION | MB_RETRYCANCEL) == IDRETRY;
return MessageBoxA(mainwindow.GetHandle(), errortext.GetChars(), GAMENAME " configuration not saved", MB_ICONEXCLAMATION | MB_RETRYCANCEL) == IDRETRY;
}

View file

@ -43,9 +43,6 @@ int I_PickIWad (WadStuff *wads, int numwads, bool queryiwad, int defaultiwad);
// The ini could not be saved at exit
bool I_WriteIniFailed ();
// [RH] Used by the display code to set the normal window procedure
void I_SetWndProc();
// [RH] Checks the registry for Steam's install path, so we can scan its
// directories for IWADs if the user purchased any through Steam.
TArray<FString> I_GetSteamPath();

View file

@ -53,49 +53,15 @@
#include "startupinfo.h"
#include "i_interface.h"
#include "texturemanager.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
#define TEXT_FONT_NAME "vga-rom-font.16"
// TYPES -------------------------------------------------------------------
#include "i_mainwindow.h"
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
void RestoreConView();
void LayoutMainWindow (HWND hWnd, HWND pane);
int LayoutNetStartPane (HWND pane, int w);
bool ST_Util_CreateStartupWindow ();
void ST_Util_SizeWindowForBitmap (int scale);
void ST_Util_InvalidateRect (HWND hwnd, BitmapInfo *bitmap_info, int left, int top, int right, int bottom);
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static INT_PTR CALLBACK NetStartPaneProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
extern HINSTANCE g_hInst;
extern HWND Window, ConWindow, ProgressBar, NetStartPane, StartupScreen, GameTitleWindow;
// PUBLIC DATA DEFINITIONS -------------------------------------------------
FStartupScreen *StartScreen;
CUSTOM_CVAR(Int, showendoom, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
if (self < 0) self = 0;
else if (self > 2) self=2;
}
// PRIVATE DATA DEFINITIONS ------------------------------------------------
@ -110,36 +76,9 @@ CUSTOM_CVAR(Int, showendoom, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
//
//==========================================================================
FStartupScreen *FStartupScreen::CreateInstance(int max_progress)
FStartupScreen *FStartupScreen::CreateInstance(int max_progress, bool showprogress)
{
FStartupScreen *scr = NULL;
HRESULT hr = -1;
if (!Args->CheckParm("-nostartup"))
{
if (GameStartupInfo.Type == FStartupInfo::HexenStartup)
{
scr = new FHexenStartupScreen(max_progress, hr);
}
else if (GameStartupInfo.Type == FStartupInfo::HereticStartup)
{
scr = new FHereticStartupScreen(max_progress, hr);
}
else if (GameStartupInfo.Type == FStartupInfo::StrifeStartup)
{
scr = new FStrifeStartupScreen(max_progress, hr);
}
if (scr != NULL && FAILED(hr))
{
delete scr;
scr = NULL;
}
}
if (scr == NULL)
{
scr = new FBasicStartupScreen(max_progress, true);
}
return scr;
return new FBasicStartupScreen(max_progress, showprogress);
}
//==========================================================================
@ -155,12 +94,7 @@ FBasicStartupScreen::FBasicStartupScreen(int max_progress, bool show_bar)
{
if (show_bar)
{
ProgressBar = CreateWindowEx(0, PROGRESS_CLASS,
NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
0, 0, 0, 0,
Window, 0, g_hInst, NULL);
SendMessage (ProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0,MaxPos));
LayoutMainWindow (Window, NULL);
mainwindow.ShowProgressBar(MaxPos);
}
NetMaxPos = 0;
NetCurPos = 0;
@ -177,13 +111,8 @@ FBasicStartupScreen::FBasicStartupScreen(int max_progress, bool show_bar)
FBasicStartupScreen::~FBasicStartupScreen()
{
if (ProgressBar != NULL)
{
DestroyWindow (ProgressBar);
ProgressBar = NULL;
LayoutMainWindow (Window, NULL);
}
KillTimer(Window, 1337);
mainwindow.HideProgressBar();
KillTimer(mainwindow.GetHandle(), 1337);
}
//==========================================================================
@ -199,7 +128,7 @@ void FBasicStartupScreen::Progress()
if (CurPos < MaxPos)
{
CurPos++;
SendMessage (ProgressBar, PBM_SETPOS, CurPos, 0);
mainwindow.SetProgressPos(CurPos);
}
}
@ -218,61 +147,8 @@ void FBasicStartupScreen::Progress()
void FBasicStartupScreen::NetInit(const char *message, int numplayers)
{
NetMaxPos = numplayers;
if (NetStartPane == NULL)
{
NetStartPane = CreateDialogParam (g_hInst, MAKEINTRESOURCE(IDD_NETSTARTPANE), Window, NetStartPaneProc, 0);
// We don't need two progress bars.
if (ProgressBar != NULL)
{
DestroyWindow (ProgressBar);
ProgressBar = NULL;
}
RECT winrect;
GetWindowRect (Window, &winrect);
SetWindowPos (Window, NULL, 0, 0,
winrect.right - winrect.left, winrect.bottom - winrect.top + LayoutNetStartPane (NetStartPane, 0),
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER);
LayoutMainWindow (Window, NULL);
SetFocus (NetStartPane);
}
if (NetStartPane != NULL)
{
HWND ctl;
mainwindow.ShowNetStartPane(message, numplayers);
std::wstring wmessage = WideString(message);
SetDlgItemTextW (NetStartPane, IDC_NETSTARTMESSAGE, wmessage.c_str());
ctl = GetDlgItem (NetStartPane, IDC_NETSTARTPROGRESS);
if (numplayers == 0)
{
// PBM_SETMARQUEE is only available under XP and above, so this might fail.
NetMarqueeMode = SendMessage (ctl, PBM_SETMARQUEE, TRUE, 100);
if (NetMarqueeMode == FALSE)
{
SendMessage (ctl, PBM_SETRANGE, 0, MAKELPARAM(0,16));
}
else
{
// If we don't set the PBS_MARQUEE style, then the marquee will never show up.
SetWindowLong (ctl, GWL_STYLE, GetWindowLong (ctl, GWL_STYLE) | PBS_MARQUEE);
}
SetDlgItemTextW (NetStartPane, IDC_NETSTARTCOUNT, L"");
}
else
{
NetMarqueeMode = FALSE;
SendMessage (ctl, PBM_SETMARQUEE, FALSE, 0);
// Make sure the marquee really is turned off.
SetWindowLong (ctl, GWL_STYLE, GetWindowLong (ctl, GWL_STYLE) & (~PBS_MARQUEE));
SendMessage (ctl, PBM_SETRANGE, 0, MAKELPARAM(0,numplayers));
if (numplayers == 1)
{
SendMessage (ctl, PBM_SETPOS, 1, 0);
SetDlgItemTextW (NetStartPane, IDC_NETSTARTCOUNT, L"");
}
}
}
NetMaxPos = numplayers;
NetCurPos = 0;
NetProgress(1); // You always know about yourself
@ -288,33 +164,7 @@ void FBasicStartupScreen::NetInit(const char *message, int numplayers)
void FBasicStartupScreen::NetDone()
{
if (NetStartPane != NULL)
{
DestroyWindow (NetStartPane);
NetStartPane = NULL;
LayoutMainWindow (Window, NULL);
}
}
//==========================================================================
//
// FBasicStartupScreen :: NetMessage
//
// Call this between NetInit() and NetDone() instead of Printf() to
// display messages, in case the progress meter is mixed in the same output
// stream as normal messages.
//
//==========================================================================
void FBasicStartupScreen::NetMessage(const char *format, ...)
{
FString str;
va_list argptr;
va_start (argptr, format);
str.VFormat (format, argptr);
va_end (argptr);
Printf ("%s\n", str.GetChars());
mainwindow.HideNetStartPane();
}
//==========================================================================
@ -326,7 +176,7 @@ void FBasicStartupScreen::NetMessage(const char *format, ...)
//
//==========================================================================
void FBasicStartupScreen :: NetProgress(int count)
void FBasicStartupScreen::NetProgress(int count)
{
if (count == 0)
{
@ -336,23 +186,8 @@ void FBasicStartupScreen :: NetProgress(int count)
{
NetCurPos = count;
}
if (NetStartPane == NULL)
{
return;
}
if (NetMaxPos == 0 && !NetMarqueeMode)
{
// PBM_SETMARQUEE didn't work, so just increment the progress bar endlessly.
SendDlgItemMessage (NetStartPane, IDC_NETSTARTPROGRESS, PBM_SETPOS, NetCurPos & 15, 0);
}
else if (NetMaxPos > 1)
{
char buf[16];
mysnprintf (buf, countof(buf), "%d/%d", NetCurPos, NetMaxPos);
SetDlgItemTextA (NetStartPane, IDC_NETSTARTCOUNT, buf);
SendDlgItemMessage (NetStartPane, IDC_NETSTARTPROGRESS, PBM_SETPOS, min(NetCurPos, NetMaxPos), 0);
}
mainwindow.SetNetStartProgress(count);
}
//==========================================================================
@ -371,350 +206,5 @@ void FBasicStartupScreen :: NetProgress(int count)
bool FBasicStartupScreen::NetLoop(bool (*timer_callback)(void *), void *userdata)
{
BOOL bRet;
MSG msg;
if (SetTimer (Window, 1337, 500, NULL) == 0)
{
I_FatalError ("Could not set network synchronization timer.");
}
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
KillTimer (Window, 1337);
return false;
}
else
{
if (msg.message == WM_TIMER && msg.hwnd == Window && msg.wParam == 1337)
{
if (timer_callback (userdata))
{
KillTimer (NetStartPane, 1);
return true;
}
}
if (!IsDialogMessage (NetStartPane, &msg))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
}
KillTimer (Window, 1337);
return false;
}
//==========================================================================
//
// NetStartPaneProc
//
// DialogProc for the network startup pane. It just waits for somebody to
// click a button, and the only button available is the abort one.
//
//==========================================================================
static INT_PTR CALLBACK NetStartPaneProc (HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDCANCEL)
{
PostQuitMessage (0);
return TRUE;
}
return FALSE;
}
//==========================================================================
//
// FGraphicalStartupScreen Constructor
//
// This doesn't really do anything. The subclass is responsible for
// creating the resources that will be freed by this class's destructor.
//
//==========================================================================
FGraphicalStartupScreen::FGraphicalStartupScreen(int max_progress)
: FBasicStartupScreen(max_progress, false)
{
}
//==========================================================================
//
// FGraphicalStartupScreen Destructor
//
//==========================================================================
FGraphicalStartupScreen::~FGraphicalStartupScreen()
{
if (StartupScreen != NULL)
{
DestroyWindow (StartupScreen);
StartupScreen = NULL;
}
if (StartupBitmap != NULL)
{
ST_Util_FreeBitmap (StartupBitmap);
StartupBitmap = NULL;
}
}
//==========================================================================
//
//
//
//==========================================================================
void FHexenStartupScreen::SetWindowSize()
{
ST_Util_SizeWindowForBitmap(1);
LayoutMainWindow(Window, NULL);
InvalidateRect(StartupScreen, NULL, TRUE);
}
//==========================================================================
//
//
//
//==========================================================================
void FHereticStartupScreen::SetWindowSize()
{
ST_Util_SizeWindowForBitmap(1);
LayoutMainWindow(Window, NULL);
InvalidateRect(StartupScreen, NULL, TRUE);
}
//==========================================================================
//
//
//
//==========================================================================
void FStrifeStartupScreen::SetWindowSize()
{
ST_Util_SizeWindowForBitmap(2);
LayoutMainWindow(Window, NULL);
InvalidateRect(StartupScreen, NULL, TRUE);
}
//==========================================================================
//
// ST_Endoom
//
// Shows an ENDOOM text screen
//
//==========================================================================
int RunEndoom()
{
if (showendoom == 0 || endoomName.Len() == 0)
{
return 0;
}
int endoom_lump = fileSystem.CheckNumForFullName (endoomName, true);
uint8_t endoom_screen[4000];
uint8_t *font;
MSG mess;
BOOL bRet;
bool blinking = false, blinkstate = false;
int i;
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;
}
font = ST_Util_LoadFont (TEXT_FONT_NAME);
if (font == NULL)
{
return 0;
}
if (!ST_Util_CreateStartupWindow())
{
ST_Util_FreeFont (font);
return 0;
}
I_ShutdownGraphics ();
RestoreConView ();
S_StopMusic(true);
fileSystem.ReadFile (endoom_lump, endoom_screen);
// Draw the loading screen to a bitmap.
StartupBitmap = ST_Util_AllocTextBitmap (font);
ST_Util_DrawTextScreen (StartupBitmap, endoom_screen, font);
// Make the title banner go away.
if (GameTitleWindow != NULL)
{
DestroyWindow (GameTitleWindow);
GameTitleWindow = NULL;
}
ST_Util_SizeWindowForBitmap (1);
LayoutMainWindow (Window, NULL);
InvalidateRect (StartupScreen, NULL, TRUE);
// Does this screen need blinking?
for (i = 0; i < 80*25; ++i)
{
if (endoom_screen[1+i*2] & 0x80)
{
blinking = true;
break;
}
}
if (blinking && SetTimer (Window, 0x5A15A, BLINK_PERIOD, NULL) == 0)
{
blinking = false;
}
// Wait until any key has been pressed or a quit message has been received
for (;;)
{
bRet = GetMessage (&mess, NULL, 0, 0);
if (bRet == 0 || bRet == -1 || // bRet == 0 means we received WM_QUIT
mess.message == WM_KEYDOWN || mess.message == WM_SYSKEYDOWN || mess.message == WM_LBUTTONDOWN)
{
if (blinking)
{
KillTimer (Window, 0x5A15A);
}
ST_Util_FreeBitmap (StartupBitmap);
ST_Util_FreeFont (font);
return int(bRet == 0 ? mess.wParam : 0);
}
else if (blinking && mess.message == WM_TIMER && mess.hwnd == Window && mess.wParam == 0x5A15A)
{
ST_Util_UpdateTextBlink (StartupBitmap, endoom_screen, font, blinkstate);
blinkstate = !blinkstate;
}
TranslateMessage (&mess);
DispatchMessage (&mess);
}
}
void ST_Endoom()
{
TexMan.DeleteAll();
int code = RunEndoom();
throw CExitEvent(code);
}
//==========================================================================
//
// ST_Util_CreateStartupWindow
//
// Creates the static control that will draw the startup screen.
//
//==========================================================================
bool ST_Util_CreateStartupWindow ()
{
StartupScreen = CreateWindowEx (WS_EX_NOPARENTNOTIFY, L"STATIC", NULL,
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | SS_OWNERDRAW,
0, 0, 0, 0, Window, NULL, g_hInst, NULL);
if (StartupScreen == NULL)
{
return false;
}
SetWindowLong (StartupScreen, GWL_ID, IDC_STATIC_STARTUP);
return true;
}
//==========================================================================
//
// ST_Util_SizeWindowForBitmap
//
// Resizes the main window so that the startup bitmap will be drawn
// at the desired scale.
//
//==========================================================================
void ST_Util_SizeWindowForBitmap (int scale)
{
DEVMODE displaysettings;
int w, h, cx, cy, x, y;
RECT rect;
if (GameTitleWindow != NULL)
{
GetClientRect (GameTitleWindow, &rect);
}
else
{
rect.bottom = 0;
}
RECT sizerect = { 0, 0, StartupBitmap->bmiHeader.biWidth * scale,
StartupBitmap->bmiHeader.biHeight * scale + rect.bottom };
AdjustWindowRectEx(&sizerect, WS_VISIBLE|WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW);
w = sizerect.right - sizerect.left;
h = sizerect.bottom - sizerect.top;
// Resize the window, but keep its center point the same, unless that
// puts it partially offscreen.
memset (&displaysettings, 0, sizeof(displaysettings));
displaysettings.dmSize = sizeof(displaysettings);
EnumDisplaySettings (NULL, ENUM_CURRENT_SETTINGS, &displaysettings);
GetWindowRect (Window, &rect);
cx = (rect.left + rect.right) / 2;
cy = (rect.top + rect.bottom) / 2;
x = cx - w / 2;
y = cy - h / 2;
if (x + w > (int)displaysettings.dmPelsWidth)
{
x = displaysettings.dmPelsWidth - w;
}
if (x < 0)
{
x = 0;
}
if (y + h > (int)displaysettings.dmPelsHeight)
{
y = displaysettings.dmPelsHeight - h;
}
if (y < 0)
{
y = 0;
}
MoveWindow (Window, x, y, w, h, TRUE);
}
//==========================================================================
//
// ST_Util_InvalidateRect
//
// Invalidates the portion of the window that the specified rect of the
// bitmap appears in.
//
//==========================================================================
void ST_Util_InvalidateRect (HWND hwnd, BitmapInfo *bitmap_info, int left, int top, int right, int bottom)
{
RECT rect;
GetClientRect (hwnd, &rect);
rect.left = left * rect.right / bitmap_info->bmiHeader.biWidth - 1;
rect.top = top * rect.bottom / bitmap_info->bmiHeader.biHeight - 1;
rect.right = right * rect.right / bitmap_info->bmiHeader.biWidth + 1;
rect.bottom = bottom * rect.bottom / bitmap_info->bmiHeader.biHeight + 1;
InvalidateRect (hwnd, &rect, FALSE);
}
void ST_Util_InvalidateRect(BitmapInfo* bitmap_info, int left, int top, int right, int bottom)
{
ST_Util_InvalidateRect(StartupScreen , bitmap_info, left, top, right, bottom);
return mainwindow.RunMessageLoop(timer_callback, userdata);
}

File diff suppressed because it is too large Load diff

View file

@ -47,6 +47,7 @@
#include "printf.h"
#include "win32basevideo.h"
#include "cmdlib.h"
#include "i_mainwindow.h"
CVAR(Int, vid_adapter, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
@ -58,7 +59,7 @@ CVAR(Int, vid_adapter, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
Win32BaseVideo::Win32BaseVideo()
{
I_SetWndProc();
mainwindow.ShowGameView();
GetDisplayDeviceName();
}

View file

@ -3,6 +3,7 @@
#include "hardware.h"
#include "engineerrors.h"
#include <Windows.h>
#include "i_mainwindow.h"
#ifdef HAVE_SOFTPOLY
@ -10,8 +11,6 @@ EXTERN_CVAR(Bool, vid_vsync)
bool ViewportLinearScale();
extern HWND Window;
#include <d3d9.h>
#pragma comment(lib, "d3d9.lib")
@ -41,7 +40,7 @@ void I_PolyPresentInit()
}
RECT rect = {};
GetClientRect(Window, &rect);
GetClientRect(mainwindow.GetHandle(), &rect);
ClientWidth = rect.right;
ClientHeight = rect.bottom;
@ -52,10 +51,10 @@ void I_PolyPresentInit()
pp.BackBufferWidth = ClientWidth;
pp.BackBufferHeight = ClientHeight;
pp.BackBufferCount = 1;
pp.hDeviceWindow = Window;
pp.hDeviceWindow = mainwindow.GetHandle();
pp.PresentationInterval = CurrentVSync ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE;
HRESULT result = d3d9->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, Window, D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, nullptr, &device);
HRESULT result = d3d9->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, mainwindow.GetHandle(), D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp, nullptr, &device);
if (FAILED(result))
{
I_FatalError("IDirect3D9.CreateDevice failed");
@ -67,7 +66,7 @@ uint8_t *I_PolyPresentLock(int w, int h, bool vsync, int &pitch)
HRESULT result;
RECT rect = {};
GetClientRect(Window, &rect);
GetClientRect(mainwindow.GetHandle(), &rect);
if (rect.right != ClientWidth || rect.bottom != ClientHeight || CurrentVSync != vsync)
{
if (surface)
@ -86,7 +85,7 @@ uint8_t *I_PolyPresentLock(int w, int h, bool vsync, int &pitch)
pp.BackBufferWidth = ClientWidth;
pp.BackBufferHeight = ClientHeight;
pp.BackBufferCount = 1;
pp.hDeviceWindow = Window;
pp.hDeviceWindow = mainwindow.GetHandle();
pp.PresentationInterval = CurrentVSync ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE;
device->Reset(&pp);
}

View file

@ -7,16 +7,14 @@
#endif
#include "volk/volk.h"
extern HWND Window;
#include "i_mainwindow.h"
void I_GetVulkanDrawableSize(int *width, int *height)
{
assert(Window);
assert(mainwindow.GetHandle());
RECT clientRect = { 0 };
GetClientRect(Window, &clientRect);
GetClientRect(mainwindow.GetHandle(), &clientRect);
if (width != nullptr)
{
@ -67,7 +65,7 @@ bool I_CreateVulkanSurface(VkInstance instance, VkSurfaceKHR *surface)
windowCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
windowCreateInfo.pNext = nullptr;
windowCreateInfo.flags = 0;
windowCreateInfo.hwnd = Window;
windowCreateInfo.hwnd = mainwindow.GetHandle();
windowCreateInfo.hinstance = GetModuleHandle(nullptr);
const VkResult result = vkCreateWin32SurfaceKHR(instance, &windowCreateInfo, nullptr, surface);

View file

@ -561,6 +561,11 @@ void OpenGLFrameBuffer::PostProcessScene(bool swscene, int fixedcm, float flash,
GLRenderer->PostProcessScene(fixedcm, flash, afterBloomDrawEndScene2D);
}
bool OpenGLFrameBuffer::CompileNextShader()
{
return GLRenderer->mShaderManager->CompileNextShader();
}
//==========================================================================
//
// OpenGLFrameBuffer :: WipeStartScreen

View file

@ -23,7 +23,7 @@ public:
explicit OpenGLFrameBuffer() {}
OpenGLFrameBuffer(void *hMonitor, bool fullscreen) ;
~OpenGLFrameBuffer();
bool CompileNextShader() override;
void InitializeState() override;
void Update() override;

View file

@ -672,7 +672,7 @@ bool FShader::Bind()
FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType)
{
FString defines;
defines += shaderdefines;
if (shaderdefines) defines += shaderdefines;
// this can't be in the shader code due to ATI strangeness.
if (!usediscard) defines += "#define NO_ALPHATEST\n";
if (passType == GBUFFER_PASS) defines += "#define GBUFFER_PASS\n";
@ -707,6 +707,20 @@ FShaderManager::FShaderManager()
mPassShaders.Push(new FShaderCollection((EPassType)passType));
}
bool FShaderManager::CompileNextShader()
{
if (mPassShaders[mCompilePass]->CompileNextShader())
{
mCompilePass++;
if (mCompilePass >= MAX_PASS_TYPES)
{
mCompilePass = -1;
return true;
}
}
return false;
}
FShaderManager::~FShaderManager()
{
glUseProgram(0);
@ -727,7 +741,7 @@ void FShaderManager::SetActiveShader(FShader *sh)
FShader *FShaderManager::BindEffect(int effect, EPassType passType)
{
if (passType < mPassShaders.Size())
if (passType < mPassShaders.Size() && mCompilePass == -1)
return mPassShaders[passType]->BindEffect(effect);
else
return nullptr;
@ -735,7 +749,11 @@ FShader *FShaderManager::BindEffect(int effect, EPassType passType)
FShader *FShaderManager::Get(unsigned int eff, bool alphateston, EPassType passType)
{
if (r_skipmats && eff >= 3 && eff <= 4)
if (mCompilePass > -1)
{
return mPassShaders[0]->Get(0, false);
}
if ((r_skipmats && eff >= 3 && eff <= 4))
eff = 0;
if (passType < mPassShaders.Size())
@ -752,7 +770,14 @@ FShader *FShaderManager::Get(unsigned int eff, bool alphateston, EPassType passT
FShaderCollection::FShaderCollection(EPassType passType)
{
CompileShaders(passType);
mPassType = passType;
mMaterialShaders.Clear();
mMaterialShadersNAT.Clear();
for (int i = 0; i < MAX_EFFECTS; i++)
{
mEffectShaders[i] = NULL;
}
CompileNextShader();
}
//==========================================================================
@ -772,35 +797,47 @@ FShaderCollection::~FShaderCollection()
//
//==========================================================================
void FShaderCollection::CompileShaders(EPassType passType)
bool FShaderCollection::CompileNextShader()
{
mMaterialShaders.Clear();
mMaterialShadersNAT.Clear();
for (int i = 0; i < MAX_EFFECTS; i++)
int i = mCompileIndex;
if (mCompileState == 0)
{
mEffectShaders[i] = NULL;
}
for(int i=0;defaultshaders[i].ShaderName != NULL;i++)
{
FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, true, passType);
FShader *shc = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, true, mPassType);
mMaterialShaders.Push(shc);
if (i < SHADER_NoTexture)
mCompileIndex++;
if (defaultshaders[mCompileIndex].ShaderName == nullptr)
{
FShader *shc1 = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, false, passType);
mMaterialShadersNAT.Push(shc1);
mCompileIndex = 0;
mCompileState++;
}
}
for(unsigned i = 0; i < usershaders.Size(); i++)
else if (mCompileState == 1)
{
FShader *shc1 = Compile(defaultshaders[i].ShaderName, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, false, mPassType);
mMaterialShadersNAT.Push(shc1);
mCompileIndex++;
if (mCompileIndex >= SHADER_NoTexture)
{
mCompileIndex = 0;
mCompileState++;
if (usershaders.Size() == 0) mCompileState++;
}
}
else if (mCompileState == 2)
{
FString name = ExtractFileBase(usershaders[i].shader);
FString defines = defaultshaders[usershaders[i].shaderType].Defines + usershaders[i].defines;
FShader *shc = Compile(name, usershaders[i].shader, defaultshaders[usershaders[i].shaderType].lightfunc, defines, true, passType);
FShader *shc = Compile(name, usershaders[i].shader, defaultshaders[usershaders[i].shaderType].lightfunc, defines, true, mPassType);
mMaterialShaders.Push(shc);
mCompileIndex++;
if (mCompileIndex >= (int)usershaders.Size())
{
mCompileIndex = 0;
mCompileState++;
}
}
for(int i=0;i<MAX_EFFECTS;i++)
else if (mCompileState == 3)
{
FShader *eff = new FShader(effectshaders[i].ShaderName);
if (!eff->Load(effectshaders[i].ShaderName, effectshaders[i].vp, effectshaders[i].fp1,
@ -809,7 +846,13 @@ void FShaderCollection::CompileShaders(EPassType passType)
delete eff;
}
else mEffectShaders[i] = eff;
mCompileIndex++;
if (mCompileIndex >= MAX_EFFECTS)
{
return true;
}
}
return false;
}
//==========================================================================

View file

@ -309,10 +309,12 @@ public:
FShader *Get(unsigned int eff, bool alphateston, EPassType passType);
void SetActiveShader(FShader *sh);
bool CompileNextShader();
private:
FShader *mActiveShader = nullptr;
TArray<FShaderCollection*> mPassShaders;
int mCompilePass = 0;
friend class FShader;
};
@ -322,21 +324,23 @@ class FShaderCollection
TArray<FShader*> mMaterialShaders;
TArray<FShader*> mMaterialShadersNAT;
FShader *mEffectShaders[MAX_EFFECTS];
int mCompileState = 0, mCompileIndex = 0;
EPassType mPassType;
void Clean();
void CompileShaders(EPassType passType);
public:
FShaderCollection(EPassType passType);
~FShaderCollection();
FShader *Compile(const char *ShaderName, const char *ShaderPath, const char *LightModePath, const char *shaderdefines, bool usediscard, EPassType passType);
int Find(const char *mame);
bool CompileNextShader();
FShader *BindEffect(int effect);
FShader *Get(unsigned int eff, bool alphateston)
{
// indices 0-2 match the warping modes, 3 no texture, the following are custom
if (!alphateston && eff <= 2)
if (!alphateston && eff < SHADER_NoTexture && mCompileState == -1)
{
return mMaterialShadersNAT[eff]; // Non-alphatest shaders are only created for default, warp1+2 and brightmap. The rest won't get used anyway
}

View file

@ -26,6 +26,7 @@ public:
void InitializeState() override;
void Update() override;
int GetShaderCount() override { return 0; }
void FirstEye() override;
void NextEye(int eyecount) override;

View file

@ -733,7 +733,7 @@ bool FShader::Bind(ShaderFlavourData& flavour)
//==========================================================================
//
// Since all shaders are REQUIRED, any error here needs to be fatal
//
//
//==========================================================================
@ -744,21 +744,8 @@ FShader *FShaderCollection::Compile (const char *ShaderName, const char *ShaderP
// this can't be in the shader code due to ATI strangeness.
if (!usediscard) defines += "#define NO_ALPHATEST\n";
FShader *shader = NULL;
try
{
shader = new FShader(ShaderName);
if (!shader->Configure(ShaderName, "shaders_gles/glsl/main.vp", "shaders_gles/glsl/main.fp", ShaderPath, LightModePath, defines.GetChars()))
{
I_FatalError("Unable to load shader %s\n", ShaderName);
}
}
catch(CRecoverableError &err)
{
if (shader != NULL) delete shader;
shader = NULL;
I_FatalError("Unable to load shader %s:\n%s\n", ShaderName, err.GetMessage());
}
FShader *shader = new FShader(ShaderName);
shader->Configure(ShaderName, "shaders_gles/glsl/main.vp", "shaders_gles/glsl/main.fp", ShaderPath, LightModePath, defines.GetChars());
return shader;
}

View file

@ -153,7 +153,6 @@ ADD_STAT(lightstats)
static int printstats;
static bool switchfps;
static uint64_t waitstart;
extern uint64_t LastCount;
EXTERN_CVAR(Bool, vid_fps)
void CheckBench()

View file

@ -34,7 +34,11 @@
*/
#include "cmdlib.h"
#include "hw_shaderpatcher.h"
#include "textures.h"
#include "hw_renderstate.h"
#include "v_video.h"
static bool IsGlslWhitespace(char c)
@ -296,3 +300,12 @@ const FEffectShader effectshaders[] =
{ "burn", "shaders/glsl/main.vp", "shaders/glsl/burn.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" },
{ "stencil", "shaders/glsl/main.vp", "shaders/glsl/stencil.fp", nullptr, nullptr, "#define SIMPLE\n#define NO_ALPHATEST\n" },
};
int DFrameBuffer::GetShaderCount()
{
int i;
for (i = 0; defaultshaders[i].ShaderName != nullptr; i++);
return MAX_PASS_TYPES * (countof(defaultshaders) - 1 + usershaders.Size() + MAX_EFFECTS + SHADER_NoTexture);
}

View file

@ -28,3 +28,4 @@ struct FEffectShader
extern const FDefaultShader defaultshaders[];
extern const FEffectShader effectshaders[];

View file

@ -26,6 +26,7 @@ public:
PolyFrameBuffer(void *hMonitor, bool fullscreen);
~PolyFrameBuffer();
int GetShaderCount() override { return 0; }
void Update() override;

View file

@ -32,10 +32,6 @@
#include "polyrenderer/drawers/poly_triangle.h"
#include <chrono>
#ifdef WIN32
void PeekThreadedErrorPane();
#endif
CVAR(Int, r_multithreaded, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
CVAR(Int, r_debug_draw, 0, 0);
@ -101,12 +97,7 @@ void DrawerThreads::WaitForWorkers()
std::unique_lock<std::mutex> end_lock(queue->end_mutex);
if (!queue->end_condition.wait_for(end_lock, 5s, [&]() { return queue->tasks_left == 0; }))
{
#ifdef WIN32
PeekThreadedErrorPane();
#endif
// Invoke the crash reporter so that we can capture the call stack of whatever the hung worker thread is doing
int *threadCrashed = nullptr;
*threadCrashed = 0xdeadbeef;
I_FatalError("Drawer threads did not finish within 5 seconds!");
}
end_lock.unlock();

View file

@ -158,6 +158,8 @@ public:
virtual void InitializeState() = 0; // For stuff that needs 'screen' set.
virtual bool IsVulkan() { return false; }
virtual bool IsPoly() { return false; }
virtual int GetShaderCount();
virtual bool CompileNextShader() { return true; }
void SetAABBTree(hwrenderer::LevelAABBTree * tree)
{
mShadowMap.SetAABBTree(tree);
@ -290,7 +292,6 @@ extern DFrameBuffer *screen;
#define SCREENWIDTH (screen->GetWidth ())
#define SCREENHEIGHT (screen->GetHeight ())
#define SCREENPITCH (screen->GetPitch ())
EXTERN_CVAR (Float, vid_gamma)

View file

@ -48,7 +48,7 @@ VkSampleCountFlagBits VkRenderBuffers::GetBestSampleCount()
int samples = 1;
VkSampleCountFlags bit = VK_SAMPLE_COUNT_1_BIT;
VkSampleCountFlags best = bit;
while (samples < requestedSamples)
while (samples <= requestedSamples)
{
if (deviceSampleCounts & bit)
{

View file

@ -28,51 +28,93 @@
#include "version.h"
#include <ShaderLang.h>
bool VkShaderManager::CompileNextShader()
{
const char *mainvp = "shaders/glsl/main.vp";
const char *mainfp = "shaders/glsl/main.fp";
int i = compileIndex;
if (compileState == 0)
{
// regular material shaders
VkShaderProgram prog;
prog.vert = LoadVertShader(defaultshaders[i].ShaderName, mainvp, defaultshaders[i].Defines);
prog.frag = LoadFragShader(defaultshaders[i].ShaderName, mainfp, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, true, compilePass == GBUFFER_PASS);
mMaterialShaders[compilePass].push_back(std::move(prog));
compileIndex++;
if (defaultshaders[compileIndex].ShaderName == nullptr)
{
compileIndex = 0;
compileState++;
}
}
else if (compileState == 1)
{
// NAT material shaders
VkShaderProgram natprog;
natprog.vert = LoadVertShader(defaultshaders[i].ShaderName, mainvp, defaultshaders[i].Defines);
natprog.frag = LoadFragShader(defaultshaders[i].ShaderName, mainfp, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, false, compilePass == GBUFFER_PASS);
mMaterialShadersNAT[compilePass].push_back(std::move(natprog));
compileIndex++;
if (compileIndex == SHADER_NoTexture)
{
compileIndex = 0;
compileState++;
if (usershaders.Size() == 0) compileState++;
}
}
else if (compileState == 2)
{
// user shaders
const FString& name = ExtractFileBase(usershaders[i].shader);
FString defines = defaultshaders[usershaders[i].shaderType].Defines + usershaders[i].defines;
VkShaderProgram prog;
prog.vert = LoadVertShader(name, mainvp, defines);
prog.frag = LoadFragShader(name, mainfp, usershaders[i].shader, defaultshaders[usershaders[i].shaderType].lightfunc, defines, true, compilePass == GBUFFER_PASS);
mMaterialShaders[compilePass].push_back(std::move(prog));
compileIndex++;
if (compileIndex >= (int)usershaders.Size())
{
compileIndex = 0;
compileState++;
}
}
else if (compileState == 3)
{
// Effect shaders
VkShaderProgram prog;
prog.vert = LoadVertShader(effectshaders[i].ShaderName, effectshaders[i].vp, effectshaders[i].defines);
prog.frag = LoadFragShader(effectshaders[i].ShaderName, effectshaders[i].fp1, effectshaders[i].fp2, effectshaders[i].fp3, effectshaders[i].defines, true, compilePass == GBUFFER_PASS);
mEffectShaders[compilePass].push_back(std::move(prog));
compileIndex++;
if (compileIndex >= MAX_EFFECTS)
{
compileIndex = 0;
compilePass++;
if (compilePass == MAX_PASS_TYPES)
{
compileIndex = -1; // we're done.
return true;
}
compileState = 0;
}
}
return false;
}
VkShaderManager::VkShaderManager(VulkanDevice *device) : device(device)
{
ShInitialize();
const char *mainvp = "shaders/glsl/main.vp";
const char *mainfp = "shaders/glsl/main.fp";
for (int j = 0; j < MAX_PASS_TYPES; j++)
{
bool gbufferpass = j;
for (int i = 0; defaultshaders[i].ShaderName != nullptr; i++)
{
VkShaderProgram prog;
prog.vert = LoadVertShader(defaultshaders[i].ShaderName, mainvp, defaultshaders[i].Defines);
prog.frag = LoadFragShader(defaultshaders[i].ShaderName, mainfp, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, true, gbufferpass);
mMaterialShaders[j].push_back(std::move(prog));
if (i < SHADER_NoTexture)
{
VkShaderProgram natprog;
natprog.vert = LoadVertShader(defaultshaders[i].ShaderName, mainvp, defaultshaders[i].Defines);
natprog.frag = LoadFragShader(defaultshaders[i].ShaderName, mainfp, defaultshaders[i].gettexelfunc, defaultshaders[i].lightfunc, defaultshaders[i].Defines, false, gbufferpass);
mMaterialShadersNAT[j].push_back(std::move(natprog));
}
}
for (unsigned i = 0; i < usershaders.Size(); i++)
{
FString name = ExtractFileBase(usershaders[i].shader);
FString defines = defaultshaders[usershaders[i].shaderType].Defines + usershaders[i].defines;
VkShaderProgram prog;
prog.vert = LoadVertShader(name, mainvp, defines);
prog.frag = LoadFragShader(name, mainfp, usershaders[i].shader, defaultshaders[usershaders[i].shaderType].lightfunc, defines, true, gbufferpass);
mMaterialShaders[j].push_back(std::move(prog));
}
for (int i = 0; i < MAX_EFFECTS; i++)
{
VkShaderProgram prog;
prog.vert = LoadVertShader(effectshaders[i].ShaderName, effectshaders[i].vp, effectshaders[i].defines);
prog.frag = LoadFragShader(effectshaders[i].ShaderName, effectshaders[i].fp1, effectshaders[i].fp2, effectshaders[i].fp3, effectshaders[i].defines, true, gbufferpass);
mEffectShaders[j].push_back(std::move(prog));
}
}
CompileNextShader();
}
VkShaderManager::~VkShaderManager()
@ -82,7 +124,7 @@ VkShaderManager::~VkShaderManager()
VkShaderProgram *VkShaderManager::GetEffect(int effect, EPassType passType)
{
if (effect >= 0 && effect < MAX_EFFECTS && mEffectShaders[passType][effect].frag)
if (compileIndex == -1 && effect >= 0 && effect < MAX_EFFECTS && mEffectShaders[passType][effect].frag)
{
return &mEffectShaders[passType][effect];
}
@ -91,8 +133,9 @@ VkShaderProgram *VkShaderManager::GetEffect(int effect, EPassType passType)
VkShaderProgram *VkShaderManager::Get(unsigned int eff, bool alphateston, EPassType passType)
{
if (compileIndex != -1) return &mMaterialShaders[0][0];
// indices 0-2 match the warping modes, 3 no texture, the following are custom
if (!alphateston && eff <= 2)
if (!alphateston && eff < SHADER_NoTexture)
{
return &mMaterialShadersNAT[passType][eff]; // Non-alphatest shaders are only created for default, warp1+2. The rest won't get used anyway
}

View file

@ -65,6 +65,7 @@ public:
VkShaderProgram *GetEffect(int effect, EPassType passType);
VkShaderProgram *Get(unsigned int eff, bool alphateston, EPassType passType);
bool CompileNextShader();
private:
std::unique_ptr<VulkanShader> LoadVertShader(FString shadername, const char *vert_lump, const char *defines);
@ -79,4 +80,6 @@ private:
std::vector<VkShaderProgram> mMaterialShaders[MAX_PASS_TYPES];
std::vector<VkShaderProgram> mMaterialShadersNAT[MAX_PASS_TYPES];
std::vector<VkShaderProgram> mEffectShaders[MAX_PASS_TYPES];
uint8_t compilePass = 0, compileState = 0;
int compileIndex = 0;
};

View file

@ -277,16 +277,20 @@ void VulkanDevice::CreateInstance()
Extensions = GetExtensions();
EnabledExtensions = GetPlatformExtensions();
std::string debugLayer = "VK_LAYER_LUNARG_standard_validation";
std::string debugLayer = "VK_LAYER_KHRONOS_validation";
bool wantDebugLayer = vk_debug;
bool debugLayerFound = false;
for (const VkLayerProperties &layer : AvailableLayers)
if (wantDebugLayer)
{
if (layer.layerName == debugLayer && wantDebugLayer)
for (const VkLayerProperties& layer : AvailableLayers)
{
EnabledValidationLayers.push_back(debugLayer.c_str());
EnabledExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
debugLayerFound = true;
if (layer.layerName == debugLayer)
{
EnabledValidationLayers.push_back(layer.layerName);
EnabledExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
debugLayerFound = true;
break;
}
}
}

View file

@ -207,6 +207,11 @@ void VulkanFrameBuffer::Update()
Super::Update();
}
bool VulkanFrameBuffer::CompileNextShader()
{
return mShaderManager->CompileNextShader();
}
void VulkanFrameBuffer::DeleteFrameObjects(bool uploadOnly)
{
FrameTextureUpload.Buffers.clear();

View file

@ -77,7 +77,7 @@ public:
void Update() override;
void InitializeState() override;
bool CompileNextShader() override;
void PrecacheMaterial(FMaterial *mat, int translation) override;
void UpdatePalette() override;
const char* DeviceName() const override;

View file

@ -380,12 +380,13 @@ VulkanDescriptorSet* VkMaterial::GetDescriptorSet(const FMaterialState& state)
auto base = Source();
int clampmode = state.mClampMode;
int translation = state.mTranslation;
auto translationp = IsLuminosityTranslation(translation)? translation : intptr_t(GPalette.GetTranslation(GetTranslationType(translation), GetTranslationIndex(translation)));
clampmode = base->GetClampMode(clampmode);
for (auto& set : mDescriptorSets)
{
if (set.descriptor && set.clampmode == clampmode && set.flags == translation) return set.descriptor.get();
if (set.descriptor && set.clampmode == clampmode && set.remap == translationp) return set.descriptor.get();
}
int numLayers = NumLayers();
@ -430,7 +431,7 @@ VulkanDescriptorSet* VkMaterial::GetDescriptorSet(const FMaterialState& state)
}
update.updateSets(fb->device);
mDescriptorSets.emplace_back(clampmode, translation, std::move(descriptor));
mDescriptorSets.emplace_back(clampmode, translationp, std::move(descriptor));
return mDescriptorSets.back().descriptor.get();
}

View file

@ -70,13 +70,13 @@ class VkMaterial : public FMaterial
struct DescriptorEntry
{
int clampmode;
int flags;
intptr_t remap;
std::unique_ptr<VulkanDescriptorSet> descriptor;
DescriptorEntry(int cm, int f, std::unique_ptr<VulkanDescriptorSet>&& d)
DescriptorEntry(int cm, intptr_t f, std::unique_ptr<VulkanDescriptorSet>&& d)
{
clampmode = cm;
flags = f;
remap = f;
descriptor = std::move(d);
}
};

View file

@ -11158,7 +11158,7 @@ ExpEmit FxLocalArrayDeclaration::Emit(VMFunctionBuilder *build)
auto zero = build->GetConstantInt(0);
auto elementSizeConst = build->GetConstantInt(static_cast<PArray *>(ValueType)->ElementSize);
int arrOffsetReg;
int arrOffsetReg = 0;
if (!isDynamicArray)
{
arrOffsetReg = build->Registers[REGT_POINTER].Get(1);

View file

@ -421,6 +421,45 @@ PNamespace *ParseOneScript(const int baselump, ZCCParseState &state)
for (unsigned i = 0; i < Includes.Size(); i++)
{
lumpnum = fileSystem.CheckNumForFullName(Includes[i], true);
if (lumpnum == -1 && ( Includes[i].IndexOf("./") == 0 || Includes[i].IndexOf("../") == 0 ) ) // relative path resolving
{
FString fullPath = IncludeLocs[i].FileName.GetChars(); // get full path, format 'wad:filepath/filename'
long start = fullPath.IndexOf(":"); // find first ':'
long end = fullPath.LastIndexOf("/"); // find last '/'
if (start!=-1&&end!=-1)
{
FString resolvedPath = fullPath.Mid(start + 1, end - start - 1); // extract filepath from string
FString relativePath = Includes[i];
if ( relativePath.IndexOf("./") == 0 ) // strip initial marker
{
relativePath = relativePath.Mid(2);
}
bool pathOk = true;
while (relativePath.IndexOf("../") == 0) // go back one folder for each '..'
{
relativePath = relativePath.Mid(3);
long slash_index = resolvedPath.LastIndexOf("/");
if (slash_index != -1) {
resolvedPath = resolvedPath.Mid(0,slash_index);
} else {
pathOk = false;
break;
}
}
if ( pathOk ) // if '..' parsing was successful
{
resolvedPath += "/" + relativePath; // add relative path
lumpnum = fileSystem.CheckNumForFullName(resolvedPath, true); // check for relative include
}
}
}
if (lumpnum == -1)
{
IncludeLocs[i].Message(MSG_ERROR, "Include script lump %s not found", Includes[i].GetChars());

View file

@ -0,0 +1,199 @@
/*
** 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 -------------------------------------------------
CUSTOM_CVAR(Int, showendoom, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
{
if (self < 0) self = 0;
else if (self > 2) self=2;
}
// 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);
}
}
//==========================================================================
//
// 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);
}

View file

@ -0,0 +1,716 @@
/*
** 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
0x263a, //#START OF HEADING
0x263B, //#START OF TEXT
0x2665, //#END OF TEXT
0x2666, //#END OF TRANSMISSION
0x2663, //#ENQUIRY
0x2660, //#ACKNOWLEDGE
0x2022, //#BELL
0x25d8, //#BACKSPACE
0x25cb, //#HORIZONTAL TABULATION
0x25d9, //#LINE FEED
0x2642, //#VERTICAL TABULATION
0x2640, //#FORM FEED
0x266a, //#CARRIAGE RETURN
0x266b, //#SHIFT OUT
0x263c, //#SHIFT IN
0x25ba, //#DATA LINK ESCAPE
0x25c4, //#DEVICE CONTROL ONE
0x2195, //#DEVICE CONTROL TWO
0x203c, //#DEVICE CONTROL THREE
0x00b6, //#DEVICE CONTROL FOUR
0x00a7, //#NEGATIVE ACKNOWLEDGE
0x25ac, //#SYNCHRONOUS IDLE
0x21ab, //#END OF TRANSMISSION BLOCK
0x2191, //#CANCEL
0x2193, //#END OF MEDIUM
0x2192, //#SUBSTITUTE
0x2190, //#ESCAPE
0x221f, //#FILE SEPARATOR
0x2194, //#GROUP SEPARATOR
0x25b2, //#RECORD SEPARATOR
0x25bc, //#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
0x2302, //#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)
{
return CreateHexenStartScreen(max_progress);
}
else if (GameStartupInfo.Type == FStartupInfo::HereticStartup)
{
return CreateHereticStartScreen(max_progress);
}
else if (GameStartupInfo.Type == FStartupInfo::StrifeStartup)
{
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);
}
return nullptr;
}
//==========================================================================
//
// 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 > 30 || force)
{
screen->FrameTime = nowtime;
screen->BeginFrame();
twod->ClearClipRect();
I_GetEvent();
ValidateTexture();
float displaywidth;
float displayheight;
twod->Begin(screen->GetWidth(), screen->GetHeight());
// At this point the shader for untextured rendering has not been loaded yet, so we got to clear the screen by rendering a texture with black color.
DrawTexture(twod, StartupTexture, 0, 0, DTA_VirtualWidthF, StartupTexture->GetDisplayWidth(), DTA_VirtualHeightF, StartupTexture->GetDisplayHeight(), DTA_KeepRatio, true, DTA_Color, PalEntry(255,0,0,0), TAG_END);
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);
}
}

View file

@ -0,0 +1,104 @@
#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 <stdint.h>
#include <functional>
#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 void ST_Endoom();

View file

@ -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;
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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -0,0 +1,202 @@
/*
** 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 "textures.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;
StartupTexture->CleanHardwareData(true);
}
}
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);
}

View file

@ -59,9 +59,9 @@ CVAR(Color, crosshaircolor, 0xff0000, CVAR_ARCHIVE);
CVAR(Int, crosshairhealth, 2, CVAR_ARCHIVE);
CVARD(Float, crosshairscale, 0.5, CVAR_ARCHIVE, "changes the size of the crosshair");
CVAR(Bool, crosshairgrow, false, CVAR_ARCHIVE);
EXTERN_CVAR(Bool, vid_fps)
EXTERN_CVAR(Float, hud_scalefactor)
EXTERN_CVAR(Bool, hud_aspectscale)
void ST_LoadCrosshair(int num, bool alwaysload)
{
@ -353,10 +353,12 @@ void DStatusBarCore::SetScale()
int vert = VerticalResolution;
double refaspect = horz / double(vert);
double screenaspect = w / double(h);
double aspectscale = 1.0;
if ((horz == 320 && vert == 200) || (horz == 640 && vert == 400))
{
refaspect = 1.333;
if (!hud_aspectscale) aspectscale = 1 / 1.2;
}
if (screenaspect < refaspect)
@ -370,14 +372,14 @@ void DStatusBarCore::SetScale()
refw = h * refaspect;
}
refw *= hud_scalefactor;
refh *= hud_scalefactor;
refh *= hud_scalefactor * aspectscale;
int sby = VerticalResolution - RelTop;
int sby = vert - int(RelTop * hud_scalefactor * aspectscale);
// Use full pixels for destination size.
ST_X = xs_CRoundToInt((w - refw) / 2);
ST_Y = xs_CRoundToInt(h - refh);
SBarTop = Scale(sby, h, VerticalResolution);
SBarTop = Scale(sby, h, vert);
SBarScale.X = refw / horz;
SBarScale.Y = refh / vert;
}
@ -565,9 +567,6 @@ void DStatusBarCore::DrawGraphic(FGameTexture* tex, double x, double y, int flag
case DI_SCREEN_BOTTOM: orgy = twod->GetHeight(); break;
}
// move stuff in the top right corner a bit down if the fps counter is on.
if ((flags & (DI_SCREEN_HMASK | DI_SCREEN_VMASK)) == DI_SCREEN_RIGHT_TOP && vid_fps) y += 10;
DVector2 Scale = GetHUDScale();
x *= Scale.X;
@ -659,9 +658,6 @@ void DStatusBarCore::DrawRotated(FGameTexture* tex, double x, double y, int flag
case DI_SCREEN_BOTTOM: orgy = twod->GetHeight(); break;
}
// move stuff in the top right corner a bit down if the fps counter is on.
if ((flags & (DI_SCREEN_HMASK | DI_SCREEN_VMASK)) == DI_SCREEN_RIGHT_TOP && vid_fps) y += 10;
x *= Scale.X;
y *= Scale.Y;
scaleX *= Scale.X;
@ -739,9 +735,6 @@ void DStatusBarCore::DrawString(FFont* font, const FString& cstring, double x, d
case DI_SCREEN_VCENTER: orgy = twod->GetHeight() / 2; break;
case DI_SCREEN_BOTTOM: orgy = twod->GetHeight(); break;
}
// move stuff in the top right corner a bit down if the fps counter is on.
if ((flags & (DI_SCREEN_HMASK | DI_SCREEN_VMASK)) == DI_SCREEN_RIGHT_TOP && vid_fps) y += 10;
}
else
{
@ -904,9 +897,6 @@ void DStatusBarCore::TransformRect(double& x, double& y, double& w, double& h, i
case DI_SCREEN_BOTTOM: orgy = twod->GetHeight(); break;
}
// move stuff in the top right corner a bit down if the fps counter is on.
if ((flags & (DI_SCREEN_HMASK | DI_SCREEN_VMASK)) == DI_SCREEN_RIGHT_TOP && vid_fps) y += 10;
DVector2 Scale = GetHUDScale();
x *= Scale.X;

View file

@ -209,13 +209,14 @@ typedef void (*CopyFunc)(uint8_t *pout, const uint8_t *pin, int count, int step,
iCopyColors<cIA, cBGRA, op>, \
iCopyColors<cCMYK, cBGRA, op>, \
iCopyColors<cYCbCr, cBGRA, op>, \
iCopyColors<cYCCK, cBGRA, op>, \
iCopyColors<cBGR, cBGRA, op>, \
iCopyColors<cBGRA, cBGRA, op>, \
iCopyColors<cI16, cBGRA, op>, \
iCopyColors<cRGB555, cBGRA, op>, \
iCopyColors<cPalEntry, cBGRA, op> \
}
static const CopyFunc copyfuncs[][11]={
static const CopyFunc copyfuncs[][12]={
COPY_FUNCS(bCopy),
COPY_FUNCS(bBlend),
COPY_FUNCS(bAdd),
@ -402,7 +403,6 @@ void FBitmap::CopyPixelDataRGB(int originx, int originy, const uint8_t *patch, i
}
}
template<class TDest, class TBlend>
void iCopyPaletted(uint8_t *buffer, const uint8_t * patch, int srcwidth, int srcheight, int Pitch,
int step_x, int step_y, int rotate, const PalEntry * palette, FCopyInfo *inf)

View file

@ -65,6 +65,7 @@ enum ColorType
CF_IA,
CF_CMYK,
CF_YCbCr,
CF_YCCK,
CF_BGR,
CF_BGRA,
CF_I16,
@ -326,6 +327,15 @@ struct cYCbCr
static __forceinline int Gray(const unsigned char * p) { return (R(p) * 77 + G(p) * 143 + B(p) * 36) >> 8; }
};
struct cYCCK
{
static __forceinline unsigned char R(const unsigned char* p) { auto myR = cYCbCr::R(p); return p[3] - ((myR * p[3]) >> 8); }
static __forceinline unsigned char G(const unsigned char* p) { auto myG = cYCbCr::G(p); return p[3] - ((myG * p[3]) >> 8); }
static __forceinline unsigned char B(const unsigned char* p) { auto myB = cYCbCr::B(p); return p[3] - ((myB * p[3]) >> 8); }
static __forceinline unsigned char A(const unsigned char* p, uint8_t x, uint8_t y, uint8_t z) { return 255; }
static __forceinline int Gray(const unsigned char* p) { return (R(p) * 77 + G(p) * 143 + B(p) * 36) >> 8; }
};
struct cBGR
{
static __forceinline unsigned char R(const unsigned char * p) { return p[2]; }

View file

@ -282,6 +282,7 @@ TArray<uint8_t> FJPEGTexture::CreatePalettedPixels(int conversion)
jpeg_read_header(&cinfo, TRUE);
if (!((cinfo.out_color_space == JCS_RGB && cinfo.num_components == 3) ||
(cinfo.out_color_space == JCS_CMYK && cinfo.num_components == 4) ||
(cinfo.out_color_space == JCS_YCCK && cinfo.num_components == 4) ||
(cinfo.out_color_space == JCS_YCbCr && cinfo.num_components == 3) ||
(cinfo.out_color_space == JCS_GRAYSCALE && cinfo.num_components == 1)))
{
@ -350,6 +351,24 @@ TArray<uint8_t> FJPEGTexture::CreatePalettedPixels(int conversion)
}
break;
case JCS_YCCK:
// Probably useless but since I had the formula available...
for (int x = Width; x > 0; --x)
{
double Y = in[0], Cb = in[1], Cr = in[2];
int K = in[3];
int r = clamp((int)(Y + 1.40200 * (Cr - 0x80)), 0, 255);
int g = clamp((int)(Y - 0.34414 * (Cb - 0x80) - 0.71414 * (Cr - 0x80)), 0, 255);
int b = clamp((int)(Y + 1.77200 * (Cb - 0x80)), 0, 255);
r = r - ((r * K) >> 8);
g = g - ((g * K) >> 8);
b = b - ((b * K) >> 8);
*out = ImageHelpers::RGBToPalette(doalpha, r, g, b);
out += Height;
in += 4;
}
break;
default:
// The other colorspaces were considered above and discarded,
// but GCC will complain without a default for them here.
@ -402,6 +421,7 @@ int FJPEGTexture::CopyPixels(FBitmap *bmp, int conversion)
if (!((cinfo.out_color_space == JCS_RGB && cinfo.num_components == 3) ||
(cinfo.out_color_space == JCS_CMYK && cinfo.num_components == 4) ||
(cinfo.out_color_space == JCS_YCCK && cinfo.num_components == 4) ||
(cinfo.out_color_space == JCS_YCbCr && cinfo.num_components == 3) ||
(cinfo.out_color_space == JCS_GRAYSCALE && cinfo.num_components == 1)))
{
@ -439,6 +459,11 @@ int FJPEGTexture::CopyPixels(FBitmap *bmp, int conversion)
4, cinfo.output_width * cinfo.output_components, 0, CF_CMYK);
break;
case JCS_YCCK:
bmp->CopyPixelDataRGB(0, 0, buff.Data(), cinfo.output_width, cinfo.output_height,
4, cinfo.output_width * cinfo.output_components, 0, CF_YCCK);
break;
case JCS_YCbCr:
bmp->CopyPixelDataRGB(0, 0, buff.Data(), cinfo.output_width, cinfo.output_height,
4, cinfo.output_width * cinfo.output_components, 0, CF_YCbCr);

View file

@ -62,9 +62,9 @@ public:
//
//==========================================================================
static bool CheckIfRaw(FileReader & data)
bool CheckIfRaw(FileReader & data, int desiredsize)
{
if (data.GetLength() != 64000) return false;
if (data.GetLength() != desiredsize) return false;
// This is probably a raw page graphic, but do some checking to be sure
patch_t *foo;
@ -136,7 +136,7 @@ static bool CheckIfRaw(FileReader & data)
FImageSource *RawPageImage_TryCreate(FileReader & file, int lumpnum)
{
if (!CheckIfRaw(file)) return nullptr;
if (!CheckIfRaw(file, 64000)) return nullptr;
return new FRawPageTexture(lumpnum);
}

View file

@ -0,0 +1,82 @@
/*
** startscreentexture.cpp
** Texture class to create a texture from the start screen's imagé
**
**---------------------------------------------------------------------------
** Copyright 2004-2006 Randy Heit
** Copyright 2019 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 "files.h"
#include "gi.h"
#include "bitmap.h"
#include "textures.h"
#include "imagehelpers.h"
#include "image.h"
#include "startscreen.h"
//==========================================================================
//
//
//
//==========================================================================
class FStartScreenTexture : public FImageSource
{
FBitmap& info; // This must remain constant for the lifetime of this texture
public:
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;
}
};
//==========================================================================
//
//
//
//==========================================================================
FImageSource *CreateStartScreenTexture(FBitmap& srcdata)
{
return new FStartScreenTexture(srcdata);
}

View file

@ -0,0 +1,381 @@
/*
** startuptexture.cpp
** Texture class for Hexen's 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 "files.h"
#include "filesystem.h"
#include "bitmap.h"
#include "imagehelpers.h"
#include "image.h"
#define ST_NOTCH_WIDTH 16
#define ST_NOTCH_HEIGHT 23
#define ST_NETNOTCH_WIDTH 4
#define ST_NETNOTCH_HEIGHT 16
struct StrifeStartupInfo
{
char name[9];
uint8_t width, height;
};
static StrifeStartupInfo StrifeRawPics[] =
{
{ "STRTPA1", 32, 64},
{ "STRTPB1", 32, 64},
{ "STRTPC1", 32, 64},
{ "STRTPD1", 32, 64},
{ "STRTLZ1", 16, 16},
{ "STRTLZ2", 16, 16},
{ "STRTBOT", 48, 48}
};
// there is only one palette for all these images.
static uint8_t startuppalette8[16];
static uint32_t startuppalette32[16];
//==========================================================================
//
//
//
//==========================================================================
class FStartupTexture : public FImageSource
{
public:
FStartupTexture (int lumpnum);
TArray<uint8_t> CreatePalettedPixels(int conversion) override;
int CopyPixels(FBitmap *bmp, int conversion) override;
};
class FNotchTexture : public FImageSource
{
public:
FNotchTexture (int lumpnum, int width, int height);
TArray<uint8_t> CreatePalettedPixels(int conversion) override;
int CopyPixels(FBitmap *bmp, int conversion) override;
};
class FStrifeStartupTexture : public FImageSource
{
public:
FStrifeStartupTexture (int lumpnum, int w, int h);
TArray<uint8_t> CreatePalettedPixels(int conversion) override;
};
class FStrifeStartupBackground : public FImageSource
{
public:
FStrifeStartupBackground (int lumpnum);
TArray<uint8_t> CreatePalettedPixels(int conversion) override;
};
//==========================================================================
//
// Use the same function as raw textures to eliminate Doom patches
//
//==========================================================================
bool CheckIfRaw(FileReader & data, int desiredsize);
//==========================================================================
//
// loads all raw images for Hexen's and Strife's startup screens
//
//==========================================================================
FImageSource *StartupPageImage_TryCreate(FileReader & file, int lumpnum)
{
if (fileSystem.CheckFileName(lumpnum, "STARTUP"))
{
if (!CheckIfRaw(file, 153648)) return nullptr;
return new FStartupTexture(lumpnum);
}
if (fileSystem.CheckFileName(lumpnum, "NOTCH"))
{
if (!CheckIfRaw(file, ST_NOTCH_WIDTH * ST_NOTCH_HEIGHT / 2)) return nullptr;
return new FNotchTexture(lumpnum, ST_NOTCH_WIDTH, ST_NOTCH_HEIGHT);
}
if (fileSystem.CheckFileName(lumpnum, "NETNOTCH"))
{
if (!CheckIfRaw(file, ST_NETNOTCH_WIDTH * ST_NETNOTCH_HEIGHT / 2)) return nullptr;
return new FNotchTexture(lumpnum, ST_NETNOTCH_WIDTH, ST_NETNOTCH_HEIGHT);
}
if (fileSystem.CheckFileName(lumpnum, "STARTUP0"))
{
if (!CheckIfRaw(file, 64000)) return nullptr;
return new FStrifeStartupBackground(lumpnum);
}
for(auto& sst : StrifeRawPics)
{
if (fileSystem.CheckFileName(lumpnum, sst.name))
{
if (!CheckIfRaw(file, sst.width * sst.height)) return nullptr;
return new FStrifeStartupTexture(lumpnum, sst.width, sst.height);
}
}
return nullptr;
}
//==========================================================================
//
//
//
//==========================================================================
FStartupTexture::FStartupTexture (int lumpnum)
: FImageSource(lumpnum)
{
Width = 640;
Height = 480;
bUseGamePalette = false;
FileData lump = fileSystem.ReadFile (SourceLump);
const uint8_t *source = (const uint8_t *)lump.GetMem();
// Initialize the bitmap palette.
// the palette is static so that the notches can share it.
// Note that if the STARTUP image gets replaced, the notches will be all black unless they get replaced as well!
for (int i = 0; i < 16; ++i)
{
PalEntry pe;
pe.r = source[i * 3 + 0];
pe.g = source[i * 3 + 1];
pe.b = source[i * 3 + 2];
pe.a = 63;
// Convert from 6-bit per component to 8-bit per component.
pe.d= (pe.d << 2) | ((pe.d >> 4) & 0x03030303);
startuppalette8[i] = ColorMatcher.Pick(pe);
startuppalette32[i] = pe;
}
}
//==========================================================================
//
// PlanarToChunky
//
// Convert a 4-bpp planar image to chunky pixels.
//
//==========================================================================
template<class T>
void PlanarToChunky(T* dest, const uint8_t* src, const T* remap, int width, int height)
{
int y, x;
const uint8_t* src1, * src2, * src3, * src4;
size_t plane_size = width / 8 * height;
src1 = src;
src2 = src1 + plane_size;
src3 = src2 + plane_size;
src4 = src3 + plane_size;
for (y = height; y > 0; --y)
{
for (x = width; x > 0; x -= 8)
{
dest[0] = remap[((*src4 & 0x80) | ((*src3 & 0x80) >> 1) | ((*src2 & 0x80) >> 2) | ((*src1 & 0x80) >> 3)) >> 4];
dest[1] = remap[((*src4 & 0x40) >> 3) | ((*src3 & 0x40) >> 4) | ((*src2 & 0x40) >> 5) | ((*src1 & 0x40) >> 6)];
dest[2] = remap[(((*src4 & 0x20) << 2) | ((*src3 & 0x20) << 1) | ((*src2 & 0x20)) | ((*src1 & 0x20) >> 1)) >> 4];
dest[3] = remap[((*src4 & 0x10) >> 1) | ((*src3 & 0x10) >> 2) | ((*src2 & 0x10) >> 3) | ((*src1 & 0x10) >> 4)];
dest[4] = remap[(((*src4 & 0x08) << 4) | ((*src3 & 0x08) << 3) | ((*src2 & 0x08) << 2) | ((*src1 & 0x08) << 1)) >> 4];
dest[5] = remap[((*src4 & 0x04) << 1) | ((*src3 & 0x04)) | ((*src2 & 0x04) >> 1) | ((*src1 & 0x04) >> 2)];
dest[6] = remap[(((*src4 & 0x02) << 6) | ((*src3 & 0x02) << 5) | ((*src2 & 0x02) << 4) | ((*src1 & 0x02) << 3)) >> 4];
dest[7] = remap[((*src4 & 0x01) << 3) | ((*src3 & 0x01) << 2) | ((*src2 & 0x01) << 1) | ((*src1 & 0x01))];
dest += 8;
src1 += 1;
src2 += 1;
src3 += 1;
src4 += 1;
}
}
}
//==========================================================================
//
//
//
//==========================================================================
TArray<uint8_t> FStartupTexture::CreatePalettedPixels(int conversion)
{
FileData lump = fileSystem.ReadFile (SourceLump);
const uint8_t *source = (const uint8_t *)lump.GetMem();
const uint8_t *remap = ImageHelpers::GetRemap(conversion == luminance);
TArray<uint8_t> Work(Width*Height, true);
TArray<uint8_t> Pixels(Width*Height, true);
PlanarToChunky(Work.Data(), source + 48, startuppalette8, Width, Height);
ImageHelpers::FlipNonSquareBlockRemap(Pixels.Data(), Work.Data(), Width, Height, Width, remap);
return Pixels;
}
//==========================================================================
//
//
//
//==========================================================================
int FStartupTexture::CopyPixels(FBitmap *bmp, int conversion)
{
FileData lump = fileSystem.ReadFile (SourceLump);
const uint8_t *source = (const uint8_t *)lump.GetMem();
PlanarToChunky((uint32_t*)bmp->GetPixels(), source + 48, startuppalette32, Width, Height);
return 0;
}
//==========================================================================
//
//
//
//==========================================================================
FNotchTexture::FNotchTexture (int lumpnum, int width, int height)
: FImageSource(lumpnum)
{
Width = width;
Height = height;
bUseGamePalette = false;
}
//==========================================================================
//
//
//
//==========================================================================
TArray<uint8_t> FNotchTexture::CreatePalettedPixels(int conversion)
{
FileData lump = fileSystem.ReadFile (SourceLump);
const uint8_t *source = (const uint8_t *)lump.GetMem();
const uint8_t *remap = ImageHelpers::GetRemap(conversion == luminance);
TArray<uint8_t> Work(Width*Height, true);
TArray<uint8_t> Pixels(Width*Height, true);
for(int i=0; i * Width * Height / 2; i++)
{
Work[i * 2] = startuppalette8[source[i] >> 4];
Work[i * 2 + 1] = startuppalette8[source[i] & 15];
}
ImageHelpers::FlipNonSquareBlockRemap(Pixels.Data(), Work.Data(), Width, Height, Width, remap);
return Pixels;
}
//==========================================================================
//
//
//
//==========================================================================
int FNotchTexture::CopyPixels(FBitmap *bmp, int conversion)
{
FileData lump = fileSystem.ReadFile (SourceLump);
const uint8_t *source = (const uint8_t *)lump.GetMem();
auto Work = (uint32_t*)bmp->GetPixels();
for(int i = 0; i < Width * Height / 2; i++)
{
Work[i * 2] = startuppalette32[source[i] >> 4];
Work[i * 2 + 1] = startuppalette32[source[i] & 15];
}
return 0;
}
//==========================================================================
//
//
//
//==========================================================================
FStrifeStartupTexture::FStrifeStartupTexture (int lumpnum, int w, int h)
: FImageSource(lumpnum)
{
Width = w;
Height = h;
}
//==========================================================================
//
//
//
//==========================================================================
TArray<uint8_t> FStrifeStartupTexture::CreatePalettedPixels(int conversion)
{
FileData lump = fileSystem.ReadFile (SourceLump);
const uint8_t *source = (const uint8_t *)lump.GetMem();
TArray<uint8_t> Pixels(Width*Height, true);
const uint8_t *remap = ImageHelpers::GetRemap(conversion == luminance);
ImageHelpers::FlipNonSquareBlockRemap(Pixels.Data(), source, Width, Height, Width, remap);
return Pixels;
}
//==========================================================================
//
//
//
//==========================================================================
FStrifeStartupBackground::FStrifeStartupBackground (int lumpnum)
: FImageSource(lumpnum)
{
Width = 320;
Height = 200;
}
//==========================================================================
//
// this image is very messy but let's prepare it just like Strife does
// so that the screen can be replaced with a whole image.
//
//==========================================================================
TArray<uint8_t> FStrifeStartupBackground::CreatePalettedPixels(int conversion)
{
TArray<uint8_t> source(64000, true);
memset(source.Data(), 0xF0, 64000);
auto lumpr = fileSystem.OpenFileReader(SourceLump);
lumpr.Seek(57 * 320, FileReader::SeekSet);
lumpr.Read(source.Data() + 41 * 320, 95 * 320);
TArray<uint8_t> Pixels(Width*Height, true);
const uint8_t *remap = ImageHelpers::GetRemap(conversion == luminance);
ImageHelpers::FlipNonSquareBlockRemap(Pixels.Data(), source.Data(), Width, Height, Width, remap);
return Pixels;
}

View file

@ -311,7 +311,7 @@ void FGameTexture::SetupSpriteData()
if (i == 1 && ShouldExpandSprite())
{
spi.mTrimResult = Base->TrimBorders(spi.trim); // get the trim size before adding the empty frame
spi.mTrimResult = Base->TrimBorders(spi.trim) && !GetNoTrimming(); // get the trim size before adding the empty frame
spi.spriteWidth += 2;
spi.spriteHeight += 2;
}

View file

@ -335,6 +335,7 @@ FImageSource *FlatImage_TryCreate(FileReader &, int lumpnum);
FImageSource *PatchImage_TryCreate(FileReader &, int lumpnum);
FImageSource *EmptyImage_TryCreate(FileReader &, int lumpnum);
FImageSource *AutomapImage_TryCreate(FileReader &, int lumpnum);
FImageSource *StartupPageImage_TryCreate(FileReader &, int lumpnum);
// Examines the lump contents to decide what type of texture to create,
@ -350,6 +351,7 @@ FImageSource * FImageSource::GetImage(int lumpnum, bool isflat)
{ StbImage_TryCreate, false },
{ TGAImage_TryCreate, false },
{ AnmImage_TryCreate, false },
{ StartupPageImage_TryCreate, false },
{ RawPageImage_TryCreate, false },
{ FlatImage_TryCreate, true }, // flat detection is not reliable, so only consider this for real flats.
{ PatchImage_TryCreate, false },

View file

@ -1186,19 +1186,19 @@ FGameTexture *CreateShaderTexture(bool, bool);
void InitBuildTiles();
FImageSource* CreateEmptyTexture();
void FTextureManager::Init(void (*progressFunc_)(), void (*checkForHacks)(BuildInfo&))
void FTextureManager::Init()
{
progressFunc = progressFunc_;
DeleteAll();
//if (BuildTileFiles.Size() == 0) CountBuildTiles ();
// Add all the static content
auto nulltex = MakeGameTexture(new FImageTexture(CreateEmptyTexture()), nullptr, ETextureType::Null);
AddGameTexture(nulltex);
// This is for binding to unused texture units, because accessing an unbound texture unit is undefined. It's a one pixel empty texture.
auto emptytex = MakeGameTexture(new FImageTexture(CreateEmptyTexture()), nullptr, ETextureType::Override);
emptytex->SetSize(1, 1);
AddGameTexture(emptytex);
AddGameTexture(emptytex);
// some special textures used in the game.
AddGameTexture(CreateShaderTexture(false, false));
AddGameTexture(CreateShaderTexture(false, true));
@ -1211,6 +1211,12 @@ void FTextureManager::Init(void (*progressFunc_)(), void (*checkForHacks)(BuildI
mt = MakeGameTexture(new AnimTexture(), "AnimTextureFrame2", ETextureType::Override);
mt->SetUpscaleFlag(false, true);
AddGameTexture(mt);
}
void FTextureManager::AddTextures(void (*progressFunc_)(), void (*checkForHacks)(BuildInfo&))
{
progressFunc = progressFunc_;
//if (BuildTileFiles.Size() == 0) CountBuildTiles ();
int wadcnt = fileSystem.GetNumWads();

View file

@ -123,7 +123,8 @@ public:
void LoadTextureX(int wadnum, FMultipatchTextureBuilder &build);
void AddTexturesForWad(int wadnum, FMultipatchTextureBuilder &build);
void Init(void (*progressFunc_)(), void (*checkForHacks)(BuildInfo &));
void Init();
void AddTextures(void (*progressFunc_)(), void (*checkForHacks)(BuildInfo&));
void DeleteAll();
void ReplaceTexture (FTextureID picnum, FGameTexture *newtexture, bool free);

View file

@ -238,6 +238,8 @@
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
# elif defined(_MSC_VER) && defined(_M_ARM)
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
# elif defined(_MSC_VER) && defined(_M_ARM64)
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
# elif defined(RAPIDJSON_DOXYGEN_RUNNING)
# define RAPIDJSON_ENDIAN
# else

View file

@ -408,6 +408,7 @@ public:
{
new(&Array[start + i]) T(std::move(item[i]));
}
item.Clear();
return start;
}

View file

@ -1463,7 +1463,7 @@ bool DoArbitrate (void *userdata)
data->playersdetected[0] |= 1 << netbuffer[1];
StartScreen->NetMessage ("Found %s (node %d, player %d)", GetPlayerName(netbuffer[1]).GetChars(),
I_NetMessage ("Found %s (node %d, player %d)", GetPlayerName(netbuffer[1]).GetChars(),
node, netbuffer[1]+1);
}
}
@ -1610,8 +1610,8 @@ bool D_ArbitrateNetStart (void)
data.gotsetup[0] = 0x80;
}
StartScreen->NetInit ("Exchanging game information", 1);
if (!StartScreen->NetLoop (DoArbitrate, &data))
I_NetInit ("Exchanging game information", 1);
if (!I_NetLoop (DoArbitrate, &data))
{
return false;
}
@ -1629,7 +1629,7 @@ bool D_ArbitrateNetStart (void)
fprintf (debugfile, "player %d is on node %d\n", i, nodeforplayer[i]);
}
}
StartScreen->NetDone();
I_NetDone();
return true;
}

View file

@ -80,6 +80,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "razefont.h"
#include "coreactor.h"
#include "wipe.h"
#include "findfile.h"
#include "version.h"
void LoadHexFont(const char* filename);
CVAR(Bool, autoloadlights, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Bool, autoloadbrightmaps, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
@ -918,9 +922,11 @@ void GetGames()
static void InitTextures()
{
TexMan.usefullnames = true;
TexMan.Init([]() {}, [](BuildInfo&) {});
StartScreen->Progress();
TexMan.Init();
TexMan.AddTextures([]() {}, [](BuildInfo&) {});
StartWindow->Progress();
mdinit();
TileFiles.Init();
@ -951,6 +957,13 @@ int RunGame()
{
GameStartupInfo.FgColor = 0xffffff;
auto wad = BaseFileSearch(ENGINERES_FILE, NULL, true, GameConfig);
if (wad == NULL)
{
I_FatalError("Cannot find " ENGINERES_FILE);
}
LoadHexFont(wad); // load hex font early so we have it during startup.
// Set up the console before anything else so that it can receive text.
C_InitConsole(1024, 768, true);
@ -1026,8 +1039,8 @@ int RunGame()
V_InitScreenSize();
V_InitScreen();
StartScreen = FStartupScreen::CreateInstance(8);
StartScreen->Progress();
StartWindow = FStartupScreen::CreateInstance(8, true);
StartWindow->Progress();
TArray<FString> addArt;
for (auto& grp : usedgroups)
@ -1062,18 +1075,18 @@ int RunGame()
GPalette.Init(MAXPALOOKUPS + 2, palindexmap); // one slot for each translation, plus a separate one for the base palettes and the internal one
gi->loadPalette();
BuildFogTable();
StartScreen->Progress();
StartWindow->Progress();
InitTextures();
StartScreen->Progress();
StartWindow->Progress();
I_InitSound();
StartScreen->Progress();
StartWindow->Progress();
Mus_InitMusic();
S_ParseSndInfo();
S_ParseReverbDef();
InitStatistics();
LoadScripts();
StartScreen->Progress();
StartWindow->Progress();
SetDefaultStrings();
Job_Init();
Local_Job_Init();
@ -1082,12 +1095,12 @@ int RunGame()
SetupGameButtons();
gameinfo.mBackButton = "engine/graphics/m_back.png";
StartScreen->Progress();
StartWindow->Progress();
engineInit();
GC::AddMarkerFunc(MarkMap);
gi->app_init();
StartScreen->Progress();
StartWindow->Progress();
G_ParseMapInfo();
ParseGLDefs();
ReplaceMusics(true);
@ -1095,7 +1108,7 @@ int RunGame()
SetDefaultMenuColors();
M_Init();
BuildGameMenus();
StartScreen->Progress();
StartWindow->Progress();
if (!(paletteloaded & PALETTE_MAIN))
I_FatalError("No palette found.");
@ -1104,6 +1117,10 @@ int RunGame()
DeleteStartupScreen();
V_Init2();
while (!screen->CompileNextShader())
{
// here we can do some visual updates later
}
twod->Begin(screen->GetWidth(), screen->GetHeight());
twod->End();
UpdateJoystickMenu(NULL);

View file

@ -139,6 +139,12 @@ CUSTOM_CVARD(Float, hud_scalefactor, 1, CVAR_ARCHIVE, "changes the hud scale")
else setViewport(hud_size);
}
CUSTOM_CVAR(Bool, hud_aspectscale, false, CVAR_ARCHIVE)
{
setViewport(hud_size);
}
// Note: The shift detection here should be part of the key event data, but that requires a lot more work. Ideally use a ShiftBinds mapping. For control through bound keys this should be fine, bunt not for use from the console.
CCMD(sizeup)
{

View file

@ -44,6 +44,7 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
#include "dukeactor.h"
#include "interpolate.h"
#include "razefont.h"
#include "startscreen.h"
BEGIN_DUKE_NS

View file

@ -48,6 +48,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
#include "st_start.h"
#include "i_interface.h"
#include "psky.h"
#include "startscreen.h"

View file

@ -143,6 +143,33 @@ enum EPrintLevel
PRINT_NOLOG = 2048, // Flag - do not print to log file
};
/*
// These are here to document the intrinsic methods and fields available on
// the built-in ZScript types
struct Vector2
{
Vector2(x, y);
double x, y;
native double Length();
native Vector2 Unit();
// The dot product of two vectors can be calculated like this:
// double d = a dot b;
}
struct Vector3
{
Vector3(x, y, z);
double x, y, z;
Vector2 xy; // Convenient access to the X and Y coordinates of a 3D vector
native double Length();
native Vector3 Unit();
// The dot product of two vectors can be calculated like this:
// double d = a dot b;
// The cross product of two vectors can be calculated like this:
// Vector3 d = a cross b;
}
*/
struct _ native // These are the global variables, the struct is only here to avoid extending the parser for this.
{
native readonly Array<class> AllClasses;
@ -216,7 +243,7 @@ struct MusPlayingInfo native
native int baseorder;
native bool loop;
native voidptr handle;
};
struct TexMan
@ -252,7 +279,7 @@ struct TexMan
ForceLookup = 128,
NoAlias = 256
};
enum ETexReplaceFlags
{
NOT_BOTTOM = 1,
@ -274,6 +301,47 @@ struct TexMan
native static bool UseGamePalette(TextureID tex);
}
/*
// Intrinsic TextureID methods
// This isn't really a class, and can be used as an integer
struct TextureID
{
native bool IsValid();
native bool IsNull();
native bool Exists();
native void SetInvalid();
native void SetNull();
}
// 32-bit RGBA color - each component is one byte, or 8-bit
// This isn't really a class, and can be used as an integer
struct Color
{
// Constructor - alpha channel is optional
Color(int alpha, int red, int green, int blue);
Color(int red, int green, int blue); // Alpha is 0 if omitted
int r; // Red
int g; // Green
int b; // Blue
int a; // Alpha
}
// Name - a string with an integer ID
struct Name
{
Name(Name name);
Name(String name);
}
// Sound ID - can be created by casting from a string (name from SNDINFO) or an
// integer (sound ID as integer).
struct Sound
{
Sound(String soundName);
Sound(int id);
}
*/
enum EScaleMode
{
FSMode_None = 0,
@ -475,7 +543,7 @@ struct Font native
CR_TEAL,
NUM_TEXT_COLORS
};
const TEXTCOLOR_BRICK = "\034A";
const TEXTCOLOR_TAN = "\034B";
const TEXTCOLOR_GRAY = "\034C";
@ -509,7 +577,8 @@ struct Font native
const TEXTCOLOR_CHAT = "\034*";
const TEXTCOLOR_TEAMCHAT = "\034!";
// native Font(const String name); // String/name to font casts
// native Font(const Name name);
native int GetCharWidth(int code);
native int StringWidth(String code);
@ -591,14 +660,64 @@ class Object native
private native static void BuiltinRandomSeed(voidptr rng, int seed);
private native static Class<Object> BuiltinNameToClass(Name nm, Class<Object> filter);
private native static Object BuiltinClassCast(Object inptr, Class<Object> test);
native static uint MSTime();
deprecated("4.8", "Use MSTimeF instead") native static uint MSTime();
native static double MSTimeF();
native vararg static void ThrowAbortException(String fmt, ...);
native virtualscope void Destroy();
// This does not call into the native method of the same name to avoid problems with objects that get garbage collected late on shutdown.
virtual virtualscope void OnDestroy() {}
//
// Object intrinsics
// Every ZScript "class" inherits from Object, and so inherits these methods as well
// clearscope bool IsAbstract(); // Query whether or not the class of this object is abstract
// clearscope Object GetParentClass(); // Get the parent class of this object
// clearscope Name GetClassName(); // Get the name of this object's class
// clearscope Class<Object> GetClass(); // Get the object's class
// clearscope Object new(class<Object> type); // Create a new object with this class. This is only valid for thinkers and plain objects, except menus. For actors, use Actor.Spawn();
//
//
// Intrinsic random number generation functions. Note that the square
// bracket syntax for specifying an RNG ID is only available for these
// functions.
// clearscope void SetRandomSeed[Name rngId = 'None'](int seed); // Set the seed for the given RNG.
// clearscope int Random[Name rngId = 'None'](int min, int max); // Use the given RNG to generate a random integer number in the range (min, max) inclusive.
// clearscope int Random2[Name rngId = 'None'](int mask); // Use the given RNG to generate a random integer number, and do a "union" (bitwise AND, AKA &) operation with the bits in the mask integer.
// clearscope double FRandom[Name rngId = 'None'](double min, double max); // Use the given RNG to generate a random real number in the range (min, max) inclusive.
// clearscope int RandomPick[Name rngId = 'None'](int choices...); // Use the given RNG to generate a random integer from the given choices.
// clearscope double FRandomPick[Name rngId = 'None'](double choices...); // Use the given RNG to generate a random real number from the given choices.
//
//
// Intrinsic math functions - the argument and return types for these
// functions depend on the arguments given. Other than that, they work the
// same way similarly-named functions in other programming languages work.
// Note that trigonometric functions work with degrees instead of radians
// clearscope T abs(T x);
// clearscope T atan2(T y, T x); // NOTE: Returns a value in degrees instead of radians
// clearscope T vectorangle(T x, T y); // Same as Atan2 with the arguments in a different order
// clearscope T min(T x...);
// clearscope T max(T x...);
// clearscope T clamp(T x, T min, T max);
//
// These math functions only work with doubles - they are defined in FxFlops
// clearscope double exp(double x);
// clearscope double log(double x);
// clearscope double log10(double x);
// clearscope double sqrt(double x);
// clearscope double ceil(double x);
// clearscope double floor(double x);
// clearscope double acos(double x);
// clearscope double asin(double x);
// clearscope double atan(double x);
// clearscope double cos(double x);
// clearscope double sin(double x);
// clearscope double tan(double x);
// clearscope double cosh(double x);
// clearscope double sinh(double x);
// clearscope double tanh(double x);
// clearscope double round(double x);
}
class BrokenLines : Object native version("2.4")
@ -665,11 +784,15 @@ enum EmptyTokenType
}
// Although String is a builtin type, this is a convenient way to attach methods to it.
// All of these methods are available on strings
struct StringStruct native
{
native static vararg String Format(String fmt, ...);
native vararg void AppendFormat(String fmt, ...);
// native int Length(); // Intrinsic
// native bool operator==(String other); // Equality comparison
// native bool operator~==(String other); // Case-insensitive equality comparison
// native String operator..(String other); // Concatenate with another String
native void Replace(String pattern, String replacement);
native String Left(int len) const;
native String Mid(int pos = 0, int len = 2147483647) const;

View file

@ -4,7 +4,7 @@
struct DynArray_I8 native
{
native readonly uint Size;
native void Copy(DynArray_I8 other);
native void Move(DynArray_I8 other);
native void Append (DynArray_I8 other);
@ -65,7 +65,7 @@ struct DynArray_I32 native
struct DynArray_F32 native
{
native readonly uint Size;
native void Copy(DynArray_F32 other);
native void Move(DynArray_F32 other);
native void Append (DynArray_F32 other);
@ -85,7 +85,7 @@ struct DynArray_F32 native
struct DynArray_F64 native
{
native readonly uint Size;
native void Copy(DynArray_F64 other);
native void Move(DynArray_F64 other);
native void Append (DynArray_F64 other);
@ -105,7 +105,7 @@ struct DynArray_F64 native
struct DynArray_Ptr native
{
native readonly uint Size;
native void Copy(DynArray_Ptr other);
native void Move(DynArray_Ptr other);
native void Append (DynArray_Ptr other);
@ -125,7 +125,7 @@ struct DynArray_Ptr native
struct DynArray_Obj native
{
native readonly uint Size;
native void Copy(DynArray_Obj other);
native void Move(DynArray_Obj other);
native void Append (DynArray_Obj other);

View file

@ -193,7 +193,7 @@ class OptionMenu : Menu
mDesc.mScrollPos += 2;
VisBottom += 2;
}
else
else if (VisBottom < mDesc.mItems.Size()-1)
{
mDesc.mScrollPos++;
VisBottom++;
@ -201,6 +201,30 @@ class OptionMenu : Menu
}
return true;
}
else if (ev.type == UIEvent.Type_Char)
{
int key = String.CharLower(ev.keyChar);
int itemsNumber = mDesc.mItems.Size();
int direction = ev.IsAlt ? -1 : 1;
for (int i = 0; i < itemsNumber; ++i)
{
int index = (mDesc.mSelectedItem + direction * (i + 1) + itemsNumber) % itemsNumber;
if (!mDesc.mItems[index].Selectable()) continue;
String label = StringTable.Localize(mDesc.mItems[index].mLabel);
int firstLabelCharacter = String.CharLower(label.GetNextCodePoint(0));
if (firstLabelCharacter == key)
{
mDesc.mSelectedItem = index;
break;
}
}
if (mDesc.mSelectedItem <= mDesc.mScrollTop + mDesc.mScrollPos
|| mDesc.mSelectedItem > VisBottom)
{
int pagesize = VisBottom - mDesc.mScrollPos - mDesc.mScrollTop;
mDesc.mScrollPos = clamp(mDesc.mSelectedItem - mDesc.mScrollTop - 1, 0, mDesc.mItems.size() - pagesize - 1);
}
}
return Super.OnUIEvent(ev);
}