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,