From 7b4d6e2f8768a5a8c467d4cf22ed6b5cd11bde0f Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Fri, 3 Apr 2015 22:42:22 -0500 Subject: [PATCH] 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. --- src/d_main.cpp | 3 + src/doomstat.cpp | 1 + src/doomstat.h | 3 + src/resourcefiles/resourcefile.cpp | 170 ++++++++++++++++++++++++++++- src/resourcefiles/resourcefile.h | 8 +- src/w_wad.cpp | 30 +++++ src/w_wad.h | 2 + 7 files changed, 215 insertions(+), 2 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index 737622de3..ab2459788 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -1992,6 +1992,9 @@ static void D_DoomInit() static void AddAutoloadFiles(const char *group, const char *autoname) { + LumpFilterGroup = group; + LumpFilterIWAD = autoname; + if (!(gameinfo.flags & GI_SHAREWARE) && !Args->CheckParm("-noautoload")) { FString file; diff --git a/src/doomstat.cpp b/src/doomstat.cpp index 27c50b81e..697ef3afe 100644 --- a/src/doomstat.cpp +++ b/src/doomstat.cpp @@ -69,3 +69,4 @@ int SinglePlayerClass[MAXPLAYERS]; bool ToggleFullscreen; int BorderTopRefresh; +FString LumpFilterGroup, LumpFilterIWAD; diff --git a/src/doomstat.h b/src/doomstat.h index b1784530f..d7f3796ac 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -250,4 +250,7 @@ EXTERN_CVAR (Int, compatflags); EXTERN_CVAR (Int, compatflags2); 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 diff --git a/src/resourcefiles/resourcefile.cpp b/src/resourcefiles/resourcefile.cpp index 181559f85..64fe9b6cf 100644 --- a/src/resourcefiles/resourcefile.cpp +++ b/src/resourcefiles/resourcefile.cpp @@ -38,7 +38,8 @@ #include "cmdlib.h" #include "w_wad.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 // // Sorts files by name. +// For files named "filter//*": 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 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,) that match the pattern +// "filter//*" 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. is left +// indicating the first matching one. 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; } //========================================================================== diff --git a/src/resourcefiles/resourcefile.h b/src/resourcefiles/resourcefile.h index 8fd7366ef..9927b1eae 100644 --- a/src/resourcefiles/resourcefile.h +++ b/src/resourcefiles/resourcefile.h @@ -65,9 +65,16 @@ protected: FResourceFile(const char *filename, FileReader *r); + // for archives that can contain directories + void PostProcessArchive(void *lumps, size_t lumpsize); + private: 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: static FResourceFile *OpenResourceFile(const char *filename, FileReader *file, bool quiet = false); static FResourceFile *OpenDirectory(const char *filename, bool quiet = false); @@ -76,7 +83,6 @@ public: DWORD LumpCount() const { return NumLumps; } DWORD GetFirstLump() const { return FirstLump; } void SetFirstLump(DWORD f) { FirstLump = f; } - void PostProcessArchive(void *lumps, size_t lumpsize); // for archives that can contain directories virtual void FindStrifeTeaserVoices (); virtual bool Open(bool quiet) = 0; diff --git a/src/w_wad.cpp b/src/w_wad.cpp index ccb851a0b..4d0b51623 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -1571,3 +1571,33 @@ static void PrintLastError () Printf (TEXTCOLOR_RED " %s\n", strerror(errno)); } #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 diff --git a/src/w_wad.h b/src/w_wad.h index 4dfe3434d..262a332c6 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -52,6 +52,8 @@ struct wadlump_t // [RH] Namespaces from BOOM. typedef enum { + ns_invalid = -1, + ns_global = 0, ns_sprites, ns_flats,