source-sdk-2013 icon indicating copy to clipboard operation
source-sdk-2013 copied to clipboard

[Bug] Linux CUtilFilenameSymbolTable conflicts with packed-file casing

Open rtldg opened this issue 9 months ago • 2 comments

Per https://github.com/ValveSoftware/Source-1-Games/issues/6868 loading packed files from BSPs (& VPKs!) has some casing issue that popped up recently for Linux users.

The problem stems from CZipPackFile::GetFileInfo() (and CPackFile?) which is used when trying to open packed files.

Previously (at least in CS:S previous_build) it would do something like this:

CPackFileEntry entry;
entry.hash = HasStringCaselessConventional(filename);
int i = this->files.Find(entry);
if (i != -1)
{
	// return file info here...
	return 1;
}
return 0;

Now it looks like it does something like this:

CPackFileEntry entry;
entry.hash = HasStringCaselessConventional(filename);
int i = this->files.Find(entry);
if (i != -1)
{
	FileNameHandle_t f = this->filesystem_maybe->FindFileName_maybe(filename);
	if (this->files[i].handle == f)
	{
		// return file info here...
		return 1;
	}
}
return 0;

This is making its way to CUtilFilenameSymbolTable::FindFileName() https://github.com/ValveSoftware/source-sdk-2013/blob/aea94b32cbefeba5d16ef6fc70eff9508cf11673/src/tier1/utlsymbol.cpp#L362-L381 where it'll Q_strlower(filename) only on Windows.

So even if the packed file did find a match case-insensitively, the FindFileName() causes it to fail.

(Same with CUtilFilenameSymbolTable::FindOrAddFileName()) https://github.com/ValveSoftware/source-sdk-2013/blob/aea94b32cbefeba5d16ef6fc70eff9508cf11673/src/tier1/utlsymbol.cpp#L312-L332

TL;DR: Remove the #ifdef _WIN32 wrapper around Q_strlower( fn ); in CUtlFilenameSymbolTable::FindFileName and CUtlFilenameSymbolTable::FindOrAddFileName.

rtldg avatar Feb 27 '25 08:02 rtldg

There have been random linux textures loading issues in old versions too (although way less common) and maybe they're also related to CUtilFilenameSymbolTable::FindFileName() for loose files.

rtldg avatar Feb 27 '25 09:02 rtldg

I've stepped through the server in gdb and was able to confirm that the call to FindFileName was causing GetFileInfo to fail.

I've also had two people confirm that patching the client-side FindFileName & FindOrAddFileName (in filesystem_stdio.so) to lowercase does fix texture loading.

(EDIT 2025-03-17: And patching them server-side (in dedicated_srv.so) with a sourcemod plugin fixes some server-side model collisions)

rtldg avatar Mar 07 '25 02:03 rtldg

The root of this issue is CUtlFilenameSymbolTable used to store file names in the filesystem module using CUtlStableHashtable<CUtlConstString> as HashTable, with CUtlStableHashtable's KeyHashT ending up being DefaultHashFunctor<CUtlConstStringBase<char>> (since CUtlConstString is CUtlConstStringBase<char> ), which is StringHashFunctor - a case-sensitive hash function.

A simple fix would be updating CUtlFilenameSymbolTable::HashTable to derive from CUtlStableHashtable< CUtlConstString, empty_t, CaselessStringHashFunctor, CaselessStringEqualFunctor > or CUtlStableHashtable< CUtlConstString, empty_t, CaselessStringHashFunctor, CaselessStringEqualFunctor, uint32 > to bump the max number of strings along the way.

mutezero avatar Aug 04 '25 09:08 mutezero