mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-08 05:51:26 +00:00
691 lines
19 KiB
C++
691 lines
19 KiB
C++
|
/*
|
||
|
** d_iwad.cpp
|
||
|
** IWAD detection code
|
||
|
**
|
||
|
**---------------------------------------------------------------------------
|
||
|
** Copyright 1998-2009 Randy Heit
|
||
|
** Copyright 2009 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 "d_main.h"
|
||
|
#include "gi.h"
|
||
|
#include "cmdlib.h"
|
||
|
#include "doomstat.h"
|
||
|
#include "i_system.h"
|
||
|
#include "w_wad.h"
|
||
|
#include "w_zip.h"
|
||
|
#include "v_palette.h"
|
||
|
#include "m_argv.h"
|
||
|
#include "c_cvars.h"
|
||
|
#include "gameconfigfile.h"
|
||
|
|
||
|
|
||
|
CVAR (Bool, queryiwad, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
||
|
CVAR (String, defaultiwad, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
||
|
|
||
|
// If autoname is NULL, that's either because that game doesn't allow
|
||
|
// loading of external wads or because it's already caught by the
|
||
|
// general game-specific wads section.
|
||
|
const IWADInfo IWADInfos[NUM_IWAD_TYPES] =
|
||
|
{
|
||
|
// banner text, autoname, fg color, bg color
|
||
|
{ "Final Doom: TNT - Evilution", "TNT", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/tnt.txt", GI_MAPxx },
|
||
|
{ "Final Doom: Plutonia Experiment", "Plutonia", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/plutonia.txt", GI_MAPxx },
|
||
|
{ "Hexen: Beyond Heretic", NULL, MAKERGB(240,240,240), MAKERGB(107,44,24), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx },
|
||
|
{ "Hexen: Deathkings of the Dark Citadel", "HexenDK", MAKERGB(240,240,240), MAKERGB(139,68,9), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx },
|
||
|
{ "Hexen: Demo Version", "HexenDemo",MAKERGB(240,240,240), MAKERGB(107,44,24), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx | GI_SHAREWARE },
|
||
|
{ "DOOM 2: Hell on Earth", "Doom2", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx },
|
||
|
{ "Heretic Shareware", NULL, MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/hereticsw.txt",GI_SHAREWARE },
|
||
|
{ "Heretic: Shadow of the Serpent Riders", NULL, MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/heretic.txt", GI_MENUHACK_EXTENDED },
|
||
|
{ "Heretic", NULL, MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/heretic.txt" },
|
||
|
{ "DOOM Shareware", NULL, MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom1.txt", GI_SHAREWARE },
|
||
|
{ "The Ultimate DOOM", "Doom1", MAKERGB(84,84,84), MAKERGB(168,168,168), GAME_Doom, "mapinfo/ultdoom.txt" },
|
||
|
{ "DOOM Registered", "Doom1", MAKERGB(84,84,84), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom1.txt" },
|
||
|
{ "Strife: Quest for the Sigil", NULL, MAKERGB(224,173,153), MAKERGB(0,107,101), GAME_Strife, "mapinfo/strife.txt", GI_MAPxx },
|
||
|
{ "Strife: Teaser (Old Version)", NULL, MAKERGB(224,173,153), MAKERGB(0,107,101), GAME_Strife, "mapinfo/strife.txt", GI_MAPxx | GI_SHAREWARE },
|
||
|
{ "Strife: Teaser (New Version)", NULL, MAKERGB(224,173,153), MAKERGB(0,107,101), GAME_Strife, "mapinfo/strife.txt", GI_MAPxx | GI_SHAREWARE | GI_TEASER2 },
|
||
|
{ "Freedoom", "Freedoom", MAKERGB(50,84,67), MAKERGB(198,220,209), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx },
|
||
|
{ "Freedoom \"Demo\"", "Freedoom1",MAKERGB(50,84,67), MAKERGB(198,220,209), GAME_Doom, "mapinfo/doom1.txt" },
|
||
|
{ "FreeDM", "FreeDM", MAKERGB(50,84,67), MAKERGB(198,220,209), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx },
|
||
|
{ "Chex(R) Quest", "Chex1", MAKERGB(255,255,0), MAKERGB(0,192,0), GAME_Chex, "mapinfo/chex.txt" },
|
||
|
{ "Chex(R) Quest 3", "Chex3", MAKERGB(255,255,0), MAKERGB(0,192,0), GAME_Chex, "mapinfo/chex3.txt" },
|
||
|
//{ "ZDoom Engine", NULL, MAKERGB(168,0,0), MAKERGB(168,168,168) },
|
||
|
};
|
||
|
|
||
|
static const char *IWADNames[] =
|
||
|
{
|
||
|
NULL,
|
||
|
"doom2f.wad",
|
||
|
"doom2.wad",
|
||
|
"plutonia.wad",
|
||
|
"tnt.wad",
|
||
|
"doomu.wad", // Hack from original Linux version. Not necessary, but I threw it in anyway.
|
||
|
"doom.wad",
|
||
|
"doom1.wad",
|
||
|
"heretic.wad",
|
||
|
"heretic1.wad",
|
||
|
"hexen.wad",
|
||
|
"hexdd.wad",
|
||
|
"hexendemo.wad",
|
||
|
"hexdemo.wad",
|
||
|
"strife1.wad",
|
||
|
"strife0.wad",
|
||
|
"freedoom.wad", // Freedoom.wad is distributed as Doom2.wad, but this allows to have both in the same directory.
|
||
|
"freedoom1.wad",
|
||
|
"freedm.wad",
|
||
|
"chex.wad",
|
||
|
"chex3.wad",
|
||
|
#ifdef unix
|
||
|
"DOOM2.WAD", // Also look for all-uppercase names
|
||
|
"PLUTONIA.WAD",
|
||
|
"TNT.WAD",
|
||
|
"DOOM.WAD",
|
||
|
"DOOM1.WAD",
|
||
|
"HERETIC.WAD",
|
||
|
"HERETIC1.WAD",
|
||
|
"HEXEN.WAD",
|
||
|
"HEXDD.WAD",
|
||
|
"HEXENDEMO.WAD",
|
||
|
"HEXDEMO.WAD",
|
||
|
"STRIFE1.WAD",
|
||
|
"STRIFE0.WAD",
|
||
|
"FREEDOOM.WAD",
|
||
|
"FREEDOOM1.WAD",
|
||
|
"FREEDM.WAD",
|
||
|
"CHEX.WAD",
|
||
|
"CHEX3.WAD",
|
||
|
#endif
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// ScanIWAD
|
||
|
//
|
||
|
// Scan the contents of an IWAD to determine which one it is
|
||
|
//==========================================================================
|
||
|
|
||
|
static EIWADType ScanIWAD (const char *iwad)
|
||
|
{
|
||
|
static const char checklumps[][8] =
|
||
|
{
|
||
|
"E1M1",
|
||
|
"E4M2",
|
||
|
"MAP01",
|
||
|
"MAP40",
|
||
|
"MAP60",
|
||
|
"TITLE",
|
||
|
"REDTNT2",
|
||
|
"CAMO1",
|
||
|
{ 'E','X','T','E','N','D','E','D'},
|
||
|
"ENDSTRF",
|
||
|
"MAP33",
|
||
|
"INVCURS",
|
||
|
{ 'F','R','E','E','D','O','O','M' },
|
||
|
"W94_1",
|
||
|
{ 'P','O','S','S','H','0','M','0' },
|
||
|
"CYCLA1",
|
||
|
"FLMBA1",
|
||
|
"MAPINFO",
|
||
|
{ 'G','A','M','E','I','N','F','O' },
|
||
|
"E2M1","E2M2","E2M3","E2M4","E2M5","E2M6","E2M7","E2M8","E2M9",
|
||
|
"E3M1","E3M2","E3M3","E3M4","E3M5","E3M6","E3M7","E3M8","E3M9",
|
||
|
"DPHOOF","BFGGA0","HEADA1","CYBRA1",
|
||
|
{ 'S','P','I','D','A','1','D','1' },
|
||
|
|
||
|
};
|
||
|
#define NUM_CHECKLUMPS (sizeof(checklumps)/8)
|
||
|
enum
|
||
|
{
|
||
|
Check_e1m1,
|
||
|
Check_e4m1,
|
||
|
Check_map01,
|
||
|
Check_map40,
|
||
|
Check_map60,
|
||
|
Check_title,
|
||
|
Check_redtnt2,
|
||
|
Check_cam01,
|
||
|
Check_Extended,
|
||
|
Check_endstrf,
|
||
|
Check_map33,
|
||
|
Check_invcurs,
|
||
|
Check_FreeDoom,
|
||
|
Check_W94_1,
|
||
|
Check_POSSH0M0,
|
||
|
Check_Cycla1,
|
||
|
Check_Flmba1,
|
||
|
Check_Mapinfo,
|
||
|
Check_Gameinfo,
|
||
|
Check_e2m1
|
||
|
};
|
||
|
int lumpsfound[NUM_CHECKLUMPS];
|
||
|
size_t i;
|
||
|
wadinfo_t header;
|
||
|
FILE *f;
|
||
|
|
||
|
memset (lumpsfound, 0, sizeof(lumpsfound));
|
||
|
if ( (f = fopen (iwad, "rb")) )
|
||
|
{
|
||
|
fread (&header, sizeof(header), 1, f);
|
||
|
if (header.Magic == IWAD_ID || header.Magic == PWAD_ID)
|
||
|
{
|
||
|
header.NumLumps = LittleLong(header.NumLumps);
|
||
|
if (0 == fseek (f, LittleLong(header.InfoTableOfs), SEEK_SET))
|
||
|
{
|
||
|
for (i = 0; i < (size_t)header.NumLumps; i++)
|
||
|
{
|
||
|
wadlump_t lump;
|
||
|
size_t j;
|
||
|
|
||
|
if (0 == fread (&lump, sizeof(lump), 1, f))
|
||
|
break;
|
||
|
for (j = 0; j < NUM_CHECKLUMPS; j++)
|
||
|
if (strnicmp (lump.Name, checklumps[j], 8) == 0)
|
||
|
lumpsfound[j]++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#if 0
|
||
|
else if (header.Magic == ZIP_ID)
|
||
|
{ // Using a zip as an IWAD replacement requires that the key lumps be in the global scope.
|
||
|
// This is because most of them will be "Custom IWADs" so all that we really should need
|
||
|
// to find is GAMEINFO, but why limit ourselves?
|
||
|
header.NumLumps = 0;
|
||
|
FileReader *reader = new FileReader(f);
|
||
|
DWORD centraldir = Zip_FindCentralDir(reader);
|
||
|
delete reader;
|
||
|
if (fseek(f, centraldir, SEEK_SET) == 0)
|
||
|
{
|
||
|
// First locate directory
|
||
|
FZipEndOfCentralDirectory directory;
|
||
|
if (0 != fread(&directory, sizeof(directory), 1, f) && LittleLong(directory.Magic) == 0x06054b50)
|
||
|
{
|
||
|
header.NumLumps += LittleLong(directory.NumEntries);
|
||
|
if (fseek(f, LittleLong(directory.DirectoryOffset), SEEK_SET) == 0)
|
||
|
{
|
||
|
// Scan directory for lumps in the global scope with key names.
|
||
|
do
|
||
|
{
|
||
|
FZipCentralDirectoryInfo entry;
|
||
|
if (0 == fread(&entry, sizeof(entry), 1, f))
|
||
|
break;
|
||
|
if (LittleLong(entry.Magic) == 0x02014b50)
|
||
|
{
|
||
|
// Now determine the lump's short name
|
||
|
char* fullname = new char[LittleLong(entry.NameLength)];
|
||
|
if (0 == fread(fullname, LittleLong(entry.NameLength), 1, f))
|
||
|
{
|
||
|
delete[] fullname;
|
||
|
break;
|
||
|
}
|
||
|
FString name = fullname;
|
||
|
delete[] fullname;
|
||
|
|
||
|
if(name.LastIndexOf('/') != -1)
|
||
|
continue;
|
||
|
name.Truncate(name.LastIndexOf('.'));
|
||
|
|
||
|
for (size_t j = 0; j < NUM_CHECKLUMPS; j++)
|
||
|
if (strnicmp (name, checklumps[j], 8) == 0)
|
||
|
lumpsfound[j]++;
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
while(true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
fclose (f);
|
||
|
}
|
||
|
|
||
|
// Always check for custom iwads first.
|
||
|
#if 0
|
||
|
if (lumpsfound[Check_Gameinfo])
|
||
|
{
|
||
|
return IWAD_Custom;
|
||
|
}
|
||
|
#endif
|
||
|
if (lumpsfound[Check_title] && lumpsfound[Check_map60])
|
||
|
{
|
||
|
return IWAD_HexenDK;
|
||
|
}
|
||
|
else if (lumpsfound[Check_map33] && lumpsfound[Check_endstrf])
|
||
|
{
|
||
|
if (lumpsfound[Check_map01])
|
||
|
{
|
||
|
return IWAD_Strife;
|
||
|
}
|
||
|
else if (lumpsfound[Check_invcurs])
|
||
|
{
|
||
|
return IWAD_StrifeTeaser2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return IWAD_StrifeTeaser;
|
||
|
}
|
||
|
}
|
||
|
else if (lumpsfound[Check_map01])
|
||
|
{
|
||
|
if (lumpsfound[Check_FreeDoom])
|
||
|
{
|
||
|
// Is there a 100% reliable way to tell FreeDoom and FreeDM
|
||
|
// apart based solely on the lump names?
|
||
|
if (strstr(iwad, "freedm.wad") || strstr(iwad, "FREEDM.WAD"))
|
||
|
{
|
||
|
return IWAD_FreeDM;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return IWAD_FreeDoom;
|
||
|
}
|
||
|
}
|
||
|
else if (lumpsfound[Check_redtnt2])
|
||
|
{
|
||
|
return IWAD_Doom2TNT;
|
||
|
}
|
||
|
else if (lumpsfound[Check_cam01])
|
||
|
{
|
||
|
return IWAD_Doom2Plutonia;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (lumpsfound[Check_title])
|
||
|
{
|
||
|
if (lumpsfound[Check_map40])
|
||
|
{
|
||
|
return IWAD_Hexen;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return IWAD_HexenDemo;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return IWAD_Doom2;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (lumpsfound[Check_e1m1])
|
||
|
{
|
||
|
if (lumpsfound[Check_title])
|
||
|
{
|
||
|
if (!lumpsfound[Check_e2m1])
|
||
|
{
|
||
|
return IWAD_HereticShareware;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (lumpsfound[Check_Extended])
|
||
|
{
|
||
|
return IWAD_HereticExtended;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return IWAD_Heretic;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (lumpsfound[Check_Cycla1] && lumpsfound[Check_Flmba1])
|
||
|
{
|
||
|
if (!lumpsfound[Check_Mapinfo])
|
||
|
{
|
||
|
// The original release won't work without its hacked custom EXE.
|
||
|
//I_FatalError("Found an incompatible version of Chex Quest 3");
|
||
|
return NUM_IWAD_TYPES; // Can't use it.
|
||
|
}
|
||
|
return IWAD_ChexQuest3;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (lumpsfound[Check_FreeDoom])
|
||
|
{
|
||
|
return IWAD_FreeDoom1;
|
||
|
}
|
||
|
for (i = Check_e2m1; i < NUM_CHECKLUMPS; i++)
|
||
|
{
|
||
|
if (!lumpsfound[i])
|
||
|
{
|
||
|
return IWAD_DoomShareware;
|
||
|
}
|
||
|
}
|
||
|
if (i == NUM_CHECKLUMPS)
|
||
|
{
|
||
|
if (lumpsfound[Check_e4m1])
|
||
|
{
|
||
|
if (lumpsfound[Check_W94_1] && lumpsfound[Check_POSSH0M0])
|
||
|
{
|
||
|
return IWAD_ChexQuest;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return IWAD_UltimateDoom;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return IWAD_DoomRegistered;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return NUM_IWAD_TYPES; // Don't know
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// CheckIWAD
|
||
|
//
|
||
|
// 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).
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
static int CheckIWAD (const char *doomwaddir, WadStuff *wads)
|
||
|
{
|
||
|
const char *slash;
|
||
|
int i;
|
||
|
int numfound;
|
||
|
|
||
|
numfound = 0;
|
||
|
|
||
|
slash = (doomwaddir[0] && doomwaddir[strlen (doomwaddir)-1] != '/') ? "/" : "";
|
||
|
|
||
|
// Search for a pre-defined IWAD
|
||
|
for (i = IWADNames[0] ? 0 : 1; IWADNames[i]; i++)
|
||
|
{
|
||
|
if (wads[i].Path.IsEmpty())
|
||
|
{
|
||
|
FString iwad;
|
||
|
|
||
|
iwad.Format ("%s%s%s", doomwaddir, slash, IWADNames[i]);
|
||
|
FixPathSeperator (iwad.LockBuffer());
|
||
|
iwad.UnlockBuffer();
|
||
|
if (FileExists (iwad))
|
||
|
{
|
||
|
wads[i].Type = ScanIWAD (iwad);
|
||
|
if (wads[i].Type != NUM_IWAD_TYPES)
|
||
|
{
|
||
|
wads[i].Path = iwad;
|
||
|
numfound++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return numfound;
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// CheckIWADinEnvDir
|
||
|
//
|
||
|
// Checks for an IWAD in a path that contains one or more environment
|
||
|
// variables.
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
static int CheckIWADinEnvDir (const char *str, WadStuff *wads)
|
||
|
{
|
||
|
FString expanded = ExpandEnvVars (str);
|
||
|
|
||
|
if (!expanded.IsEmpty())
|
||
|
{
|
||
|
char *dir = expanded.LockBuffer ();
|
||
|
FixPathSeperator (dir);
|
||
|
expanded.UnlockBuffer ();
|
||
|
if (expanded[expanded.Len() - 1] != '/')
|
||
|
{
|
||
|
expanded += '/';
|
||
|
}
|
||
|
return CheckIWAD (expanded, wads);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// IdentifyVersion
|
||
|
//
|
||
|
// Tries to find an IWAD in one of four directories under DOS or Win32:
|
||
|
// 1. Current directory
|
||
|
// 2. Executable directory
|
||
|
// 3. $DOOMWADDIR
|
||
|
// 4. $HOME
|
||
|
//
|
||
|
// Under UNIX OSes, the search path is:
|
||
|
// 1. Current directory
|
||
|
// 2. $DOOMWADDIR
|
||
|
// 3. $HOME/.zdoom
|
||
|
// 4. The share directory defined at compile time (/usr/local/share/zdoom)
|
||
|
//
|
||
|
// The search path can be altered by editing the IWADSearch.Directories
|
||
|
// section of the config file.
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
static EIWADType IdentifyVersion (const char *zdoom_wad)
|
||
|
{
|
||
|
WadStuff wads[countof(IWADNames)];
|
||
|
size_t foundwads[NUM_IWAD_TYPES] = { 0 };
|
||
|
const char *iwadparm = Args->CheckValue ("-iwad");
|
||
|
size_t numwads;
|
||
|
int pickwad;
|
||
|
size_t i;
|
||
|
bool iwadparmfound = false;
|
||
|
FString custwad;
|
||
|
|
||
|
if (iwadparm)
|
||
|
{
|
||
|
custwad = iwadparm;
|
||
|
FixPathSeperator (custwad.LockBuffer());
|
||
|
if (CheckIWAD (custwad, wads))
|
||
|
{ // -iwad parameter was a directory
|
||
|
iwadparm = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DefaultExtension (custwad, ".wad");
|
||
|
iwadparm = custwad;
|
||
|
IWADNames[0] = iwadparm;
|
||
|
CheckIWAD ("", wads);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (iwadparm == NULL || wads[0].Path.IsEmpty())
|
||
|
{
|
||
|
if (GameConfig->SetSection ("IWADSearch.Directories"))
|
||
|
{
|
||
|
const char *key;
|
||
|
const char *value;
|
||
|
|
||
|
while (GameConfig->NextInSection (key, value))
|
||
|
{
|
||
|
if (stricmp (key, "Path") == 0)
|
||
|
{
|
||
|
if (strchr (value, '$') != NULL)
|
||
|
{
|
||
|
CheckIWADinEnvDir (value, wads);
|
||
|
}
|
||
|
#ifdef unix
|
||
|
else if (*value == '~' && (*(value + 1) == 0 || *(value + 1) == '/'))
|
||
|
{
|
||
|
FString homepath = GetUserFile (*(value + 1) ? value + 2 : value + 1, true);
|
||
|
CheckIWAD (homepath, wads);
|
||
|
}
|
||
|
#endif
|
||
|
else
|
||
|
{
|
||
|
CheckIWAD (value, wads);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#ifdef _WIN32
|
||
|
FString steam_path = I_GetSteamPath();
|
||
|
if (steam_path.IsNotEmpty())
|
||
|
{
|
||
|
static const char *const steam_dirs[] =
|
||
|
{
|
||
|
"doom 2/base",
|
||
|
"final doom/base",
|
||
|
"heretic shadow of the serpent riders/base",
|
||
|
"hexen/base",
|
||
|
"hexen deathkings of the dark citadel/base",
|
||
|
"ultimate doom/base"
|
||
|
};
|
||
|
steam_path += "/SteamApps/common/";
|
||
|
for (i = 0; i < countof(steam_dirs); ++i)
|
||
|
{
|
||
|
CheckIWAD (steam_path + steam_dirs[i], wads);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
if (iwadparm != NULL && !wads[0].Path.IsEmpty())
|
||
|
{
|
||
|
iwadparmfound = true;
|
||
|
}
|
||
|
|
||
|
for (i = numwads = 0; i < countof(IWADNames); i++)
|
||
|
{
|
||
|
if (!wads[i].Path.IsEmpty())
|
||
|
{
|
||
|
if (i != numwads)
|
||
|
{
|
||
|
wads[numwads] = wads[i];
|
||
|
}
|
||
|
foundwads[wads[numwads].Type] = numwads + 1;
|
||
|
numwads++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (foundwads[IWAD_HexenDK] && !foundwads[IWAD_Hexen])
|
||
|
{ // Cannot play Hexen DK without Hexen
|
||
|
size_t kill = foundwads[IWAD_HexenDK];
|
||
|
for (i = kill; i < numwads; ++i)
|
||
|
{
|
||
|
wads[i - 1] = wads[i];
|
||
|
}
|
||
|
numwads--;
|
||
|
foundwads[IWAD_HexenDK] = 0;
|
||
|
for (i = 0; i < NUM_IWAD_TYPES; ++i)
|
||
|
{
|
||
|
if (foundwads[i] > kill)
|
||
|
{
|
||
|
foundwads[i]--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (numwads == 0)
|
||
|
{
|
||
|
I_FatalError ("Cannot find a game IWAD (doom.wad, doom2.wad, heretic.wad, etc.).\n"
|
||
|
"Did you install ZDoom properly? You can do either of the following:\n"
|
||
|
"\n"
|
||
|
"1. Place one or more of these wads in the same directory as ZDoom.\n"
|
||
|
"2. Edit your zdoom-username.ini and add the directories of your iwads\n"
|
||
|
"to the list beneath [IWADSearch.Directories]");
|
||
|
}
|
||
|
|
||
|
pickwad = 0;
|
||
|
|
||
|
if (!iwadparmfound && numwads > 1)
|
||
|
{
|
||
|
int defiwad = 0;
|
||
|
|
||
|
// Locate the user's prefered IWAD, if it was found.
|
||
|
if (defaultiwad[0] != '\0')
|
||
|
{
|
||
|
for (i = 0; i < numwads; ++i)
|
||
|
{
|
||
|
FString basename = ExtractFileBase (wads[i].Path);
|
||
|
if (stricmp (basename, defaultiwad) == 0)
|
||
|
{
|
||
|
defiwad = (int)i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
pickwad = I_PickIWad (wads, (int)numwads, queryiwad, defiwad);
|
||
|
if (pickwad >= 0)
|
||
|
{
|
||
|
// The newly selected IWAD becomes the new default
|
||
|
FString basename = ExtractFileBase (wads[pickwad].Path);
|
||
|
defaultiwad = basename;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pickwad < 0)
|
||
|
exit (0);
|
||
|
|
||
|
// zdoom.pk3 must always be the first file loaded and the IWAD second.
|
||
|
D_AddFile (zdoom_wad);
|
||
|
|
||
|
if (wads[pickwad].Type == IWAD_HexenDK)
|
||
|
{ // load hexen.wad before loading hexdd.wad
|
||
|
D_AddFile (wads[foundwads[IWAD_Hexen]-1].Path);
|
||
|
}
|
||
|
|
||
|
D_AddFile (wads[pickwad].Path);
|
||
|
|
||
|
if (wads[pickwad].Type == IWAD_Strife)
|
||
|
{ // Try to load voices.wad along with strife1.wad
|
||
|
long lastslash = wads[pickwad].Path.LastIndexOf ('/');
|
||
|
FString path;
|
||
|
|
||
|
if (lastslash == -1)
|
||
|
{
|
||
|
path = "";// wads[pickwad].Path;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
path = FString (wads[pickwad].Path.GetChars(), lastslash + 1);
|
||
|
}
|
||
|
path += "voices.wad";
|
||
|
D_AddFile (path);
|
||
|
}
|
||
|
|
||
|
return wads[pickwad].Type;
|
||
|
}
|
||
|
|
||
|
|
||
|
const IWADInfo *D_FindIWAD(const char *basewad)
|
||
|
{
|
||
|
EIWADType iwadType = IdentifyVersion(basewad);
|
||
|
const IWADInfo *iwad_info = &IWADInfos[iwadType];
|
||
|
I_SetIWADInfo(iwad_info);
|
||
|
return iwad_info;
|
||
|
}
|