Add lump filtering for archive resources

- Multi-directory archives (e.g. zips) now support filtering lumps
  depending on the loaded IWAD. The search rules are the same as for the
  Autoload entries in the user's ini. For instance, if you are playing
  Doom 2, the following filters will be applied:
  * "filter/doom2/*"
  * "filter/doom/*"
  They will be renamed to strip out the "filter/doom2/" and "filter/doom/"
  parts and will be ordered so they take precedence over any files not
  inside a filter/ directory. Any files inside another filter/ directory
  (e.g. "filter/hexen/*") will be ignored.
This commit is contained in:
Randy Heit 2015-04-03 22:42:22 -05:00
parent fc6f983c13
commit 7b4d6e2f87
7 changed files with 215 additions and 2 deletions

View file

@ -1992,6 +1992,9 @@ static void D_DoomInit()
static void AddAutoloadFiles(const char *group, const char *autoname) static void AddAutoloadFiles(const char *group, const char *autoname)
{ {
LumpFilterGroup = group;
LumpFilterIWAD = autoname;
if (!(gameinfo.flags & GI_SHAREWARE) && !Args->CheckParm("-noautoload")) if (!(gameinfo.flags & GI_SHAREWARE) && !Args->CheckParm("-noautoload"))
{ {
FString file; FString file;

View file

@ -69,3 +69,4 @@ int SinglePlayerClass[MAXPLAYERS];
bool ToggleFullscreen; bool ToggleFullscreen;
int BorderTopRefresh; int BorderTopRefresh;
FString LumpFilterGroup, LumpFilterIWAD;

View file

@ -250,4 +250,7 @@ EXTERN_CVAR (Int, compatflags);
EXTERN_CVAR (Int, compatflags2); EXTERN_CVAR (Int, compatflags2);
extern int i_compatflags, i_compatflags2, ii_compatflags, ii_compatflags2, ib_compatflags; extern int i_compatflags, i_compatflags2, ii_compatflags, ii_compatflags2, ib_compatflags;
// Filters from AddAutoloadFiles(). Used to filter files from archives.
extern FString LumpFilterGroup, LumpFilterIWAD;
#endif #endif

View file

@ -38,7 +38,8 @@
#include "cmdlib.h" #include "cmdlib.h"
#include "w_wad.h" #include "w_wad.h"
#include "doomerrors.h" #include "doomerrors.h"
#include "gi.h"
#include "doomstat.h"
//========================================================================== //==========================================================================
@ -329,6 +330,9 @@ int STACK_ARGS lumpcmp(const void * a, const void * b)
// FResourceFile :: PostProcessArchive // FResourceFile :: PostProcessArchive
// //
// Sorts files by name. // Sorts files by name.
// For files named "filter/<game>/*": Using the same filter rules as config
// autoloading, move them to the end and rename them without the "filter/"
// prefix. Filtered files that don't match are deleted.
// //
//========================================================================== //==========================================================================
@ -336,6 +340,170 @@ void FResourceFile::PostProcessArchive(void *lumps, size_t lumpsize)
{ {
// Entries in archives are sorted alphabetically // Entries in archives are sorted alphabetically
qsort(lumps, NumLumps, lumpsize, lumpcmp); qsort(lumps, NumLumps, lumpsize, lumpcmp);
// Filter out lumps using the same names as the Autoload.* sections
// in the ini file use. We reduce the maximum lump concidered after
// each one so that we don't risk refiltering already filtered lumps.
DWORD max = NumLumps;
max -= FilterLumps(gameinfo.ConfigName, lumps, lumpsize, max);
max -= FilterLumps(LumpFilterGroup, lumps, lumpsize, max);
max -= FilterLumps(LumpFilterIWAD, lumps, lumpsize, max);
JunkLeftoverFilters(lumps, lumpsize, max);
}
//==========================================================================
//
// FResourceFile :: FilterLumps
//
// Finds any lumps between [0,<max>) that match the pattern
// "filter/<filtername>/*" and moves them to the end of the lump list.
// Returns the number of lumps moved.
//
//==========================================================================
int FResourceFile::FilterLumps(FString filtername, void *lumps, size_t lumpsize, DWORD max)
{
FString filter;
DWORD start, end;
if (filtername.IsEmpty())
{
return 0;
}
filter << "filter/" << filtername << '/';
if (FindPrefixRange(filter, lumps, lumpsize, max, start, end))
{
void *from = (BYTE *)lumps + start * lumpsize;
// Remove filter prefix from every name
void *lump_p = from;
for (DWORD i = start; i < end; ++i, lump_p = (BYTE *)lump_p + lumpsize)
{
FResourceLump *lump = (FResourceLump *)lump_p;
assert(lump->FullName.CompareNoCase(filter, (int)filter.Len()) == 0);
lump->LumpNameSetup(&lump->FullName[filter.Len()]);
}
// Move filtered lumps to the end of the lump list.
size_t count = (end - start) * lumpsize;
void *to = (BYTE *)lumps + NumLumps * lumpsize - count;
assert (to >= from);
if (from != to)
{
// Copy filtered lumps to a temporary buffer.
BYTE *filteredlumps = new BYTE[count];
memcpy(filteredlumps, from, count);
// Shift lumps left to make room for the filtered ones at the end.
memmove(from, (BYTE *)from + count, (NumLumps - end) * lumpsize);
// Copy temporary buffer to newly freed space.
memcpy(to, filteredlumps, count);
delete[] filteredlumps;
}
}
return end - start;
}
//==========================================================================
//
// FResourceFile :: JunkLeftoverFilters
//
// Deletes any lumps beginning with "filter/" that were not matched.
//
//==========================================================================
void FResourceFile::JunkLeftoverFilters(void *lumps, size_t lumpsize, DWORD max)
{
DWORD start, end;
if (FindPrefixRange("filter/", lumps, lumpsize, max, start, end))
{
// Since the resource lumps may contain non-POD data besides the
// full name, we "delete" them by erasing their names so they
// can't be found.
void *stop = (BYTE *)lumps + end * lumpsize;
for (void *p = (BYTE *)lumps + start * lumpsize; p < stop; p = (BYTE *)p + lumpsize)
{
FResourceLump *lump = (FResourceLump *)p;
lump->FullName = 0;
lump->Name[0] = '\0';
lump->Namespace = ns_invalid;
}
}
}
//==========================================================================
//
// FResourceFile :: FindPrefixRange
//
// Finds a range of lumps that start with the prefix string. <start> is left
// indicating the first matching one. <end> is left at one plus the last
// matching one.
//
//==========================================================================
bool FResourceFile::FindPrefixRange(FString filter, void *lumps, size_t lumpsize, DWORD maxlump, DWORD &start, DWORD &end)
{
DWORD min, max, mid, inside;
FResourceLump *lump;
int cmp;
// Pretend that our range starts at 1 instead of 0 so that we can avoid
// unsigned overflow if the range starts at the first lump.
lumps = (BYTE *)lumps - lumpsize;
// Binary search to find any match at all.
min = 1, max = maxlump;
while (min <= max)
{
mid = min + (max - min) / 2;
lump = (FResourceLump *)((BYTE *)lumps + mid * lumpsize);
cmp = lump->FullName.CompareNoCase(filter, (int)filter.Len());
if (cmp == 0)
break;
else if (cmp < 0)
min = mid + 1;
else
max = mid - 1;
}
if (max < min)
{ // matched nothing
return false;
}
// Binary search to find first match.
inside = mid;
min = 1, max = mid;
while (min <= max)
{
mid = min + (max - min) / 2;
lump = (FResourceLump *)((BYTE *)lumps + mid * lumpsize);
cmp = lump->FullName.CompareNoCase(filter, (int)filter.Len());
// Go left on matches and right on misses.
if (cmp == 0)
max = mid - 1;
else
min = mid + 1;
}
start = mid + (cmp != 0) - 1;
// Binary search to find last match.
min = inside, max = maxlump;
while (min <= max)
{
mid = min + (max - min) / 2;
lump = (FResourceLump *)((BYTE *)lumps + mid * lumpsize);
cmp = lump->FullName.CompareNoCase(filter, (int)filter.Len());
// Go right on matches and left on misses.
if (cmp == 0)
min = mid + 1;
else
max = mid - 1;
}
end = mid - (cmp != 0);
return true;
} }
//========================================================================== //==========================================================================

View file

@ -65,9 +65,16 @@ protected:
FResourceFile(const char *filename, FileReader *r); FResourceFile(const char *filename, FileReader *r);
// for archives that can contain directories
void PostProcessArchive(void *lumps, size_t lumpsize);
private: private:
DWORD FirstLump; DWORD FirstLump;
int FilterLumps(FString filtername, void *lumps, size_t lumpsize, DWORD max);
bool FindPrefixRange(FString filter, void *lumps, size_t lumpsize, DWORD max, DWORD &start, DWORD &end);
void JunkLeftoverFilters(void *lumps, size_t lumpsize, DWORD max);
public: public:
static FResourceFile *OpenResourceFile(const char *filename, FileReader *file, bool quiet = false); static FResourceFile *OpenResourceFile(const char *filename, FileReader *file, bool quiet = false);
static FResourceFile *OpenDirectory(const char *filename, bool quiet = false); static FResourceFile *OpenDirectory(const char *filename, bool quiet = false);
@ -76,7 +83,6 @@ public:
DWORD LumpCount() const { return NumLumps; } DWORD LumpCount() const { return NumLumps; }
DWORD GetFirstLump() const { return FirstLump; } DWORD GetFirstLump() const { return FirstLump; }
void SetFirstLump(DWORD f) { FirstLump = f; } void SetFirstLump(DWORD f) { FirstLump = f; }
void PostProcessArchive(void *lumps, size_t lumpsize); // for archives that can contain directories
virtual void FindStrifeTeaserVoices (); virtual void FindStrifeTeaserVoices ();
virtual bool Open(bool quiet) = 0; virtual bool Open(bool quiet) = 0;

View file

@ -1571,3 +1571,33 @@ static void PrintLastError ()
Printf (TEXTCOLOR_RED " %s\n", strerror(errno)); Printf (TEXTCOLOR_RED " %s\n", strerror(errno));
} }
#endif #endif
#ifdef _DEBUG
//==========================================================================
//
// CCMD LumpNum
//
//==========================================================================
CCMD(lumpnum)
{
for (int i = 1; i < argv.argc(); ++i)
{
Printf("%s: %d\n", argv[i], Wads.CheckNumForName(argv[i]));
}
}
//==========================================================================
//
// CCMD LumpNumFull
//
//==========================================================================
CCMD(lumpnumfull)
{
for (int i = 1; i < argv.argc(); ++i)
{
Printf("%s: %d\n", argv[i], Wads.CheckNumForFullName(argv[i]));
}
}
#endif

View file

@ -52,6 +52,8 @@ struct wadlump_t
// [RH] Namespaces from BOOM. // [RH] Namespaces from BOOM.
typedef enum { typedef enum {
ns_invalid = -1,
ns_global = 0, ns_global = 0,
ns_sprites, ns_sprites,
ns_flats, ns_flats,