vkdoom_m/src/w_wad.cpp
Randy Heit e815474cbe - Fixed: ACS improperly calculated the address of local variables when
returning from one function to another function when the function that
  was called was used as part of an expression.
- Fixed: Using Thing_Hate with arg0 (hater) set to 0 from an open script
  could crash.
- Fixed: Some items along ledges in Hexen's MAP32 (Orchard of Lamentations)
  appeared at the bottom of the ledge (and consequently inside it) instead
  of on top of it because the items were placed directly on the lines.
  AActor::LinkToWorldForMapThing() needs to use the original R_PointOnLineSide()
  code to handle situations like this. Previously, it just used the original
  code for straight horizontal/vertical lines and used the new code for
  diagonal lines.
- Fixed: FWadCollection::MergeLumps() used in incorrect realloc.
- Fixed: FPlayList::NextLine() did not properly handle blank lines in the
  playlist.
- Changed: Decals now use lightweight thinkers instead of actors. (76 bytes
  versus 396, so you save 320k if you have 1024 decals present.)
- Fixed: Wads added with pullin were loaded immediately after the IWAD.
  Exec files are now processed immediately before -file but after autoloading
  wads in D_DoomMain().
- Fixed: sdl/i_system.h unconditionally defined SHARE_DIR, preventing
  redefinition from the command line.
- Fixed: The standard way to include SDL.h is <SDL.h>, not <SDL/SDL.h>.
- Fixed: Returned FActiveInterpolation::HashKey()'s return type to size_t,
  avoiding a pointer truncation warning.


SVN r30 (trunk)
2006-04-12 01:50:09 +00:00

1465 lines
37 KiB
C++

//**************************************************************************
//**
//** w_wad.c : Heretic 2 : Raven Software, Corp.
//**
//** $RCSfile: w_wad.c,v $
//** $Revision: 1.6 $
//** $Date: 95/10/06 20:56:47 $
//** $Author: cjr $
//**
//**************************************************************************
// HEADER FILES ------------------------------------------------------------
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include "m_alloc.h"
#include "doomtype.h"
#include "m_argv.h"
#include "i_system.h"
#include "cmdlib.h"
#include "w_wad.h"
#include "m_crc32.h"
#include "v_text.h"
#include "templates.h"
#include "gi.h"
// MACROS ------------------------------------------------------------------
#define NULL_INDEX (0xffff)
// TYPES -------------------------------------------------------------------
struct rffinfo_t
{
// Should be "RFF\x18"
DWORD Magic;
DWORD Version;
DWORD DirOfs;
DWORD NumLumps;
};
union MergedHeader
{
DWORD magic;
wadinfo_t wad;
rffinfo_t rff;
};
struct rfflump_t
{
BYTE IDontKnow[16];
DWORD FilePos;
DWORD Size;
BYTE IStillDontKnow[8];
BYTE Flags;
char Extension[3];
char Name[8];
BYTE WhatIsThis[4];
};
//
// WADFILE I/O related stuff.
//
struct FWadCollection::LumpRecord
{
char name[8];
int position;
int size;
int namespc;
short wadnum;
WORD flags;
};
enum
{
LUMPF_BLOODCRYPT = 1 // Lump uses Blood-style encryption
};
class FWadCollection::WadFileRecord : public FileReader
{
public:
WadFileRecord (FILE *file);
~WadFileRecord ();
char *Name;
DWORD FirstLump;
DWORD LastLump;
};
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
void W_SysWadInit ();
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
static void BloodCrypt (void *data, int key, int len);
static void PrintLastError ();
// EXTERNAL DATA DECLARATIONS ----------------------------------------------
// PUBLIC DATA DEFINITIONS -------------------------------------------------
FWadCollection Wads;
// PRIVATE DATA DEFINITIONS ------------------------------------------------
// CODE --------------------------------------------------------------------
//==========================================================================
//
// strupr
//
// Suprisingly, glibc lacks this
//==========================================================================
#ifndef HAVE_STRUPR
void strupr (char *s)
{
while (*s)
{
*s = toupper (*s);
s++;
}
}
#endif
//==========================================================================
//
// filelength
//
// Suprisingly, glibc lacks this
//==========================================================================
#ifndef HAVE_FILELENGTH
int filelength (int handle)
{
struct stat fileinfo;
if (fstat (handle, &fileinfo) == -1)
{
close (handle);
I_Error ("Error fstating");
}
return fileinfo.st_size;
}
#endif
//==========================================================================
//
// uppercoppy
//
// [RH] Copy up to 8 chars, upper-casing them in the process
//==========================================================================
void uppercopy (char *to, const char *from)
{
int i;
for (i = 0; i < 8 && from[i]; i++)
to[i] = toupper (from[i]);
for (; i < 8; i++)
to[i] = 0;
}
FWadCollection::FWadCollection ()
: FirstLumpIndex(NULL), NextLumpIndex(NULL), LumpInfo(NULL), Wads(NULL),
NumLumps(0), NumWads(0)
{
}
//==========================================================================
//
// W_InitMultipleFiles
//
// Pass a null terminated list of files to use. All files are optional,
// but at least one file must be found. Lump names can appear multiple
// times. The name searcher looks backwards, so a later file can
// override an earlier one.
//
//==========================================================================
void FWadCollection::InitMultipleFiles (wadlist_t **filenames)
{
int numfiles;
// open all the files, load headers, and count lumps
numfiles = 0;
NumWads = 0;
NumLumps = 0;
LumpInfo = NULL; // will be realloced as lumps are added
while (*filenames)
{
wadlist_t *next = (*filenames)->next;
int baselump = NumLumps;
char name[PATH_MAX];
// [RH] Automatically append .wad extension if none is specified.
strcpy (name, (*filenames)->name);
FixPathSeperator (name);
DefaultExtension (name, ".wad");
AddFile (name);
free (*filenames);
*filenames = next;
// The first two files are always zdoom.wad and the IWAD, which
// do not contain skins.
if (++numfiles > 2)
SkinHack (baselump);
}
if (NumLumps == 0)
{
I_FatalError ("W_InitMultipleFiles: no files found");
}
// [RH] Merge sprite and flat groups.
// (We don't need to bother with patches, since
// Doom doesn't use markers to identify them.)
RenameSprites (MergeLumps ("S_START", "S_END", ns_sprites));
MergeLumps ("F_START", "F_END", ns_flats);
MergeLumps ("C_START", "C_END", ns_colormaps);
MergeLumps ("A_START", "A_END", ns_acslibrary);
MergeLumps ("TX_START", "TX_END", ns_newtextures);
MergeLumps ("V_START", "V_END", ns_strifevoices);
// [RH] Set up hash table
FirstLumpIndex = new WORD[NumLumps];
NextLumpIndex = new WORD[NumLumps];
InitHashChains ();
}
//==========================================================================
//
// W_AddFile
//
// Files with a .wad extension are wadlink files with multiple lumps,
// other files are single lumps with the base filename for the lump name.
//
// [RH] Removed reload hack
//==========================================================================
void FWadCollection::AddFile (const char *filename)
{
WadFileRecord *wadinfo;
MergedHeader header;
LumpRecord* lump_p;
unsigned i;
FILE* handle;
int startlump;
wadlump_t* fileinfo = NULL, *fileinfo2free = NULL;
wadlump_t singleinfo;
// open the file and add to directory
handle = fopen (filename, "rb");
if (handle == NULL)
{ // Didn't find file
Printf (TEXTCOLOR_RED " couldn't open %s\n", filename);
PrintLastError ();
return;
}
Printf (" adding %s", filename);
startlump = NumLumps;
wadinfo = new WadFileRecord (handle);
// [RH] Determine if file is a WAD based on its signature, not its name.
if (wadinfo->Read (&header, sizeof(header)) == 0)
{
Printf (TEXTCOLOR_RED " couldn't read %s\n", filename);
PrintLastError ();
delete wadinfo;
return;
}
wadinfo->Name = copystring (filename);
if (header.magic == IWAD_ID || header.magic == PWAD_ID)
{ // This is a WAD file
header.wad.NumLumps = LittleLong(header.wad.NumLumps);
header.wad.InfoTableOfs = LittleLong(header.wad.InfoTableOfs);
fileinfo = fileinfo2free = new wadlump_t[header.wad.NumLumps];
wadinfo->Seek (header.wad.InfoTableOfs, SEEK_SET);
wadinfo->Read (fileinfo, header.wad.NumLumps * sizeof(wadlump_t));
NumLumps += header.wad.NumLumps;
Printf (" (%ld lumps)", header.wad.NumLumps);
}
else if (header.magic == RFF_ID)
{ // This is a Blood RFF file
rfflump_t *lumps, *rff_p;
int skipped = 0;
header.rff.NumLumps = LittleLong(header.rff.NumLumps);
header.rff.DirOfs = LittleLong(header.rff.DirOfs);
lumps = new rfflump_t[header.rff.NumLumps];
wadinfo->Seek (header.rff.DirOfs, SEEK_SET);
wadinfo->Read (lumps, header.rff.NumLumps * sizeof(rfflump_t));
BloodCrypt (lumps, header.rff.DirOfs, header.rff.NumLumps * sizeof(rfflump_t));
NumLumps += header.rff.NumLumps;
LumpInfo = (LumpRecord *)Realloc (LumpInfo, NumLumps*sizeof(LumpRecord));
lump_p = &LumpInfo[startlump];
for (i = 0, rff_p = lumps; i < header.rff.NumLumps; ++i, ++rff_p)
{
if (rff_p->Extension[0] == 'S' && rff_p->Extension[1] == 'F' &&
rff_p->Extension[2] == 'X')
{
lump_p->namespc = ns_bloodsfx;
}
else if (rff_p->Extension[0] == 'R' && rff_p->Extension[1] == 'A' &&
rff_p->Extension[2] == 'W')
{
lump_p->namespc = ns_bloodraw;
}
else
{
//lump_p->namespc = ns_bloodmisc;
--NumLumps;
++skipped;
continue;
}
uppercopy (lump_p->name, rff_p->Name);
lump_p->wadnum = (WORD)NumWads;
lump_p->position = LittleLong(rff_p->FilePos);
lump_p->size = LittleLong(rff_p->Size);
lump_p->flags = (rff_p->Flags & 0x10) >> 4;
lump_p++;
}
delete[] lumps;
if (skipped != 0)
{
LumpInfo = (LumpRecord *)Realloc (LumpInfo, NumLumps*sizeof(LumpRecord));
}
}
else
{ // This is just a single lump file
char name[PATH_MAX];
fileinfo2free = NULL;
fileinfo = &singleinfo;
singleinfo.FilePos = 0;
singleinfo.Size = LittleLong(wadinfo->GetLength());
ExtractFileBase (filename, name);
strupr (name);
strncpy (singleinfo.Name, name, 8);
NumLumps++;
}
Printf ("\n");
// Fill in lumpinfo
if (header.magic != RFF_ID)
{
LumpInfo = (LumpRecord *)Realloc (LumpInfo, NumLumps*sizeof(LumpRecord));
lump_p = &LumpInfo[startlump];
for (i = startlump; i < (unsigned)NumLumps; i++, lump_p++, fileinfo++)
{
// [RH] Convert name to uppercase during copy
uppercopy (lump_p->name, fileinfo->Name);
lump_p->wadnum = (WORD)NumWads;
lump_p->position = LittleLong(fileinfo->FilePos);
lump_p->size = LittleLong(fileinfo->Size);
lump_p->namespc = ns_global;
lump_p->flags = 0;
}
if (fileinfo2free)
{
delete[] fileinfo2free;
}
ScanForFlatHack (startlump);
}
wadinfo->FirstLump = startlump;
wadinfo->LastLump = NumLumps - 1;
Wads = (WadFileRecord **)Realloc (Wads, (++NumWads)*sizeof(WadFileRecord*));
Wads[NumWads-1] = wadinfo;
// [RH] Put the Strife Teaser voices into the voices namespace
if (NumWads == IWAD_FILENUM+1 && gameinfo.gametype == GAME_Strife && gameinfo.flags & GI_SHAREWARE)
{
FindStrifeTeaserVoices ();
}
}
//==========================================================================
//
// W_CheckIfWadLoaded
//
// Returns true if the specified wad is loaded, false otherwise.
// If a fully-qualified path is specified, then the wad must match exactly.
// Otherwise, any wad with that name will work, whatever its path.
//
//==========================================================================
bool FWadCollection::CheckIfWadLoaded (const char *name)
{
unsigned int i;
if (strrchr (name, '/') != NULL)
{
for (i = 0; i < NumWads; ++i)
{
if (stricmp (GetWadFullName (i), name) == 0)
{
return true;
}
}
}
else
{
for (i = 0; i < NumWads; ++i)
{
if (stricmp (GetWadName (i), name) == 0)
{
return true;
}
}
}
return false;
}
//==========================================================================
//
// W_NumLumps
//
//==========================================================================
int FWadCollection::GetNumLumps () const
{
return NumLumps;
}
//==========================================================================
//
// W_CheckNumForName
//
// Returns -1 if name not found. The version with a third parameter will
// look exclusively in the specified wad for the lump.
//
// [RH] Changed to use hash lookup ala BOOM instead of a linear search
// and namespace parameter
//==========================================================================
int FWadCollection::CheckNumForName (const char *name, int space)
{
char uname[8];
WORD i;
if (name == NULL)
{
return -1;
}
uppercopy (uname, name);
i = FirstLumpIndex[LumpNameHash (uname) % NumLumps];
while (i != NULL_INDEX &&
(*(__int64 *)&LumpInfo[i].name != *(__int64 *)&uname ||
LumpInfo[i].namespc != space))
{
i = NextLumpIndex[i];
}
return i != NULL_INDEX ? i : -1;
}
int FWadCollection::CheckNumForName (const char *name, int space, int wadnum)
{
char uname[8];
WORD i;
if (wadnum < 0)
{
return CheckNumForName (name, space);
}
uppercopy (uname, name);
i = FirstLumpIndex[LumpNameHash (uname) % NumLumps];
while (i != NULL_INDEX &&
(*(__int64 *)&LumpInfo[i].name != *(__int64 *)&uname ||
LumpInfo[i].namespc != space ||
LumpInfo[i].wadnum != wadnum))
{
i = NextLumpIndex[i];
}
return i != NULL_INDEX ? i : -1;
}
//==========================================================================
//
// W_GetNumForName
//
// Calls W_CheckNumForName, but bombs out if not found.
//
//==========================================================================
int FWadCollection::GetNumForName (const char *name)
{
int i;
i = CheckNumForName (name);
if (i == -1)
I_Error ("W_GetNumForName: %s not found!", name);
return i;
}
//==========================================================================
//
// W_LumpLength
//
// Returns the buffer size needed to load the given lump.
//
//==========================================================================
int FWadCollection::LumpLength (int lump) const
{
if ((size_t)lump >= NumLumps)
{
I_Error ("W_LumpLength: %i >= NumLumps",lump);
}
return LumpInfo[lump].size;
}
//==========================================================================
//
// GetLumpOffset
//
// Returns the offset from the beginning of the file to the lump.
//
//==========================================================================
int FWadCollection::GetLumpOffset (int lump) const
{
if ((size_t)lump >= NumLumps)
{
I_Error ("W_LumpLength: %i >= NumLumps",lump);
}
return LumpInfo[lump].position;
}
//==========================================================================
//
// W_LumpNameHash
//
// NOTE: s should already be uppercase, in contrast to the BOOM version.
//
// Hash function used for lump names.
// Must be mod'ed with table size.
// Can be used for any 8-character names.
//
//==========================================================================
DWORD FWadCollection::LumpNameHash (const char *s)
{
const DWORD *table = GetCRCTable ();;
DWORD hash = 0xffffffff;
int i;
for (i = 8; i > 0 && *s; --i, ++s)
{
hash = CRC1 (hash, *s, table);
}
return hash ^ 0xffffffff;
}
//==========================================================================
//
// W_InitHashChains
//
// Prepares the lumpinfos for hashing.
// (Hey! This looks suspiciously like something from Boom! :-)
//
//==========================================================================
void FWadCollection::InitHashChains (void)
{
char name[8];
unsigned int i, j;
// Mark all buckets as empty
memset (FirstLumpIndex, 255, NumLumps*sizeof(FirstLumpIndex[0]));
memset (NextLumpIndex, 255, NumLumps*sizeof(FirstLumpIndex[0]));
// Now set up the chains
for (i = 0; i < (unsigned)NumLumps; i++)
{
uppercopy (name, LumpInfo[i].name);
j = LumpNameHash (name) % NumLumps;
NextLumpIndex[i] = FirstLumpIndex[j];
FirstLumpIndex[j] = i;
}
}
//==========================================================================
//
// IsMarker
//
// (from BOOM)
//
//==========================================================================
bool FWadCollection::IsMarker (const FWadCollection::LumpRecord *lump, const char *marker) const
{
if (lump->namespc != ns_global)
{
return false;
}
if (strncmp (lump->name, marker, 8) == 0)
{
// If the previous lump was of the form FF_END and this one is
// of the form F_END, ignore this as a marker
if (marker[2] == 'E' && lump > LumpInfo)
{
if ((lump - 1)->name[0] == *marker &&
strncmp ((lump - 1)->name + 1, marker, 7) == 0)
{
return false;
}
}
return true;
}
// Treat double-character markers the same as single-character markers.
// (So if FF_START appears in the wad, it will be treated as if it is F_START.
// However, TTX_STAR will not be treated the same as TX_START because it
// is not a single-character marker.)
if (marker[1] == '_' &&
lump->name[0] == *marker &&
strncmp (lump->name + 1, marker, 7) == 0)
{
return true;
}
return false;
}
//==========================================================================
//
// ScanForFlatHack
//
// Try to detect wads that add extra flats by sticking an extra F_END
// at the end of the flat list without any corresponding FF_START.
// In other words, fix gothic2.wad.
//
//==========================================================================
void FWadCollection::ScanForFlatHack (int startlump)
{
if (Args.CheckParm ("-noflathack"))
{
return;
}
for (int i = startlump; (DWORD)i < NumLumps; ++i)
{
if (LumpInfo[i].name[0] == 'F')
{
int j;
if (strcmp (LumpInfo[i].name + 1, "_START") == 0 ||
strncmp (LumpInfo[i].name + 1, "F_START", 7) == 0)
{
// If the wad has a F_START/FF_START marker, check for
// a FF_START-flats-FF_END-flats-F_END pattern as seen
// in darkhour.wad. At what point do I stop making hacks
// for wads that are incorrect?
for (i = i + 1; (DWORD)i < NumLumps; ++i)
{
if (LumpInfo[i].name[0] == 'F' && strcmp (LumpInfo[i].name + 1, "F_END") == 0)
{
// Found FF_END
break;
}
if (LumpInfo[i].size != 4096)
{
return;
}
}
if (i < (int)NumLumps)
{
// Look for flats-F_END
for (j = ++i; (DWORD)j < NumLumps; ++j)
{
if (LumpInfo[j].name[0] == 'F' && strcmp (LumpInfo[j].name + 1, "_END") == 0)
{
// Found F_END, so bump all the flats between FF_END/F_END up and move the
// FF_END so it immediately precedes F_END.
if (i != j - 1)
{
for (; i < j; ++i)
{
LumpInfo[i - 1] = LumpInfo[i];
}
--i;
strcpy (LumpInfo[i].name, "FF_END");
LumpInfo[i].size = 0;
LumpInfo[i].namespc = ns_global;
LumpInfo[i].flags = 0;
}
return;
}
if (LumpInfo[j].size != 4096)
{
return;
}
}
}
return;
}
// No need to look for FF_END, because Doom doesn't. One minor
// nitpick: Doom will look for the last F_END; this finds the first
// one if there is more than one in the file. Too bad. If there's
// more than one F_END, this algorithm won't be able to properly
// determine where to put the F_START anyway.
if (strcmp (LumpInfo[i].name + 1, "_END") == 0)
{
// When F_END is found, back up past any lumps of length
// 4096, then insert an F_START marker.
for (j = i - 1; j >= startlump && LumpInfo[j].size == 4096; --j)
{
}
if (j == i - 1)
{
// Oh no! There are no flats immediately before F_END. Maybe they are
// at the beginning of the wad (e.g. slipgate.wad).
for (j = startlump; LumpInfo[j].size == 4096; ++j)
{
}
if (j > startlump)
{
// Okay, there are probably flats at the beginning of the wad.
// Move the F_END marker so it immediately follows them, and
// then add an F_START marker at the start of the wad.
for (; i > j; --i)
{
LumpInfo[i] = LumpInfo[i-1];
}
strcpy (LumpInfo[j].name, "F_END");
LumpInfo[j].size = 0;
LumpInfo[j].namespc = ns_global;
LumpInfo[j].flags = 0;
j = startlump - 1;
}
else
{
// Oh well. There won't be any flats loaded from this wad, I guess.
j = i - 1;
}
}
++NumLumps;
LumpInfo = (LumpRecord *)Realloc (LumpInfo, NumLumps*sizeof(LumpRecord));
for (; i > j; --i)
{
LumpInfo[i+1] = LumpInfo[i];
}
++i;
strcpy (LumpInfo[i].name, "F_START");
LumpInfo[i].size = 0;
LumpInfo[i].namespc = ns_global;
LumpInfo[i].flags = 0;
return;
}
}
}
}
//==========================================================================
//
// RenameSprites
//
// Renames sprites in IWADs so that unique actors can have unique sprites,
// making it possible to import any actor from any game into any other
// game without jumping through hoops to resolve duplicate sprite names.
// You just need to know what the sprite's new name is.
//
//==========================================================================
void FWadCollection::RenameSprites (int startlump)
{
bool renameAll;
static const DWORD HereticRenames[] =
{ MAKE_ID('H','E','A','D'), MAKE_ID('L','I','C','H'), // Ironlich
};
static const DWORD HexenRenames[] =
{ MAKE_ID('B','A','R','L'), MAKE_ID('Z','B','A','R'), // ZBarrel
MAKE_ID('A','R','M','1'), MAKE_ID('A','R','_','1'), // MeshArmor
MAKE_ID('A','R','M','2'), MAKE_ID('A','R','_','2'), // FalconShield
MAKE_ID('A','R','M','3'), MAKE_ID('A','R','_','3'), // PlatinumHelm
MAKE_ID('A','R','M','4'), MAKE_ID('A','R','_','4'), // AmuletOfWarding
MAKE_ID('S','U','I','T'), MAKE_ID('Z','S','U','I'), // ZSuitOfArmor and ZArmorChunk
MAKE_ID('T','R','E','1'), MAKE_ID('Z','T','R','E'), // ZTree and ZTreeDead
MAKE_ID('T','R','E','2'), MAKE_ID('T','R','E','S'), // ZTreeSwamp150
MAKE_ID('C','A','N','D'), MAKE_ID('B','C','A','N'), // ZBlueCandle
MAKE_ID('R','O','C','K'), MAKE_ID('R','O','K','K'), // rocks and dirt in a_debris.cpp
MAKE_ID('W','A','T','R'), MAKE_ID('H','W','A','T'), // Strife also has WATR
MAKE_ID('G','I','B','S'), MAKE_ID('P','O','L','5'), // RealGibs
MAKE_ID('E','G','G','M'), MAKE_ID('P','R','K','M'), // PorkFX
};
static const DWORD StrifeRenames[] =
{ MAKE_ID('M','I','S','L'), MAKE_ID('S','M','I','S'), // lots of places
MAKE_ID('A','R','M','1'), MAKE_ID('A','R','M','3'), // MetalArmor
MAKE_ID('A','R','M','2'), MAKE_ID('A','R','M','4'), // LeatherArmor
MAKE_ID('P','M','A','P'), MAKE_ID('S','M','A','P'), // StrifeMap
MAKE_ID('T','L','M','P'), MAKE_ID('T','E','C','H'), // TechLampSilver and TechLampBrass
MAKE_ID('T','R','E','1'), MAKE_ID('T','R','E','T'), // TreeStub
MAKE_ID('B','A','R','1'), MAKE_ID('B','A','R','C'), // BarricadeColumn
MAKE_ID('S','H','T','2'), MAKE_ID('M','P','U','F'), // MaulerPuff
MAKE_ID('B','A','R','L'), MAKE_ID('B','B','A','R'), // StrifeBurningBarrel
MAKE_ID('T','R','C','H'), MAKE_ID('T','R','H','L'), // SmallTorchLit
MAKE_ID('S','H','R','D'), MAKE_ID('S','H','A','R'), // glass shards
MAKE_ID('B','L','S','T'), MAKE_ID('M','A','U','L'), // Mauler
MAKE_ID('L','O','G','G'), MAKE_ID('L','O','G','W'), // StickInWater
MAKE_ID('V','A','S','E'), MAKE_ID('V','A','Z','E'), // Pot and Pitcher
MAKE_ID('C','N','D','L'), MAKE_ID('K','N','D','L'), // Candle
MAKE_ID('P','O','T','1'), MAKE_ID('M','P','O','T'), // MetalPot
MAKE_ID('S','P','I','D'), MAKE_ID('S','T','L','K'), // Stalker
};
const DWORD *renames;
int numrenames;
switch (gameinfo.gametype)
{
case GAME_Doom:
default:
// Doom's sprites don't get renamed.
return;
case GAME_Heretic:
renames = HereticRenames;
numrenames = sizeof(HereticRenames)/8;
break;
case GAME_Hexen:
renames = HexenRenames;
numrenames = sizeof(HexenRenames)/8;
break;
case GAME_Strife:
renames = StrifeRenames;
numrenames = sizeof(StrifeRenames)/8;
break;
}
renameAll = !!Args.CheckParm ("-oldsprites");
for (DWORD i = startlump + 1;
i < NumLumps &&
*(DWORD *)LumpInfo[i].name != MAKE_ID('S','_','E','N') &&
*(((DWORD *)LumpInfo[i].name) + 1) != MAKE_ID('D',0,0,0);
++i)
{
// Only sprites in the IWAD normally get renamed
if (renameAll || LumpInfo[i].wadnum == IWAD_FILENUM)
{
for (int j = 0; j < numrenames; ++j)
{
if (*(DWORD *)LumpInfo[i].name == renames[j*2])
{
*(DWORD *)LumpInfo[i].name = renames[j*2+1];
}
}
}
}
}
//==========================================================================
//
// MergeLumps
//
// Merge multiple tagged groups into one
// Basically from BOOM, too, although I tried to write it independently.
//
//==========================================================================
int FWadCollection::MergeLumps (const char *start, const char *end, int space)
{
char ustart[8], uend[8];
LumpRecord *newlumpinfos;
int newlumps, oldlumps;
int markerpos = -1;
unsigned int i;
BOOL insideBlock;
uppercopy (ustart, start);
uppercopy (uend, end);
newlumpinfos = new LumpRecord[NumLumps];
newlumps = 0;
oldlumps = 0;
insideBlock = false;
for (i = 0; i < NumLumps; i++)
{
if (!insideBlock)
{
// Check if this is the start of a block
if (IsMarker (LumpInfo + i, ustart))
{
insideBlock = true;
markerpos = i;
// Create start marker if we haven't already
if (!newlumps)
{
newlumps++;
strncpy (newlumpinfos[0].name, ustart, 8);
newlumpinfos[0].wadnum = -1;
newlumpinfos[0].position =
newlumpinfos[0].size = 0;
newlumpinfos[0].namespc = ns_global;
}
}
else
{
// Copy lumpinfo down this list
LumpInfo[oldlumps++] = LumpInfo[i];
}
}
else
{
if (i && LumpInfo[i].wadnum != LumpInfo[i-1].wadnum)
{
// Blocks cannot span multiple files
insideBlock = false;
LumpInfo[oldlumps++] = LumpInfo[i];
}
else if (IsMarker (LumpInfo + i, uend))
{
// It is the end of a block. We'll add the end marker once
// we've processed everything.
insideBlock = false;
}
else
{
newlumpinfos[newlumps] = LumpInfo[i];
newlumpinfos[newlumps++].namespc = space;
}
}
}
// Now copy the merged lumps to the end of the old list
// and create the end marker entry.
if (newlumps)
{
if (size_t(oldlumps + newlumps) >= NumLumps)
LumpInfo = (LumpRecord *)Realloc (LumpInfo, (oldlumps + newlumps + 1) * sizeof(LumpRecord));
memcpy (LumpInfo + oldlumps, newlumpinfos, sizeof(LumpRecord) * newlumps);
markerpos = oldlumps;
NumLumps = oldlumps + newlumps;
strncpy (LumpInfo[NumLumps].name, uend, 8);
LumpInfo[NumLumps].wadnum = -1;
LumpInfo[NumLumps].position =
LumpInfo[NumLumps].size = 0;
LumpInfo[NumLumps].namespc = ns_global;
NumLumps++;
}
delete[] newlumpinfos;
return markerpos;
}
//==========================================================================
//
// FindStrifeTeaserVoices
//
// Strife0.wad does not have the voices between V_START/V_END markers, so
// figure out which lumps are voices based on their names.
//
//==========================================================================
void FWadCollection::FindStrifeTeaserVoices ()
{
for (DWORD i = Wads[IWAD_FILENUM]->FirstLump; i <= Wads[IWAD_FILENUM]->LastLump; ++i)
{
if (LumpInfo[i].name[0] == 'V' &&
LumpInfo[i].name[1] == 'O' &&
LumpInfo[i].name[2] == 'C')
{
int j;
for (j = 3; j < 8; ++j)
{
if (LumpInfo[i].name[j] != 0 && !isdigit(LumpInfo[i].name[j]))
break;
}
if (j == 8)
{
LumpInfo[i].namespc = ns_strifevoices;
}
}
}
}
//==========================================================================
//
// W_FindLump
//
// Find a named lump. Specifically allows duplicates for merging of e.g.
// SNDINFO lumps.
//
//==========================================================================
int FWadCollection::FindLump (const char *name, int *lastlump, bool anyns)
{
char name8[8];
LumpRecord *lump_p;
uppercopy (name8, name);
lump_p = LumpInfo + *lastlump;
while (lump_p < LumpInfo + NumLumps)
{
if ((anyns || lump_p->namespc == ns_global) &&
*(__int64 *)&lump_p->name == *(__int64 *)&name8)
{
int lump = lump_p - LumpInfo;
*lastlump = lump + 1;
return lump;
}
lump_p++;
}
*lastlump = NumLumps;
return -1;
}
//==========================================================================
//
// W_CheckLumpName
//
//==========================================================================
bool FWadCollection::CheckLumpName (int lump, const char *name)
{
if ((size_t)lump >= NumLumps)
return false;
return !strnicmp (LumpInfo[lump].name, name, 8);
}
//==========================================================================
//
// W_GetLumpName
//
//==========================================================================
void FWadCollection::GetLumpName (char *to, int lump) const
{
if ((size_t)lump >= NumLumps)
*to = 0;
else
uppercopy (to, LumpInfo[lump].name);
}
//==========================================================================
//
// GetLumpNamespace
//
//==========================================================================
int FWadCollection::GetLumpNamespace (int lump) const
{
if ((size_t)lump >= NumLumps)
return ns_global;
else
return LumpInfo[lump].namespc;
}
//==========================================================================
//
// W_GetLumpFile
//
//==========================================================================
int FWadCollection::GetLumpFile (int lump) const
{
if ((size_t)lump >= NumLumps)
return -1;
return LumpInfo[lump].wadnum;
}
//==========================================================================
//
// W_ReadLump
//
// Loads the lump into the given buffer, which must be >= W_LumpLength().
//
//==========================================================================
void FWadCollection::ReadLump (int lump, void *dest)
{
FWadLump lumpr = OpenLumpNum (lump);
long size = lumpr.GetLength ();
long numread = lumpr.Read (dest, size);
if (numread != size)
{
I_Error ("W_ReadLump: only read %ld of %ld on lump %i\n",
numread, size, lump);
}
if (LumpInfo[lump].flags & LUMPF_BLOODCRYPT)
{
BloodCrypt (dest, 0, MIN<int> (size, 256));
}
}
//==========================================================================
//
// ReadLump - variant 2
//
// Loads the lump into a newly created buffer and returns it.
//
//==========================================================================
FMemLump FWadCollection::ReadLump (int lump)
{
FWadLump lumpr = OpenLumpNum (lump);
long size = lumpr.GetLength ();
BYTE *dest = new BYTE[size];
long numread = lumpr.Read (dest, size);
if (numread != size)
{
I_Error ("W_ReadLump: only read %ld of %ld on lump %i\n",
numread, size, lump);
}
if (LumpInfo[lump].flags & LUMPF_BLOODCRYPT)
{
BloodCrypt (dest, 0, MIN<int> (size, 256));
}
return FMemLump (dest);
}
//==========================================================================
//
// OpenLumpNum
//
// Returns a copy of the file object for a lump's wad and positions its
// file pointer at the beginning of the lump.
//
//==========================================================================
FWadLump FWadCollection::OpenLumpNum (int lump)
{
LumpRecord *l;
WadFileRecord *wad;
if ((unsigned)lump >= (unsigned)NumLumps)
{
I_Error ("W_MapLumpNum: %u >= NumLumps",lump);
}
l = &LumpInfo[lump];
wad = Wads[l->wadnum];
#if 0
// Blood encryption?
if (writeable || l->flags & LUMPF_BLOODCRYPT)
{
ptr = Malloc (l->size);
NonMappedPointers.insert (ptr);
W_ReadLump (lump, ptr);
return ptr;
}
#endif
wad->Seek (l->position, SEEK_SET);
return FWadLump (*wad, l->size);
}
//==========================================================================
//
// ReopenLumpNum
//
// Similar to OpenLumpNum, except a new, independant file object is created
// for the lump's wad. Use this when you won't read the lump's data all at
// once (e.g. for streaming an Ogg Vorbis stream from a wad as music).
//
//==========================================================================
FWadLump *FWadCollection::ReopenLumpNum (int lump)
{
LumpRecord *l;
WadFileRecord *wad;
FILE *f;
if ((unsigned)lump >= (unsigned)NumLumps)
{
I_Error ("W_MapLumpNum: %u >= NumLumps",lump);
}
l = &LumpInfo[lump];
wad = Wads[l->wadnum];
f = fopen (wad->Name, "rb");
if (f == NULL)
{
I_Error ("Could not reopen %s\n", wad->Name);
}
fseek (f, l->position, SEEK_SET);
return new FWadLump (f, l->size);
}
//==========================================================================
//
// W_GetWadName
//
// Returns the name of the given wad.
//
//==========================================================================
const char *FWadCollection::GetWadName (int wadnum) const
{
const char *name, *slash;
if ((DWORD)wadnum >= NumWads)
{
return NULL;
}
name = Wads[wadnum]->Name;
slash = strrchr (name, '/');
return slash != NULL ? slash+1 : name;
}
//==========================================================================
//
// W_GetWadFullName
//
// Returns the name of the given wad, including any path
//
//==========================================================================
const char *FWadCollection::GetWadFullName (int wadnum) const
{
if ((unsigned int)wadnum >= NumWads)
{
return NULL;
}
return Wads[wadnum]->Name;
}
//==========================================================================
//
// W_SkinHack
//
// Tests a wad file to see if it contains an S_SKIN marker. If it does,
// every lump in the wad is moved into a new namespace. Because skins are
// only supposed to replace player sprites, sounds, or faces, this should
// not be a problem. Yes, there are skins that replace more than that, but
// they are such a pain, and breaking them like this was done on purpose.
// This also renames any S_SKINxx lumps to just S_SKIN.
//
//==========================================================================
void FWadCollection::SkinHack (int baselump)
{
bool skinned = false;
bool hasmap = false;
size_t i;
for (i = baselump; i < NumLumps; i++)
{
if (LumpInfo[i].name[0] == 'S' &&
LumpInfo[i].name[1] == '_' &&
LumpInfo[i].name[2] == 'S' &&
LumpInfo[i].name[3] == 'K' &&
LumpInfo[i].name[4] == 'I' &&
LumpInfo[i].name[5] == 'N')
{ // Wad has at least one skin.
LumpInfo[i].name[6] = LumpInfo[i].name[7] = 0;
if (!skinned)
{
skinned = true;
size_t j;
for (j = baselump; j < NumLumps; j++)
{
// Using the baselump as the namespace is safe, because
// zdoom.wad guarantees the first possible baselump
// passed to this function is a largish number.
LumpInfo[j].namespc = baselump;
}
}
}
if (LumpInfo[i].name[0] == 'M' &&
LumpInfo[i].name[1] == 'A' &&
LumpInfo[i].name[2] == 'P')
{
hasmap = true;
}
}
if (skinned && hasmap)
{
Printf (TEXTCOLOR_BLUE
"The maps in %s will not be loaded because it has a skin.\n"
TEXTCOLOR_BLUE
"You should remove the skin from the wad to play these maps.\n",
Wads[LumpInfo[baselump].wadnum]->Name);
}
}
//==========================================================================
//
// BloodCrypt
//
//==========================================================================
static void BloodCrypt (void *data, int key, int len)
{
int p = (BYTE)key, i;
for (i = 0; i < len; ++i)
{
((BYTE *)data)[i] ^= (unsigned char)(p+(i>>1));
}
}
//==========================================================================
//
// PrintLastError
//
//==========================================================================
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
static void PrintLastError ()
{
char *lpMsgBuf;
FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR)&lpMsgBuf,
0,
NULL
);
Printf (TEXTCOLOR_RED " %s\n", lpMsgBuf);
// Free the buffer.
LocalFree( lpMsgBuf );
}
#else
static void PrintLastError ()
{
Printf (TEXTCOLOR_RED " %s\n", strerror(errno));
}
#endif
// WadFileRecord ------------------------------------------------------------
FWadCollection::WadFileRecord::WadFileRecord (FILE *file)
: FileReader(file), Name(NULL), FirstLump(0), LastLump(0)
{
}
FWadCollection::WadFileRecord::~WadFileRecord ()
{
if (Name != NULL)
{
delete[] Name;
}
}
// FWadLump -----------------------------------------------------------------
FWadLump::FWadLump ()
: FileReader ()
{
}
FWadLump::FWadLump (const FWadLump &copy)
{
File = copy.File;
}
FWadLump::FWadLump (const FileReader &other, long length)
: FileReader (other, length)
{
}
FWadLump::FWadLump (FILE *file, long length)
: FileReader (file, length)
{
}
// FMemLump -----------------------------------------------------------------
FMemLump::FMemLump ()
: Block (NULL)
{
}
#ifdef __GNUC__
FMemLump::FMemLump (const FMemLump &copy)
#else
FMemLump::FMemLump (FMemLump &copy)
#endif
{
Block = copy.Block;
const_cast<FMemLump *>(&copy)->Block = NULL;
}
#ifdef __GNUC__
FMemLump &FMemLump::operator = (const FMemLump &copy)
#else
FMemLump &FMemLump::operator = (FMemLump &copy)
#endif
{
if (Block != NULL)
{
delete[] Block;
}
Block = copy.Block;
const_cast<FMemLump *>(&copy)->Block = NULL;
return *this;
}
FMemLump::FMemLump (BYTE *data)
: Block (data)
{
}
FMemLump::~FMemLump ()
{
if (Block != NULL)
{
delete[] Block;
}
}