From 3b955b7c947e8656c9b0bd3dea81f51ff2c4668d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 11 Jan 2020 17:05:25 +0100 Subject: [PATCH] - added support for ZDoom-style GAMEINFO. --- source/common/gamecontrol.cpp | 16 +- source/common/gamecontrol.h | 14 ++ source/common/initfs.cpp | 213 ++++++++++++++++++++-- source/common/utility/m_argv.h | 1 + source/platform/posix/cocoa/st_console.mm | 10 +- source/platform/win32/i_main.cpp | 51 ++++++ 6 files changed, 285 insertions(+), 20 deletions(-) diff --git a/source/common/gamecontrol.cpp b/source/common/gamecontrol.cpp index c647a9016..842af461c 100644 --- a/source/common/gamecontrol.cpp +++ b/source/common/gamecontrol.cpp @@ -51,6 +51,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "st_start.h" #include "s_music.h" #include "i_video.h" +#include "v_text.h" +#include "resourcefile.h" #include "c_dispatch.h" #include "glbackend/glbackend.h" #ifndef NETCODE_DISABLE @@ -62,6 +64,7 @@ MapRecord *currentLevel; // level that is currently played. (The real level, not MapRecord* lastLevel; // Same here, for the last level. MapRecord userMapRecord; // stand-in for the user map. +FStartupInfo RazeStartupInfo; FMemArena dump; // this is for memory blocks than cannot be deallocated without some huge effort. Put them in here so that they do not register on shutdown. FString progdir; @@ -70,7 +73,9 @@ void C_CON_SetAliases(); InputState inputState; void SetClipshapes(); int ShowStartupWindow(TArray &); +FString GetGameFronUserFiles(); void InitFileSystem(TArray&); +void I_SetWindowTitle(const char* caption); bool gHaveNetworking; bool AppActive; @@ -86,6 +91,7 @@ CVAR(Bool, disableautoload, false, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALC //CVAR(Bool, autoloadbrightmaps, false, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG) // hopefully this is an option for later //CVAR(Bool, autoloadlights, false, CVAR_ARCHIVE | CVAR_NOINITCALL | CVAR_GLOBALCONFIG) + //========================================================================== // // @@ -443,6 +449,12 @@ static TArray SetupGame() // If the user has specified a file name, let's see if we know it. // + FString game = GetGameFronUserFiles(); + if (userConfig.gamegrp.IsEmpty()) + { + userConfig.gamegrp = game; + } + if (userConfig.gamegrp.Len()) { FString gamegrplower = "/" + userConfig.gamegrp.MakeLower(); @@ -461,7 +473,6 @@ static TArray SetupGame() } } - if (groupno == -1) { int pick = 0; @@ -510,6 +521,9 @@ static TArray SetupGame() if (groupno == -1) return TArray(); auto& group = groups[groupno]; + if (RazeStartupInfo.Name.IsNotEmpty()) I_SetWindowTitle(RazeStartupInfo.Name); + else I_SetWindowTitle(group.FileInfo.name); + // Now filter out the data we actually need and delete the rest. usedgroups.Push(group); diff --git a/source/common/gamecontrol.h b/source/common/gamecontrol.h index e242a0c17..b9b662b22 100644 --- a/source/common/gamecontrol.h +++ b/source/common/gamecontrol.h @@ -157,3 +157,17 @@ void S_SetSoundPaused(int state); void G_HandleMemErr(int32_t lineNum, const char* fileName, const char* funcName); void G_FatalEngineError(void); + + +struct FStartupInfo +{ + FString Name; + uint32_t FgColor; // Foreground color for title banner + uint32_t BkColor; // Background color for title banner + //FString Song; + //int Type; + //int LoadLights = -1; + //int LoadBrightmaps = -1; +}; + +extern FStartupInfo RazeStartupInfo; \ No newline at end of file diff --git a/source/common/initfs.cpp b/source/common/initfs.cpp index 32664b920..cbe2973bb 100644 --- a/source/common/initfs.cpp +++ b/source/common/initfs.cpp @@ -42,6 +42,9 @@ #include "printf.h" #include "m_argv.h" #include "version.h" +#include "sc_man.h" +#include "v_video.h" +#include "v_text.h" #include "../platform/win32/i_findfile.h" // This is a temporary direct path. Needs to be fixed when stuff gets cleaned up. #ifndef PATH_MAX @@ -268,7 +271,7 @@ static void D_AddDirectory (TArray &wadfiles, const char *dir) { skindir[stuffstart++] = '/'; int savedstart = stuffstart; - static const char* validexts[] = { "*.grp", "*.zip", "*.pk3", "*.pk4", "*.7z", "*.pk7", "*.dat" }; + static const char* validexts[] = { "*.grp", "*.zip", "*.pk3", "*.pk4", "*.7z", "*.pk7", "*.dat", "*.rff" }; for (auto ext : validexts) { stuffstart = savedstart; @@ -290,6 +293,186 @@ static void D_AddDirectory (TArray &wadfiles, const char *dir) } } +//========================================================================== +// +// +// +//========================================================================== + +static FString ParseGameInfo(TArray& pwads, const char* fn, const char* data, int size) +{ + FScanner sc; + FString iwad; + int pos = 0; + + const char* lastSlash = strrchr(fn, '/'); + + sc.OpenMem("GAMEINFO", data, size); + sc.SetCMode(true); + while (sc.GetToken()) + { + sc.TokenMustBe(TK_Identifier); + FString nextKey = sc.String; + sc.MustGetToken('='); + if (!nextKey.CompareNoCase("GAME")) + { + sc.MustGetString(); + iwad = sc.String; + } + else if (!nextKey.CompareNoCase("LOAD")) + { + do + { + sc.MustGetString(); + + // Try looking for the wad in the same directory as the .wad + // before looking for it in the current directory. + + FString checkpath; + if (lastSlash != NULL) + { + checkpath = FString(fn, (lastSlash - fn) + 1); + checkpath += sc.String; + } + else + { + checkpath = sc.String; + } + if (!FileExists(checkpath)) + { + pos += D_AddFile(pwads, sc.String, true, pos); + } + else + { + pos += D_AddFile(pwads, checkpath, true, pos); + } + } while (sc.CheckToken(',')); + } + else if (!nextKey.CompareNoCase("STARTUPTITLE")) + { + sc.MustGetString(); + RazeStartupInfo.Name = sc.String; + } + else if (!nextKey.CompareNoCase("STARTUPCOLORS")) + { + sc.MustGetString(); + RazeStartupInfo.FgColor = V_GetColor(NULL, sc); + sc.MustGetStringName(","); + sc.MustGetString(); + RazeStartupInfo.BkColor = V_GetColor(NULL, sc); + } + else + { + // Silently ignore unknown properties + do + { + sc.MustGetAnyToken(); + } while (sc.CheckToken(',')); + } + } + return iwad; +} +//========================================================================== +// +// +// +//========================================================================== + +static FString CheckGameInfo(TArray& pwads) +{ + // scan the list of WADs backwards to find the last one that contains a GAMEINFO lump + for (int i = pwads.Size() - 1; i >= 0; i--) + { + bool isdir = false; + FResourceFile* resfile; + const char* filename = pwads[i]; + + // Does this exist? If so, is it a directory? + if (!DirEntryExists(pwads[i], &isdir)) + { + Printf(TEXTCOLOR_RED "Could not find %s\n", filename); + continue; + } + + if (!isdir) + { + FileReader fr; + if (!fr.OpenFile(filename)) + { + // Didn't find file + continue; + } + resfile = FResourceFile::OpenResourceFile(filename, fr, true); + } + else + resfile = FResourceFile::OpenDirectory(filename, true); + + FName gameinfo = "GAMEINFO.TXT"; + if (resfile != NULL) + { + uint32_t cnt = resfile->LumpCount(); + for (int i = cnt - 1; i >= 0; i--) + { + FResourceLump* lmp = resfile->GetLump(i); + + if (lmp->LumpName[0] == gameinfo) + { + // Found one! + FString iwad = ParseGameInfo(pwads, resfile->FileName, (const char*)lmp->Lock(), lmp->LumpSize); + delete resfile; + return iwad; + } + } + delete resfile; + } + } + return ""; +} + +//========================================================================== +// +// +// +//========================================================================== + +FString GetGameFronUserFiles() +{ + TArray Files; + + if (userConfig.AddFilesPre) for (auto& file : *userConfig.AddFilesPre) + { + D_AddFile(Files, file); + } + if (userConfig.AddFiles) + { + for (auto& file : *userConfig.AddFiles) + { + D_AddFile(Files, file); + } + + // Finally, if the last entry in the chain is a directory, it's being considered the mod directory, and all GRPs inside need to be loaded, too. + if (userConfig.AddFiles->NumArgs() > 0) + { + auto fn = (*userConfig.AddFiles)[userConfig.AddFiles->NumArgs() - 1]; + bool isdir = false; + if (DirEntryExists(fn, &isdir) && isdir) + { + // Insert the GRPs before this entry itself. + FString lastfn; + Files.Pop(lastfn); + D_AddDirectory(Files, fn); + Files.Push(lastfn); + } + } + } + return CheckGameInfo(Files); +} + +//========================================================================== +// +// +// +//========================================================================== void InitFileSystem(TArray& groups) { @@ -338,6 +521,20 @@ void InitFileSystem(TArray& groups) i--; } + const char* key; + const char* value; + if (GameConfig->SetSection("global.Autoload")) + { + while (GameConfig->NextInSection(key, value)) + { + if (stricmp(key, "Path") == 0) + { + FString nice = NicePath(value); + D_AddFile(Files, nice); + } + } + } + if (!insertdirectoriesafter && userConfig.AddFilesPre) for (auto& file : *userConfig.AddFilesPre) { D_AddFile(Files, file); @@ -364,19 +561,7 @@ void InitFileSystem(TArray& groups) } } } - const char* key; - const char* value; - if (GameConfig->SetSection("global.Autoload")) - { - while (GameConfig->NextInSection(key, value)) - { - if (stricmp(key, "Path") == 0) - { - FString nice = NicePath(value); - D_AddFile(Files, nice); - } - } - } + TArray todelete; for (auto& g : groups) diff --git a/source/common/utility/m_argv.h b/source/common/utility/m_argv.h index 8684b1fd1..953769703 100644 --- a/source/common/utility/m_argv.h +++ b/source/common/utility/m_argv.h @@ -102,6 +102,7 @@ public: FString TakeValue(const char *check); int NumArgs() const; void FlushArgs(); + TArray& Array() { return Argv; } private: TArray Argv; diff --git a/source/platform/posix/cocoa/st_console.mm b/source/platform/posix/cocoa/st_console.mm index ef42c1b97..fa55e36cb 100644 --- a/source/platform/posix/cocoa/st_console.mm +++ b/source/platform/posix/cocoa/st_console.mm @@ -336,17 +336,17 @@ void FConsoleWindow::SetTitleText() // It's used in graphical startup screen, with Hexen style in particular // Native OS X backend doesn't implement this yet - if (DoomStartupInfo.FgColor == DoomStartupInfo.BkColor) + if (RazeStartupInfo.FgColor == RazeStartupInfo.BkColor) { - DoomStartupInfo.FgColor = ~DoomStartupInfo.FgColor; + RazeStartupInfo.FgColor = ~RazeStartupInfo.FgColor; } NSTextField* titleText = [[NSTextField alloc] initWithFrame:titleTextRect]; - [titleText setStringValue:[NSString stringWithCString:DoomStartupInfo.Name + [titleText setStringValue:[NSString stringWithCString:RazeStartupInfo.Name encoding:NSISOLatin1StringEncoding]]; [titleText setAlignment:NSCenterTextAlignment]; - [titleText setTextColor:RGB(DoomStartupInfo.FgColor)]; - [titleText setBackgroundColor:RGB(DoomStartupInfo.BkColor)]; + [titleText setTextColor:RGB(RazeStartupInfo.FgColor)]; + [titleText setBackgroundColor:RGB(RazeStartupInfo.BkColor)]; [titleText setFont:[NSFont fontWithName:@"Trebuchet MS Bold" size:18.0f]]; [titleText setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin]; [titleText setSelectable:NO]; diff --git a/source/platform/win32/i_main.cpp b/source/platform/win32/i_main.cpp index ac1cb204a..face4d06e 100644 --- a/source/platform/win32/i_main.cpp +++ b/source/platform/win32/i_main.cpp @@ -426,6 +426,57 @@ LRESULT CALLBACK LConProc (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) } return 0; + case WM_DRAWITEM: + // Draw title banner. + if (wParam == IDC_STATIC_TITLE && RazeStartupInfo.Name.IsNotEmpty()) + { + const PalEntry *c; + + // Draw the game title strip at the top of the window. + auto drawitem = (LPDRAWITEMSTRUCT)lParam; + SIZE size; + + // Draw the background. + auto rect = drawitem->rcItem; + rect.bottom -= 1; + c = (const PalEntry *)&RazeStartupInfo.BkColor; + auto 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 = RazeStartupInfo.Name.WideString(); + GetTextExtentPoint32W (drawitem->hDC, widename.c_str(), (int)widename.length(), &size); + + // Draw the title. + c = (const PalEntry *)&RazeStartupInfo.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; + auto drawitem = (LPDRAWITEMSTRUCT)lParam; + + // This background color should match the edit control's. + auto 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)