- rewrite of the IWAD loading mechanism.

The old code went through a list of predefined file names and looked each of them up in a list of predefined directories until it found a match. This made it nearly impossible to add custom IWAD support because the list of valid file names could not be extended.
This has now been switched around to run a scan for matching files on each given directory. With this approach it can look for *.iwad and *.ipk3 as IWAD extensions as well and read an IWADINFO out of these files that can be added to the internal list of IWADs, making it finally possible to define custom IWADs without having to add them to the internal list.

(This isn't fully tested yet so some errors may still occur.)
This commit is contained in:
Christoph Oelckers 2017-08-19 19:30:48 +02:00
parent 2f5535dbca
commit cdff5bdc08
7 changed files with 474 additions and 251 deletions

View file

@ -184,6 +184,25 @@ bool FileExists (const char *filename)
return stat(filename, &buff) == 0 && !(buff.st_mode & S_IFDIR);
}
//==========================================================================
//
// DirExists
//
// Returns true if the given path exists and is a directory.
//
//==========================================================================
bool DirExists(const char *filename)
{
struct stat buff;
// [RH] Empty filenames are never there
if (filename == NULL || *filename == 0)
return false;
return stat(filename, &buff) == 0 && (buff.st_mode & S_IFDIR);
}
//==========================================================================
//
// DirEntryExists

View file

@ -20,6 +20,7 @@
int Q_filelength (FILE *f);
bool FileExists (const char *filename);
bool DirExists(const char *filename);
bool DirEntryExists (const char *pathname);
extern FString progdir;

View file

@ -48,80 +48,38 @@
#include "gameconfigfile.h"
#include "resourcefiles/resourcefile.h"
#include "version.h"
#include "doomerrors.h"
#include "v_text.h"
CVAR (Bool, queryiwad, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
CVAR (String, defaultiwad, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
//==========================================================================
//
// Clear check list
//
//==========================================================================
void FIWadManager::ClearChecks()
{
mLumpsFound.Resize(mIWads.Size());
for(unsigned i=0;i<mLumpsFound.Size(); i++)
{
mLumpsFound[i] = 0;
}
}
//==========================================================================
//
// Check one lump
//
//==========================================================================
void FIWadManager::CheckLumpName(const char *name)
{
for(unsigned i=0; i< mIWads.Size(); i++)
{
for(unsigned j=0; j < mIWads[i].Lumps.Size(); j++)
{
if (!mIWads[i].Lumps[j].CompareNoCase(name))
{
mLumpsFound[i] |= (1<<j);
}
}
}
}
//==========================================================================
//
// Returns check result
//
//==========================================================================
int FIWadManager::GetIWadInfo()
{
for(unsigned i=0; i< mIWads.Size(); i++)
{
if (mLumpsFound[i] == (1 << mIWads[i].Lumps.Size()) - 1)
{
return i;
}
}
return -1;
}
//==========================================================================
//
// Parses IWAD definitions
//
//==========================================================================
void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize)
void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize, FIWADInfo *result)
{
FScanner sc;
int numblocks = 0;
sc.OpenMem("IWADINFO", data, datasize);
while (sc.GetString())
{
if (sc.Compare("IWAD"))
{
FIWADInfo *iwad = &mIWads[mIWads.Reserve(1)];
numblocks++;
if (result && numblocks > 1)
{
sc.ScriptMessage("Multiple IWAD records ignored");
// Skip the rest.
break;
}
FIWADInfo *iwad = result ? result : &mIWadInfos[mIWadInfos.Reserve(1)];
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
@ -138,6 +96,17 @@ void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize)
sc.MustGetString();
iwad->Autoname = sc.String;
}
else if (sc.Compare("IWadname"))
{
sc.MustGetStringName("=");
sc.MustGetString();
iwad->IWadname = sc.String;
if (sc.CheckString(","))
{
sc.MustGetNumber();
iwad->prio = sc.Number;
}
}
else if (sc.Compare("Config"))
{
sc.MustGetStringName("=");
@ -225,7 +194,7 @@ void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize)
iwad->MapInfo = "mapinfo/mindefaults.txt";
}
}
else if (sc.Compare("NAMES"))
else if (result == nullptr && sc.Compare("NAMES"))
{
sc.MustGetStringName("{");
mIWadNames.Push(FString());
@ -247,6 +216,19 @@ void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize)
#endif
}
}
else if (result == nullptr && sc.Compare("ORDER"))
{
sc.MustGetStringName("{");
while (!sc.CheckString("}"))
{
sc.MustGetString();
mOrderNames.Push(sc.String);
}
}
else
{
sc.ScriptError("Unknown keyword '%s'", sc.String);
}
}
}
@ -256,7 +238,7 @@ void FIWadManager::ParseIWadInfo(const char *fn, const char *data, int datasize)
//
//==========================================================================
void FIWadManager::ParseIWadInfos(const char *fn)
FIWadManager::FIWadManager(const char *fn)
{
FResourceFile *resfile = FResourceFile::OpenResourceFile(fn, NULL, true);
if (resfile != NULL)
@ -275,7 +257,7 @@ void FIWadManager::ParseIWadInfos(const char *fn)
}
delete resfile;
}
if (mIWadNames.Size() == 0 || mIWads.Size() == 0)
if (mIWadNames.Size() == 0 || mIWadInfos.Size() == 0)
{
I_FatalError("No IWAD definitions found");
}
@ -293,9 +275,25 @@ int FIWadManager::ScanIWAD (const char *iwad)
{
FResourceFile *iwadfile = FResourceFile::OpenResourceFile(iwad, NULL, true);
mLumpsFound.Resize(mIWadInfos.Size());
auto CheckLumpName = [=](const char *name)
{
for (unsigned i = 0; i< mIWadInfos.Size(); i++)
{
for (unsigned j = 0; j < mIWadInfos[i].Lumps.Size(); j++)
{
if (!mIWadInfos[i].Lumps[j].CompareNoCase(name))
{
mLumpsFound[i] |= (1 << j);
}
}
}
};
if (iwadfile != NULL)
{
ClearChecks();
memset(&mLumpsFound[0], 0, mLumpsFound.Size() * sizeof(mLumpsFound[0]));
for(uint32_t ii = 0; ii < iwadfile->LumpCount(); ii++)
{
FResourceLump *lump = iwadfile->GetLump(ii);
@ -312,52 +310,170 @@ int FIWadManager::ScanIWAD (const char *iwad)
}
delete iwadfile;
}
return GetIWadInfo();
for (unsigned i = 0; i< mIWadInfos.Size(); i++)
{
if (mLumpsFound[i] == (1 << mIWadInfos[i].Lumps.Size()) - 1)
{
DPrintf(DMSG_NOTIFY, "Identified %s as %s\n", iwad, mIWadInfos[i].Name.GetChars());
return i;
}
}
return -1;
}
//==========================================================================
//
// CheckIWAD
// Look for IWAD definition lump
//
// Tries to find an IWAD from a set of known IWAD names, and checks the
// contents of each one found to determine which game it belongs to.
// Returns the number of new wads found in this pass (does not count wads
// found from a previous call).
//
//==========================================================================
int FIWadManager::CheckIWAD (const char *doomwaddir, WadStuff *wads)
int FIWadManager::CheckIWADInfo(const char *fn)
{
const char *slash;
int numfound;
numfound = 0;
slash = (doomwaddir[0] && doomwaddir[strlen (doomwaddir)-1] != '/') ? "/" : "";
// Search for a pre-defined IWAD
for (unsigned i=0; i< mIWadNames.Size(); i++)
FResourceFile *resfile = FResourceFile::OpenResourceFile(fn, NULL, true);
if (resfile != NULL)
{
if (mIWadNames[i].IsNotEmpty() && wads[i].Path.IsEmpty())
uint32_t cnt = resfile->LumpCount();
for (int i = cnt - 1; i >= 0; i--)
{
FString iwad;
iwad.Format ("%s%s%s", doomwaddir, slash, mIWadNames[i].GetChars());
FixPathSeperator (iwad);
if (FileExists (iwad))
FResourceLump *lmp = resfile->GetLump(i);
if (lmp->Namespace == ns_global && !stricmp(lmp->Name, "IWADINFO"))
{
wads[i].Type = ScanIWAD (iwad);
if (wads[i].Type != -1)
// Found one!
try
{
wads[i].Path = iwad;
wads[i].Name = mIWads[wads[i].Type].Name;
numfound++;
FIWADInfo result;
ParseIWadInfo(resfile->Filename, (const char*)lmp->CacheLump(), lmp->LumpSize, &result);
delete resfile;
for (auto &wadinf : mIWadInfos)
{
if (wadinf.Name.Compare(result.Name))
{
return -1; // do not show the same one twice.
}
}
return mIWadInfos.Push(result);
}
catch (CRecoverableError &err)
{
delete resfile;
Printf(TEXTCOLOR_RED "%s: %s\nFile has been removed from the list of IWADs\n", fn, err.GetMessage());
return -1;
}
break;
}
}
delete resfile;
Printf(TEXTCOLOR_RED "%s: Unable to find IWADINFO\nFile has been removed from the list of IWADs\n", fn);
return -1;
}
Printf(TEXTCOLOR_RED "%s: Unable to open as resource file.\nFile has been removed from the list of IWADs\n", fn);
return -1;
}
//==========================================================================
//
// CollectSearchPaths
//
// collect all paths in a local array for easier management
//
//==========================================================================
void FIWadManager::CollectSearchPaths()
{
if (GameConfig->SetSection("IWADSearch.Directories"))
{
const char *key;
const char *value;
while (GameConfig->NextInSection(key, value))
{
if (stricmp(key, "Path") == 0)
{
FString nice = NicePath(value);
if (nice.Len() > 0) mSearchPaths.Push(nice);
}
}
}
mSearchPaths.Append(I_GetGogPaths());
mSearchPaths.Append(I_GetSteamPath());
return numfound;
// Unify and remove trailing slashes
for (auto &str : mSearchPaths)
{
FixPathSeperator(str);
if (str[str.Len() - 1] == '/') str.Truncate(str.Len() - 1);
}
}
//==========================================================================
//
// AddIWADCandidates
//
// scans the given directory and adds all potential IWAD candidates
//
//==========================================================================
void FIWadManager::AddIWADCandidates(const char *dir)
{
void *handle;
findstate_t findstate;
FStringf slasheddir("%s/", dir);
FString findmask = slasheddir + "*.*";
if ((handle = I_FindFirst(findmask, &findstate)) != (void *)-1)
{
do
{
if (!(I_FindAttr(&findstate) & FA_DIREC))
{
auto p = strrchr(findstate.Name, '.');
if (p != nullptr)
{
// special IWAD extension.
if (!stricmp(p, ".iwad") || !stricmp(p, ".ipk3") || !stricmp(p, "ipk7"))
{
mFoundWads.Push(FFoundWadInfo{ slasheddir + findstate.Name, "", -1 });
}
}
for (auto &name : mIWadNames)
{
if (!stricmp(name, findstate.Name))
{
mFoundWads.Push(FFoundWadInfo{ slasheddir + name, "", -1 });
}
}
}
} while (I_FindNext(handle, &findstate) == 0);
I_FindClose(handle);
}
}
//==========================================================================
//
// ValidateIWADs
//
// validate all found candidates and eliminate the bogus ones.
//
//==========================================================================
void FIWadManager::ValidateIWADs()
{
TArray<int> returns;
unsigned originalsize = mIWadInfos.Size();
for (auto &p : mFoundWads)
{
int index;
auto x = strrchr(p.mFullPath, '.');
if (x != nullptr && (!stricmp(x, ".iwad") || !stricmp(x, ".ipk3") || !stricmp(x, "ipk7")))
{
index = CheckIWADInfo(p.mFullPath);
}
else
{
index = ScanIWAD(p.mFullPath);
}
p.mInfoIndex = index;
}
}
//==========================================================================
@ -385,129 +501,132 @@ static bool havepicked = false;
int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad, const char *zdoom_wad)
{
TArray<WadStuff> wads;
TArray<size_t> foundwads;
const char *iwadparm = Args->CheckValue ("-iwad");
int pickwad;
size_t numwads;
size_t i;
bool iwadparmfound = false;
FString custwad;
wads.Resize(mIWadNames.Size());
foundwads.Resize(mIWads.Size());
memset(&foundwads[0], 0, foundwads.Size() * sizeof(foundwads[0]));
CollectSearchPaths();
if (iwadparm == NULL && iwad != NULL && *iwad != 0)
// Collect all IWADs in the search path
for (auto &dir : mSearchPaths)
{
iwadparm = iwad;
AddIWADCandidates(dir);
}
unsigned numFoundWads = mFoundWads.Size();
if (iwadparm)
{
// Check if the given IWAD has an absolute path, in which case the search path will be ignored.
custwad = iwadparm;
FixPathSeperator (custwad);
if (CheckIWAD (custwad, &wads[0]))
{ // -iwad parameter was a directory
iwadparm = NULL;
FixPathSeperator(custwad);
bool isAbsolute = (custwad[0] == '/');
#ifdef WINDOWS
isAbsolute |= (custwad.Len() >= 2 && custwad[1] == ':');
#endif
if (isAbsolute)
{
if (FileExists(custwad)) mFoundWads.Push({ custwad, "", -1 });
}
else
{
DefaultExtension (custwad, ".wad");
iwadparm = custwad;
mIWadNames[0] = custwad;
CheckIWAD ("", &wads[0]);
}
}
if (iwadparm == NULL || wads[0].Path.IsEmpty() || mIWads[wads[0].Type].Required.IsNotEmpty())
{
if (GameConfig->SetSection ("IWADSearch.Directories"))
{
const char *key;
const char *value;
while (GameConfig->NextInSection (key, value))
for (auto &dir : mSearchPaths)
{
if (stricmp (key, "Path") == 0)
FStringf fullpath("%s/%s", dir, custwad.GetChars());
if (FileExists(fullpath))
{
FString nice = NicePath(value);
FixPathSeperator(nice);
CheckIWAD(nice, &wads[0]);
mFoundWads.Push({ fullpath, "", -1 });
}
}
}
TArray<FString> gog_paths = I_GetGogPaths();
for (i = 0; i < gog_paths.Size(); ++i)
{
CheckIWAD (gog_paths[i], &wads[0]);
}
TArray<FString> steam_path = I_GetSteamPath();
for (i = 0; i < steam_path.Size(); ++i)
{
CheckIWAD (steam_path[i], &wads[0]);
}
}
// -iwad not found or not specified. Revert back to standard behavior.
if (mFoundWads.Size() == numFoundWads) iwadparm = nullptr;
if (iwadparm != NULL && !wads[0].Path.IsEmpty())
{
iwadparmfound = true;
}
// Now check if what got collected actually is an IWAD.
ValidateIWADs();
for (i = numwads = 0; i < mIWadNames.Size(); i++)
// Check for required dependencies.
for (unsigned i = 0; i < mFoundWads.Size(); i++)
{
if (!wads[i].Path.IsEmpty())
auto infndx = mFoundWads[i].mInfoIndex;
if (infndx >= 0)
{
if (i != numwads)
auto &wadinfo = mIWadInfos[infndx];
if (wadinfo.Required.IsNotEmpty())
{
wads[numwads] = wads[i];
}
foundwads[wads[numwads].Type] = numwads + 1;
numwads++;
}
}
for (unsigned i=0; i<mIWads.Size(); i++)
{
if (mIWads[i].Required.IsNotEmpty() && foundwads[i])
{
bool found = false;
// needs to be loaded with another IWAD (HexenDK)
for (unsigned j=0; j<mIWads.Size(); j++)
{
if (!mIWads[i].Required.Compare(mIWads[j].Name))
bool found = false;
// needs to be loaded with another IWAD (HexenDK)
for (unsigned j = 0; j < mFoundWads.Size(); j++)
{
if (foundwads[j])
auto inf2ndx = mFoundWads[j].mInfoIndex;
if (inf2ndx >= 0)
{
found = true;
mIWads[i].preload = j;
}
break;
}
}
// The required WAD is not there so this one can't be used and must be deleted from the list
if (!found)
{
size_t kill = foundwads[i];
for (size_t j = kill; j < numwads; ++j)
{
wads[j - 1] = wads[j];
}
numwads--;
foundwads[i] = 0;
for (unsigned j = 0; j < foundwads.Size(); ++j)
{
if (foundwads[j] > kill)
{
foundwads[j]--;
if (!mIWadInfos[infndx].Required.Compare(mIWadInfos[inf2ndx].Name))
{
mFoundWads[i].mRequiredPath = mFoundWads[j].mFullPath;
break;
}
}
}
// The required dependency was not found. Skip this IWAD.
if (mFoundWads[i].mRequiredPath.IsEmpty()) mFoundWads[i].mInfoIndex = -1;
}
}
}
TArray<FFoundWadInfo> picks;
if (numFoundWads < mFoundWads.Size())
{
// We have a -iwad parameter. Pick the first usable IWAD we found through that.
for (unsigned i = numFoundWads; i < mFoundWads.Size(); i++)
{
if (mFoundWads[i].mInfoIndex > 0)
{
picks.Push(mFoundWads[i]);
break;
}
}
}
else if (iwad != nullptr && *iwad != 0)
{
int pickedprio = -1;
// scan the list of found IWADs for a matching one for the current PWAD.
for (auto &found : mFoundWads)
{
if (mIWadInfos[found.mInfoIndex].IWadname.CompareNoCase(iwad) == 0 && mIWadInfos[found.mInfoIndex].prio > pickedprio)
{
picks.Clear();
picks.Push(found);
pickedprio = mIWadInfos[found.mInfoIndex].prio;
}
}
}
if (picks.Size() == 0)
{
// Now sort what we found and discard all duplicates.
for (unsigned i = 0; i < mOrderNames.Size(); i++)
{
bool picked = false;
for (int j = 0; j < (int)mFoundWads.Size(); j++)
{
if (mIWadInfos[mFoundWads[j].mInfoIndex].Name.Compare(mOrderNames[i]) == 0)
{
if (!picked)
{
picked = true;
picks.Push(mFoundWads[j]);
}
mFoundWads.Delete(j--);
}
}
}
// What's left is IWADs with their own IWADINFO. Copy those in order of discovery.
for (auto &entry : mFoundWads)
{
if (entry.mInfoIndex >= 0) picks.Push(entry);
}
}
if (numwads == 0)
// If we still haven't found a suitable IWAD let's error out.
if (picks.Size() == 0)
{
I_FatalError ("Cannot find a game IWAD (doom.wad, doom2.wad, heretic.wad, etc.).\n"
"Did you install " GAMENAME " properly? You can do either of the following:\n"
@ -526,38 +645,50 @@ int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad,
"iwads to the list beneath [IWADSearch.Directories]");
#endif
}
int pick = 0;
pickwad = 0;
if (!iwadparmfound && numwads > 1)
// We got more than one so present the IWAD selection box.
if (picks.Size() > 1)
{
int defiwad = 0;
// Locate the user's prefered IWAD, if it was found.
if (defaultiwad[0] != '\0')
{
for (i = 0; i < numwads; ++i)
for (unsigned i = 0; i < picks.Size(); ++i)
{
FString basename = ExtractFileBase(wads[i].Path);
if (stricmp(basename, defaultiwad) == 0)
FString basename = mIWadInfos[picks[i].mInfoIndex].Name;
FString defname = ExtractFileBase(defaultiwad);
if (stricmp(basename, defname) == 0)
{
defiwad = (int)i;
// Delete all but the wanted one.
pick = i;
break;
}
}
}
if (!havepicked) // just use the first IWAD if the restart doesn't have a -iwad parameter. We cannot open the picker in fullscreen mode.
if (picks.Size() > 1)
{
pickwad = I_PickIWad(&wads[0], (int)numwads, queryiwad, defiwad);
if (pickwad >= 0)
if (!havepicked)
{
// The newly selected IWAD becomes the new default
FString basename = ExtractFileBase(wads[pickwad].Path);
defaultiwad = basename;
TArray<WadStuff> wads;
for (auto & found : picks)
{
WadStuff stuff;
stuff.Name = mIWadInfos[found.mInfoIndex].Name;
stuff.Path = ExtractFileBase(found.mFullPath);
wads.Push(stuff);
}
pick = I_PickIWad(&wads[0], (int)wads.Size(), queryiwad, pick);
if (pick >= 0)
{
// The newly selected IWAD becomes the new default
defaultiwad = mIWadInfos[picks[pick].mInfoIndex].Name;
}
else
{
exit(0);
}
havepicked = true;
}
if (pickwad < 0)
exit(0);
havepicked = true;
}
}
@ -565,15 +696,17 @@ int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad,
wadfiles.Clear();
D_AddFile (wadfiles, zdoom_wad);
if (mIWads[wads[pickwad].Type].preload >= 0)
if (picks[pick].mRequiredPath.IsNotEmpty())
{
D_AddFile (wadfiles, wads[foundwads[mIWads[wads[pickwad].Type].preload]-1].Path);
D_AddFile (wadfiles, picks[pick].mRequiredPath);
}
D_AddFile (wadfiles, wads[pickwad].Path);
D_AddFile (wadfiles, picks[pick].mFullPath);
for (unsigned i=0; i < mIWads[wads[pickwad].Type].Load.Size(); i++)
auto info = mIWadInfos[picks[pick].mInfoIndex];
// Load additional resources from the same directory as the IWAD itself.
for (unsigned i=0; i < info.Load.Size(); i++)
{
long lastslash = wads[pickwad].Path.LastIndexOf ('/');
long lastslash = picks[pick].mFullPath.LastIndexOf ('/');
FString path;
if (lastslash == -1)
@ -582,13 +715,13 @@ int FIWadManager::IdentifyVersion (TArray<FString> &wadfiles, const char *iwad,
}
else
{
path = FString (wads[pickwad].Path.GetChars(), lastslash + 1);
path = FString (picks[pick].mFullPath.GetChars(), lastslash + 1);
}
path += mIWads[wads[pickwad].Type].Load[i];
path += info.Load[i];
D_AddFile (wadfiles, path);
}
return wads[pickwad].Type;
return picks[pick].mInfoIndex;
}
@ -602,7 +735,7 @@ const FIWADInfo *FIWadManager::FindIWAD(TArray<FString> &wadfiles, const char *i
{
int iwadType = IdentifyVersion(wadfiles, iwad, basewad);
//gameiwad = iwadType;
const FIWADInfo *iwad_info = &mIWads[iwadType];
const FIWADInfo *iwad_info = &mIWadInfos[iwadType];
if (DoomStartupInfo.Name.IsEmpty()) DoomStartupInfo.Name = iwad_info->Name;
if (DoomStartupInfo.BkColor == 0 && DoomStartupInfo.FgColor == 0)
{

View file

@ -2342,8 +2342,7 @@ void D_DoomMain (void)
}
FString basewad = wad;
iwad_man = new FIWadManager;
iwad_man->ParseIWadInfos(basewad);
iwad_man = new FIWadManager(basewad);
// Now that we have the IWADINFO, initialize the autoload ini sections.
GameConfig->DoAutoloadSetup(iwad_man);
@ -2373,8 +2372,7 @@ void D_DoomMain (void)
if (iwad_man == NULL)
{
iwad_man = new FIWadManager;
iwad_man->ParseIWadInfos(basewad);
iwad_man = new FIWadManager(basewad);
}
const FIWADInfo *iwad_info = iwad_man->FindIWAD(allwads, iwad, basewad);
gameinfo.gametype = iwad_info->gametype;

View file

@ -70,29 +70,38 @@ extern uint32_t r_renderercaps;
struct WadStuff
{
WadStuff() : Type(0) {}
FString Path;
FString Name;
int Type;
};
struct FIWADInfo
{
FString Name; // Title banner text for this IWAD
FString Autoname; // Name of autoload ini section for this IWAD
FString IWadname; // Default name this game would use - this is for IWAD detection in GAMEINFO.
int prio = 0; // selection priority for given IWAD name.
FString Configname; // Name of config section for this IWAD
FString Required; // Requires another IWAD
uint32_t FgColor; // Foreground color for title banner
uint32_t BkColor; // Background color for title banner
EGameType gametype; // which game are we playing?
uint32_t FgColor = 0; // Foreground color for title banner
uint32_t BkColor = 0xc0c0c0; // Background color for title banner
EGameType gametype = GAME_Doom; // which game are we playing?
FString MapInfo; // Base mapinfo to load
TArray<FString> Load; // Wads to be loaded with this one.
TArray<FString> Lumps; // Lump names for identification
int flags;
int preload;
int flags = 0;
};
FIWADInfo() { flags = 0; preload = -1; FgColor = 0; BkColor= 0xc0c0c0; gametype = GAME_Doom; }
struct FFoundWadInfo
{
FString mFullPath;
FString mRequiredPath;
int mInfoIndex = -1; // must be an index because of reallocation
FFoundWadInfo() {}
FFoundWadInfo(const FString &s1, const FString &s2, int index)
: mFullPath(s1), mRequiredPath(s2), mInfoIndex(index)
{
}
};
struct FStartupInfo
@ -123,28 +132,31 @@ extern FStartupInfo DoomStartupInfo;
class FIWadManager
{
TArray<FIWADInfo> mIWads;
TArray<FIWADInfo> mIWadInfos;
TArray<FString> mIWadNames;
TArray<FString> mSearchPaths;
TArray<FString> mOrderNames;
TArray<FFoundWadInfo> mFoundWads;
TArray<int> mLumpsFound;
void ParseIWadInfo(const char *fn, const char *data, int datasize);
void ClearChecks();
void CheckLumpName(const char *name);
int GetIWadInfo();
void ParseIWadInfo(const char *fn, const char *data, int datasize, FIWADInfo *result = nullptr);
int ScanIWAD (const char *iwad);
int CheckIWAD (const char *doomwaddir, WadStuff *wads);
int CheckIWADInfo(const char *iwad);
int IdentifyVersion (TArray<FString> &wadfiles, const char *iwad, const char *zdoom_wad);
void CollectSearchPaths();
void AddIWADCandidates(const char *dir);
void ValidateIWADs();
public:
void ParseIWadInfos(const char *fn);
FIWadManager(const char *fn);
const FIWADInfo *FindIWAD(TArray<FString> &wadfiles, const char *iwad, const char *basewad);
const FString *GetAutoname(unsigned int num) const
{
if (num < mIWads.Size()) return &mIWads[num].Autoname;
if (num < mIWadInfos.Size()) return &mIWadInfos[num].Autoname;
else return NULL;
}
int GetIWadFlags(unsigned int num) const
{
if (num < mIWads.Size()) return mIWads[num].flags;
if (num < mIWadInfos.Size()) return mIWadInfos[num].flags;
else return false;
}

View file

@ -242,6 +242,15 @@ public:
::new((void*)&Array[Count]) T(item);
return Count++;
}
void Append(const TArray<T> &item)
{
unsigned start = Reserve(item.Size());
for (unsigned i = 0; i < item.Size(); i++)
{
Array[start + i] = item[i];
}
}
bool Pop ()
{
if (Count > 0)

View file

@ -1,5 +1,17 @@
// Must be sorted in identification order (easiest to recognize first!)
IWad
{
Name = "Rise Of The Wool Ball"
Autoname = "woolball.rotwb"
Game = "Doom"
Config = "WoolBall"
IWADName = "rotwb.wad"
Mapinfo = "mapinfo/doom2.txt"
MustContain = "E3M6", "B3DED", "WORLDMAP", "NUCLSKYM", "PLANETLE", "MEOWZEKI", "ZEKIINTR", "NOWAYBAC"
BannerColors = "32 54 43", "c6 dc d1"
}
IWad
{
Name = "Delaweare"
@ -7,6 +19,7 @@ IWad
Game = "Doom"
Config = "Delaweare"
Mapinfo = "mapinfo/doom2.txt"
IWADName = "delaweare.wad"
MustContain = "TITLEMAP", "ROVEA0", "GRLURD01", "SQOUI01"
BannerColors = "00 00 00", "ff ff ff"
}
@ -17,6 +30,7 @@ IWad
Autoname = "square.square"
Game = "Doom"
Config = "Square"
IWADName = "square.pk3"
Mapinfo = "mapinfo/mindefaults.txt"
MustContain = "SQU-IWAD", "E1A1"
BannerColors = "ff ff ff", "80 00 80"
@ -28,6 +42,7 @@ IWad
Autoname = "square.squareware"
Game = "Doom"
Config = "Square"
IWADName = "square1.pk3"
Mapinfo = "mapinfo/mindefaults.txt"
MustContain = "SQU-SWE1", "E1A1"
BannerColors = "ff ff ff", "80 00 80"
@ -39,6 +54,7 @@ IWad
Autoname = "harmony"
Game = "Doom"
Config = "Harmony"
IWADName = "harm1.wad"
Mapinfo = "mapinfo/harmony.txt"
MustContain = "MAP01", "0HAWK01", "0CARA3", "0NOSE1"
BannerColors = "6e b4 d6", "45 4f 7e"
@ -50,6 +66,7 @@ IWad
Game = "Doom"
Config = "Hacx"
Autoname = "hacx.hacx2"
IWADName = "hacx2.wad"
Mapinfo = "mapinfo/hacx.txt"
MustContain = "MAP01", "HACX-E"
BannerColors = "ff ff ff", "00 88 22"
@ -61,6 +78,7 @@ IWad
Game = "Doom"
Config = "Hacx"
Autoname = "hacx.hacx1"
IWADName = "hacx.wad"
Mapinfo = "mapinfo/hacx.txt"
MustContain = "MAP01", "HACX-R"
BannerColors = "00 00 a8", "a8 a8 a8"
@ -72,6 +90,7 @@ IWad
Autoname = "urbanbrawl"
Game = "Doom"
Config = "UrbanBrawl"
IWADName = "action2.wad"
Mapinfo = "mapinfo/urbanbrawl.txt"
MustContain = "MAP01", "AD2LIB"
BannerColors = "a8 a8 00", "a8 00 00"
@ -83,6 +102,7 @@ IWad
Autoname = "chex.chex3"
Game = "Chex"
Config = "Chex"
IWADName = "chex3.wad"
Mapinfo = "mapinfo/chex.txt"
Compatibility = "NoTextcolor"
MustContain = "E1M1", "CYCLA1", "FLMBA1", "MAPINFO"
@ -95,6 +115,7 @@ IWad
Autoname = "chex.chex1"
Game = "Chex"
Config = "Chex"
IWADName = "chex.wad"
Mapinfo = "mapinfo/chex.txt"
MustContain = "E1M1", "E4M1", "W94_1", "POSSH0M0"
BannerColors = "ff ff 00", "00 c0 00"
@ -106,6 +127,7 @@ IWad
Autoname = "strife.veteran"
Game = "Strife"
Config = "Strife"
IWADName = "sve.wad"
Mapinfo = "mapinfo/strife.txt"
MustContain = "MAP35", "I_RELB", "FXAA_F"
BannerColors = "f0 f0 f0", "6b 3c 18"
@ -119,6 +141,7 @@ IWad
Autoname = "strife.strife"
Game = "Strife"
Config = "Strife"
IWADName = "strife1.wad"
Mapinfo = "mapinfo/strife.txt"
MustContain = "MAP01", "MAP33", "ENDSTRF"
BannerColors = "d0 ad 99", "00 6b 65"
@ -155,6 +178,7 @@ IWad
Game = "Hexen"
Config = "Hexen"
Autoname = "hexen.hexen"
IWADName = "hexen.wad"
Mapinfo = "mapinfo/hexen.txt"
Compatibility = "Poly1"
MustContain = "TITLE", "MAP01", "MAP40", "WINNOWR"
@ -191,6 +215,7 @@ IWad
Autoname = "blasphemer"
Game = "Heretic"
Config = "Heretic"
IWADName = "blasphemer.wad"
Mapinfo = "mapinfo/heretic.txt"
MustContain = "E1M1", "E2M1", "TITLE", "BLASPHEM"
BannerColors = "73 00 00", "00 00 00"
@ -202,6 +227,7 @@ IWad
Autoname = "heretic.shadow"
Game = "Heretic"
Config = "Heretic"
IWADName = "heretic.wad"
Mapinfo = "mapinfo/heretic.txt"
Compatibility = "Extended"
MustContain = "E1M1", "E2M1", "TITLE", "MUS_E1M1", "EXTENDED"
@ -213,6 +239,7 @@ IWad
Name = "Heretic"
Game = "Heretic"
Config = "Heretic"
IWADName = "heretic.wad"
Autoname = "heretic.heretic"
Mapinfo = "mapinfo/heretic.txt"
MustContain = "E1M1", "E2M1", "TITLE", "MUS_E1M1"
@ -236,6 +263,7 @@ IWad
Autoname = "doom.freedoom.freedm"
Game = "Doom"
Config = "Doom"
IWADName = "freedm.wad"
Mapinfo = "mapinfo/doom2.txt"
MustContain = "MAP01", "FREEDM"
BannerColors = "32 54 43", "c6 dc d1"
@ -247,6 +275,7 @@ IWad
Autoname = "doom.freedoom.phase2"
Game = "Doom"
Config = "Doom"
IWADName = "freedoom2.wad"
Mapinfo = "mapinfo/doom2.txt"
MustContain = "MAP01", "FREEDOOM"
BannerColors = "32 54 43", "c6 dc d1"
@ -258,6 +287,7 @@ IWad
Autoname = "doom.freedoom.phase1"
Game = "Doom"
Config = "Doom"
IWADName = "freedoom1.wad"
Mapinfo = "mapinfo/doom1.txt"
MustContain = "E1M1", "E2M1", "E3M1", "FREEDOOM"
BannerColors = "32 54 43", "c6 dc d1"
@ -280,6 +310,7 @@ IWad
Autoname = "doom.doom1.bfg"
Game = "Doom"
Config = "Doom"
IWADName = "doom.wad", 2
Mapinfo = "mapinfo/ultdoom.txt"
Compatibility = "Shorttex"
MustContain = "E1M1","E2M1","E2M2","E2M3","E2M4","E2M5","E2M6","E2M7","E2M8","E2M9",
@ -307,6 +338,7 @@ IWad
Autoname = "doom.doom1.ultimate"
Game = "Doom"
Config = "Doom"
IWADName = "doom.wad"
Mapinfo = "mapinfo/ultdoom.txt"
Compatibility = "Shorttex"
MustContain = "E1M1","E2M1","E2M2","E2M3","E2M4","E2M5","E2M6","E2M7","E2M8","E2M9",
@ -321,6 +353,7 @@ IWad
Autoname = "doom.doom1.registered"
Game = "Doom"
Config = "Doom"
IWADName = "doom.wad", 1
Mapinfo = "mapinfo/doom1.txt"
Compatibility = "Shorttex"
MustContain = "E1M1","E2M1","E2M2","E2M3","E2M4","E2M5","E2M6","E2M7","E2M8","E2M9",
@ -329,17 +362,6 @@ IWad
BannerColors = "54 54 54", "a8 a8 a8"
}
IWad
{
Name = "Rise Of The Wool Ball"
Autoname = "woolball.rotwb"
Game = "Doom"
Config = "WoolBall"
Mapinfo = "mapinfo/doom2.txt"
MustContain = "E3M6", "B3DED", "WORLDMAP", "NUCLSKYM", "PLANETLE", "MEOWZEKI", "ZEKIINTR", "NOWAYBAC"
BannerColors = "32 54 43", "c6 dc d1"
}
IWad
{
Name = "DOOM Shareware"
@ -357,6 +379,7 @@ IWad
Autoname = "doom.doom2.tnt"
Game = "Doom"
Config = "Doom"
IWADName = "tnt.wad"
Mapinfo = "mapinfo/tnt.txt"
Compatibility = "Shorttex", "Stairs"
MustContain = "MAP01", "REDTNT2"
@ -369,6 +392,7 @@ IWad
Autoname = "doom.doom2.plutonia"
Game = "Doom"
Config = "Doom"
IWADName = "plutonia.wad"
Mapinfo = "mapinfo/plutonia.txt"
Compatibility = "Shorttex"
MustContain = "MAP01", "CAMO1"
@ -381,6 +405,7 @@ IWad
Autoname = "doom.doom2.bfg"
Game = "Doom"
Config = "Doom"
IWADName = "doom2.wad"
Mapinfo = "mapinfo/doom2bfg.txt"
Compatibility = "Shorttex"
MustContain = "MAP01", "DMENUPIC", "M_ACPT", "M_CAN", "M_EXITO", "M_CHG"
@ -388,16 +413,6 @@ IWad
Load = "nerve.wad"
}
IWad
{
Name = "WolfenDoom: Blade of Agony"
Game = "Doom"
BannerColors = "E30000", "E3E3E3"
Mapinfo = "mapinfo/doom2.txt"
MustContain = "BOALIB", "C1M1", "Episode3", "MSPUB0"
Config = "BoA"
}
// Doom 2 must be last to be checked becaude MAP01 is its only requirement
IWad
{
@ -405,6 +420,7 @@ IWad
Autoname = "doom.doom2.commercial"
Game = "Doom"
Config = "Doom"
IWADName = "doom2.wad", 1
Mapinfo = "mapinfo/doom2.txt"
Compatibility = "Shorttex"
MustContain = "MAP01"
@ -412,7 +428,6 @@ IWad
}
Names
{
"doom_complete.pk3"
@ -454,5 +469,41 @@ Names
"square1.pk3"
"delaweare.wad"
"rotwb.wad"
"boa_c2.pk3"
}
Order // Order in the IWAD selection box
{
"DOOM: Complete: WadSmoosh"
"DOOM 2: Hell on Earth"
"Final Doom: Plutonia Experiment"
"Final Doom: TNT - Evilution"
"The Ultimate DOOM"
"DOOM Registered"
"DOOM Shareware"
"DOOM 2: BFG Edition"
"DOOM: BFG Edition"
"Freedoom: Phase 1"
"Freedoom: Phase 2"
"FreeDM"
"Heretic"
"Heretic: Shadow of the Serpent Riders"
"Heretic Shareware"
"Hexen: Beyond Heretic"
"Hexen: Deathkings of the Dark Citadel"
"Hexen: Demo Version"
"Strife: Quest for the Sigil"
"Strife: Veteran Edition"
"Strife: Teaser (Old Version)"
"Strife: Teaser (New Version)"
"Blasphemer"
"Chex(R) Quest"
"Chex(R) Quest 3"
"Action Doom 2: Urban Brawl"
"Harmony"
"Hacx: Twitch'n Kill"
"Hacx 2.0"
"The Adventures of Square"
"The Adventures of Square (Square-ware)"
"Delaweare"
"Rise Of The Wool Ball"
}