Index: framework/FileSystem.cpp
===================================================================
--- framework/FileSystem.cpp	(revision 528)
+++ framework/FileSystem.cpp	(working copy)
@@ -159,7 +159,7 @@
 };
 
 // 3 search patch (fs_savepath fs_basepath fs_cdpath)
-// .jpg then .tga for 
+// often .jpg and .tga patterns
 #define MAX_CACHED_DIRS 6
 
 class idDEntry : public idStrList {
@@ -173,6 +173,11 @@
 	void Init(const char *directory, const char *extension, const idStrList &list );
 };
 
+typedef struct {
+	idStr path;
+	idStr OSpath;
+} casematch_t;
+
 class idFileSystem_local : public idFileSystem {
 public:
 						idFileSystem_local( void );
@@ -227,11 +232,14 @@
 	int					numServerPaks;
 	int					serverPaks[MAX_SEARCH_PATHS];
 
-	idDEntry			dir_cache[MAX_CACHED_DIRS]; // fifo
+	idDEntry		dir_cache[MAX_CACHED_DIRS]; // fifo
 	int					dir_cache_index;
 	int					dir_cache_count;
+	
+	idList<casematch_t> dir_case; // match directories in a case insensitive way
 
 private:
+	const char *	CaseSearch(const char *in_dir);
 	void				ReplaceSeparators( idStr &path, char sep = PATHSEPERATOR_CHAR );
 	long				HashFileName( const char *fname ) const;
 	bool				FilenameCompare( const char *s1, const char *s2 );
@@ -1118,6 +1126,89 @@
 
 /*
 ===============
+idFileSystem_local::CaseSearch
+===============
+*/
+const char* idFileSystem_local::CaseSearch(const char *in_dir) {
+	const char *ret;
+	int i, j;
+	// FIXME: go faster with a hash?
+	for( i=0; i<dir_case.Num(); i++ ) {
+		if ( !dir_case[i].path.Cmp( in_dir ) ) {
+			ret = dir_case[i].OSpath.c_str();
+			Com_Printf("index %d: '%s' matched as '%s'\n", i, in_dir, ret);
+			if ( ret[0] == '\0' ) {
+				return NULL;
+			}
+			return ret;
+		}
+	}
+	casematch_t entry;
+	entry.path = in_dir;
+	Com_Printf("CaseSearch not found: '%s'\n", in_dir);
+	// walk down the directory tree searching for a case insensitive match
+	// use StripFilename to bust out the chunks
+	idStrList dirs;
+	idStrList entries;
+	idStr walk_path = in_dir;
+	idStr current_dir;
+	int list_ret;
+	do {
+		walk_path.ExtractFileName(current_dir);
+		dirs.Append(current_dir);
+		walk_path.StripFilename(); // this is double work
+		Com_Printf("have walk_path: %s, current_dir: %s\n", walk_path.c_str(), current_dir.c_str());		
+	} while ( walk_path.Length() && ( list_ret = Sys_ListFiles( walk_path.c_str(), "/", entries ) == -1 ) );
+	// we have walked up to the first directory
+	if ( list_ret == -1 ) {
+		Com_DPrintf("WARNING: didn't find any matching root directory for '%s'\n", in_dir);
+		dir_case.Append(entry);
+		return NULL;
+	}
+	// start walking down and doing matches
+	bool bMatched;
+	entry.OSpath = walk_path;
+	for( i=dirs.Num()-1 ; i>=0; i-- ) {
+		Com_Printf("chunk: %s\n", dirs[i].c_str() );
+		bMatched = false;
+		for( j=0 ; j<entries.Num() ; j++ ) {
+			Com_Printf("match %s and %s\n", dirs[i].c_str(), entries[j].c_str());
+			if ( !dirs[i].Icmp(entries[j]) ) {
+				Com_Printf("we have a match, add this to the path and go down\n");
+				bMatched = true;
+				break; // NOTE: we could keep scanning and detect conflicts?
+			}
+		}
+		// if we didn't match, abort		
+		if (!bMatched) {
+			Com_Printf("no match\n");
+			entry.OSpath = "";
+			dir_case.Append(entry);
+			return NULL;
+		}
+		entry.OSpath += PATHSEPERATOR_STR;
+		entry.OSpath += entries[j];
+		// get the directory list
+		if ( Sys_ListFiles( entry.OSpath.c_str(), "/", entries ) == -1 ) {
+			Com_DPrintf("WARNING: didn't find entries in '%s' after successful icase match\n", entry.OSpath.c_str());
+			entry.OSpath = "";
+			dir_case.Append(entry);
+			return NULL;
+		}		
+	}
+
+	dir_case.Append(entry);
+	ret = dir_case[ dir_case.Num() - 1 ].OSpath.c_str();
+	Com_Printf("case matched '%s' as '%s'\n", in_dir, ret);
+	if ( ret[0] == '\0' ) {
+		Com_DPrintf("WARNING: unexpected empty entry after successful case walk for '%s'\n", in_dir);
+		return NULL;
+	}
+	return ret;
+}
+
+/*
+===============
 idFileSystem_local::ListOSFiles
 
  call to the OS for a listing of files in an OS directory
@@ -1156,10 +1247,19 @@
 	ret = Sys_ListFiles( directory, extension, list );
 
 	if ( ret == -1 ) {
-		return -1;
+		// try a case insensitive directory walk
+		const char *cased_dir = CaseSearch(directory);
+		if (!cased_dir) {
+			return -1;
+		}
+		ret = Sys_ListFiles( cased_dir, extension, list );
+		if ( ret == -1 ) {
+			Com_DPrintf("idFileSystem_local::ListOSFiles: unexpected, Sys_ListFiles failed on case matched directory\n");
+			return -1;
+		}
 	}
 
-	// push a new entry
+	// push a new entry - (if case matched we are caching with the requested directory name)
 	dir_cache[dir_cache_index].Init( directory, extension, list );
 	dir_cache_index = (++dir_cache_index) % MAX_CACHED_DIRS;
 	if ( dir_cache_count < MAX_CACHED_DIRS ) {
@@ -1633,6 +1733,11 @@
 
 	dir_cache_index = 0;
 	dir_cache_count = 0;
+	for ( int i = 0 ; i < MAX_CACHED_DIRS ; i++ ) {
+		dir_cache[i].Clear();
+	}
+	
+	dir_case.Clear();
 
 	// free everything
 	for ( sp = searchPaths; sp; sp = next ) {
Index: sys/linux/ChangeLog
===================================================================
--- sys/linux/ChangeLog	(revision 528)
+++ sys/linux/ChangeLog	(working copy)
@@ -81,6 +81,31 @@
 renderer TODO:
 	- bring up logging capability on *nix (implies non-implicit GL dependency mode)
 
+case insensitive directory walk - NOTES:
+
+wrote the base stuff. keeps a list of attempted/not found dirs. 
+with the case sensitive match if exists
+dumb lookup currently, probably a bit expensive in CPU and mem
+hash table would speed this up
+
+still not sure that's the right way to go with this. I think the client should send
+out warnings when it finds bad cased directory, and in the long run abort
+with an error. That would be the only way to safe keep portability.
+
+trying to find good test case for that stuff
+scary: 300 entries for game/admin?
+design the search tree some other way? binary tree? (yeah why not)
+grep "Couldn't load" run.log  | grep -v nin
+
+WARNING: Couldn't load image: models/mapobjects/tables/udesk/udesk_local
+
+timo@slinky:~/Id/DoomBase.ftpfs/base$ ls models/mapobjects/tables/Udesk/
+udesk.lwo  udesk2.tga      udesk_local.tga  work
+udesk.tga  udesk_base.lwo  udesk_s.tga
+
+Is there an entry point for a directory instead of a file? What happens?
+(i.e. last directory of the path is cased)
+
 idBoneController
 
 2003-05-09 TTimo <ttimo@idsoftware.com>
Index: sys/sys_public.h
===================================================================
--- sys/sys_public.h	(revision 528)
+++ sys/sys_public.h	(working copy)
@@ -137,6 +137,7 @@
 
 // use fs_debug to verbose Sys_ListFiles
 // returns -1 if directory was not found (the list is cleared)
+// if there is a / passed as extension, return directories
 int				Sys_ListFiles( const char *directory, const char *extension, idStrList &list);
 
 // For the mac, we need to explicitly flush vertex buffer areas before using them