gzdoom/src/resourcefiles/file_wad.cpp
Randy Heit 78902b2009 - Fixed: Some dmadds wads used zero-length sprites as placeholders. When you
ran dmadds to combine it with the IWAD's sprites, they would be replaced by
  the IWAD's sprites, so when loading such wads, we should ignore those as
  valid sprites. (Thanks to entryway for finding this.) See 22ventry's
  22sprite.wad for an example.


SVN r1782 (trunk)
2009-09-02 02:17:11 +00:00

393 lines
11 KiB
C++

/*
** file_wad.cpp
**
**---------------------------------------------------------------------------
** Copyright 1998-2009 Randy Heit
** Copyright 2005-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 "resourcefile.h"
#include "cmdlib.h"
#include "v_text.h"
#include "w_wad.h"
//==========================================================================
//
// Wad file
//
//==========================================================================
class FWadFile : public FUncompressedFile
{
bool IsMarker(int lump, const char *marker);
void SetNamespace(const char *startmarker, const char *endmarker, namespace_t space, bool flathack=false);
void SkinHack ();
public:
FWadFile(const char * filename, FileReader *file);
void FindStrifeTeaserVoices ();
bool Open(bool quiet);
};
//==========================================================================
//
// FWadFile::FWadFile
//
// Initializes a WAD file
//
//==========================================================================
FWadFile::FWadFile(const char *filename, FileReader *file) : FUncompressedFile(filename, file)
{
Lumps = NULL;
}
//==========================================================================
//
// Open it
//
//==========================================================================
bool FWadFile::Open(bool quiet)
{
wadinfo_t header;
Reader->Read(&header, sizeof(header));
NumLumps = LittleLong(header.NumLumps);
header.InfoTableOfs = LittleLong(header.InfoTableOfs);
wadlump_t *fileinfo = new wadlump_t[NumLumps];
Reader->Seek (header.InfoTableOfs, SEEK_SET);
Reader->Read (fileinfo, NumLumps * sizeof(wadlump_t));
Lumps = new FUncompressedLump[NumLumps];
if (!quiet) Printf(", %d lumps\n", NumLumps);
for(DWORD i = 0; i < NumLumps; i++)
{
uppercopy (Lumps[i].Name, fileinfo[i].Name);
Lumps[i].Name[8] = 0;
Lumps[i].Owner = this;
Lumps[i].Position = LittleLong(fileinfo[i].FilePos);
Lumps[i].LumpSize = LittleLong(fileinfo[i].Size);
Lumps[i].Namespace = ns_global;
Lumps[i].Flags = 0;
Lumps[i].FullName = NULL;
}
if (!quiet) // don't bother with namespaces here. We won't need them.
{
SetNamespace("S_START", "S_END", ns_sprites);
SetNamespace("F_START", "F_END", ns_flats, true);
SetNamespace("C_START", "C_END", ns_colormaps);
SetNamespace("A_START", "A_END", ns_acslibrary);
SetNamespace("TX_START", "TX_END", ns_newtextures);
SetNamespace("V_START", "V_END", ns_strifevoices);
SetNamespace("HI_START", "HI_END", ns_hires);
SkinHack();
}
delete [] fileinfo;
return true;
}
//==========================================================================
//
// IsMarker
//
// (from BOOM)
//
//==========================================================================
inline bool FWadFile::IsMarker(int lump, const char *marker)
{
if (Lumps[lump].Name[0] == marker[0])
{
return (!strcmp(Lumps[lump].Name, marker) ||
(marker[1] == '_' && !strcmp(Lumps[lump].Name+1, marker)));
}
else return false;
}
//==========================================================================
//
// SetNameSpace
//
// Sets namespace information for the lumps. It always looks for the first
// x_START and the last x_END lump, except when loading flats. In this case
// F_START may be absent and if that is the case all lumps with a size of
// 4096 will be flagged appropriately.
//
//==========================================================================
// This class was supposed to be local in the function but GCC
// does not like that.
struct Marker
{
int markertype;
int index;
};
void FWadFile::SetNamespace(const char *startmarker, const char *endmarker, namespace_t space, bool flathack)
{
bool warned = false;
int numstartmarkers = 0, numendmarkers = 0;
unsigned int i;
TArray<Marker> markers;
for(i = 0; i < NumLumps; i++)
{
if (IsMarker(i, startmarker))
{
Marker m = {0, i };
markers.Push(m);
numstartmarkers++;
}
else if (IsMarker(i, endmarker))
{
Marker m = {1, i };
markers.Push(m);
numendmarkers++;
}
}
if (numstartmarkers == 0)
{
if (numendmarkers == 0) return; // no markers found
Printf(TEXTCOLOR_YELLOW"WARNING: %s marker without corresponding %s found.\n", endmarker, startmarker);
if (flathack)
{
// We have found no F_START but one or more F_END markers.
// mark all lumps before the last F_END marker as potential flats.
int end = markers[markers.Size()-1].index;
for(int i = 0; i < end; i++)
{
if (Lumps[i].LumpSize == 4096)
{
// We can't add this to the flats namespace but
// it needs to be flagged for the texture manager.
DPrintf("Marking %s as potential flat\n", Lumps[i].Name);
Lumps[i].Flags |= LUMPF_MAYBEFLAT;
}
}
}
return;
}
i = 0;
while (i < markers.Size())
{
int start, end;
if (markers[i].markertype != 0)
{
Printf(TEXTCOLOR_YELLOW"WARNING: %s marker without corresponding %s found.\n", endmarker, startmarker);
i++;
continue;
}
start = i++;
// skip over subsequent x_START markers
while (i < markers.Size() && markers[i].markertype == 0)
{
Printf(TEXTCOLOR_YELLOW"WARNING: duplicate %s marker found.\n", startmarker);
i++;
continue;
}
// same for x_END markers
while (i < markers.Size()-1 && (markers[i].markertype == 1 && markers[i+1].markertype == 1))
{
Printf(TEXTCOLOR_YELLOW"WARNING: duplicate %s marker found.\n", endmarker);
i++;
continue;
}
// We found a starting marker but no end marker. Ignore this block.
if (i >= markers.Size())
{
Printf(TEXTCOLOR_YELLOW"WARNING: %s marker without corresponding %s found.\n", startmarker, endmarker);
end = NumLumps;
}
else
{
end = markers[i++].index;
}
// we found a marked block
DPrintf("Found %s block at (%d-%d)\n", startmarker, markers[start].index, end);
for(int j = markers[start].index + 1; j < end; j++)
{
if (Lumps[j].Namespace != ns_global)
{
if (!warned)
{
Printf(TEXTCOLOR_YELLOW"WARNING: Overlapping namespaces found (lump %d)\n", j);
}
warned = true;
}
else if (space == ns_sprites && Lumps[j].LumpSize < 8)
{
// sf 26/10/99:
// ignore sprite lumps smaller than 8 bytes (the smallest possible)
// in size -- this was used by some dmadds wads
// as an 'empty' graphics resource
DPrintf(" Skipped empty sprite %s (lump %d)\n", Lumps[j].Name, j);
}
else
{
Lumps[j].Namespace = space;
}
}
}
}
//==========================================================================
//
// 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 FWadFile::SkinHack ()
{
static int namespc = ns_firstskin;
bool skinned = false;
bool hasmap = false;
DWORD i;
for (i = 0; i < NumLumps; i++)
{
FResourceLump *lump = &Lumps[i];
if (lump->Name[0] == 'S' &&
lump->Name[1] == '_' &&
lump->Name[2] == 'S' &&
lump->Name[3] == 'K' &&
lump->Name[4] == 'I' &&
lump->Name[5] == 'N')
{ // Wad has at least one skin.
lump->Name[6] = lump->Name[7] = 0;
if (!skinned)
{
skinned = true;
DWORD j;
for (j = 0; j < NumLumps; j++)
{
Lumps[j].Namespace = namespc;
}
namespc++;
}
}
if (lump->Name[0] == 'M' &&
lump->Name[1] == 'A' &&
lump->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",
Filename);
}
}
//==========================================================================
//
// 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 FWadFile::FindStrifeTeaserVoices ()
{
for (DWORD i = 0; i <= NumLumps; ++i)
{
if (Lumps[i].Name[0] == 'V' &&
Lumps[i].Name[1] == 'O' &&
Lumps[i].Name[2] == 'C')
{
int j;
for (j = 3; j < 8; ++j)
{
if (Lumps[i].Name[j] != 0 && !isdigit(Lumps[i].Name[j]))
break;
}
if (j == 8)
{
Lumps[i].Namespace = ns_strifevoices;
}
}
}
}
//==========================================================================
//
// File open
//
//==========================================================================
FResourceFile *CheckWad(const char *filename, FileReader *file, bool quiet)
{
char head[4];
if (file->GetLength() >= 12)
{
file->Seek(0, SEEK_SET);
file->Read(&head, 4);
file->Seek(0, SEEK_SET);
if (!memcmp(head, "IWAD", 4) || !memcmp(head, "PWAD", 4))
{
FResourceFile *rf = new FWadFile(filename, file);
if (rf->Open(quiet)) return rf;
delete rf;
}
}
return NULL;
}