1 /// \file
2 /// \brief Support to find files
3 ///
4 ///
5 ///	 filesearch:
6 ///
7 ///	 ATTENTION : make sure there is enouth space in filename to put a full path (255 or 512)
8 ///	 if needmd5check == 0 there is no md5 check
9 ///	 if completepath then filename will be change with the full path and name
10 ///	 maxsearchdepth == 0 only search given directory, no subdirs
11 ///	 return FS_NOTFOUND
12 ///	        FS_MD5SUMBAD;
13 ///	        FS_FOUND
14 
15 #include <stdio.h>
16 #ifdef __GNUC__
17 #include <dirent.h>
18 #endif
19 #ifdef _WIN32
20 //#define WIN32_LEAN_AND_MEAN
21 #define RPC_NO_WINDOWS_H
22 #include <windows.h>
23 #endif
24 #include <sys/stat.h>
25 #include <string.h>
26 
27 #include "filesrch.h"
28 #include "d_netfil.h"
29 #include "m_misc.h"
30 #include "z_zone.h"
31 #include "m_menu.h" // Addons_option_Onchange
32 
33 #if defined (_WIN32) && defined (_MSC_VER)
34 
35 #include <errno.h>
36 #include <io.h>
37 #include <tchar.h>
38 
39 #define SUFFIX	"*"
40 #define	SLASH	"\\"
41 #define	S_ISDIR(m)	(((m) & S_IFMT) == S_IFDIR)
42 
43 #ifndef INVALID_FILE_ATTRIBUTES
44 #define INVALID_FILE_ATTRIBUTES	((DWORD)-1)
45 #endif
46 
47 struct dirent
48 {
49 	long		d_ino;		/* Always zero. */
50 	unsigned short	d_reclen;	/* Always zero. */
51 	unsigned short	d_namlen;	/* Length of name in d_name. */
52 	char		d_name[FILENAME_MAX]; /* File name. */
53 };
54 
55 /*
56  * This is an internal data structure. Good programmers will not use it
57  * except as an argument to one of the functions below.
58  * dd_stat field is now int (was short in older versions).
59  */
60 typedef struct
61 {
62 	/* disk transfer area for this dir */
63 	struct _finddata_t	dd_dta;
64 
65 	/* dirent struct to return from dir (NOTE: this makes this thread
66 	 * safe as long as only one thread uses a particular DIR struct at
67 	 * a time) */
68 	struct dirent		dd_dir;
69 
70 	/* _findnext handle */
71 #if _MSC_VER > 1200
72 	intptr_t    dd_handle;
73 #else
74 	long        dd_handle;
75 #endif
76 
77 	/*
78 	 * Status of search:
79 	 *   0 = not started yet (next entry to read is first entry)
80 	 *  -1 = off the end
81 	 *   positive = 0 based index of next entry
82 	 */
83 	int			dd_stat;
84 
85 	/* given path for dir with search pattern (struct is extended) */
86 	CHAR			dd_name[1];
87 } DIR;
88 
89 /*
90  * opendir
91  *
92  * Returns a pointer to a DIR structure appropriately filled in to begin
93  * searching a directory.
94  */
95 
96 DIR *
opendir(const CHAR * szPath)97 opendir (const CHAR *szPath)
98 {
99   DIR *nd;
100   DWORD rc;
101   CHAR szFullPath[MAX_PATH];
102 
103   errno = 0;
104 
105   if (!szPath)
106     {
107       errno = EFAULT;
108       return (DIR *) 0;
109     }
110 
111   if (szPath[0] == '\0')
112     {
113       errno = ENOTDIR;
114       return (DIR *) 0;
115     }
116 
117   /* Attempt to determine if the given path really is a directory. */
118   rc = GetFileAttributesA(szPath);
119   if (rc == INVALID_FILE_ATTRIBUTES)
120     {
121       /* call GetLastError for more error info */
122       errno = ENOENT;
123       return (DIR *) 0;
124     }
125   if (!(rc & FILE_ATTRIBUTE_DIRECTORY))
126     {
127       /* Error, entry exists but not a directory. */
128       errno = ENOTDIR;
129       return (DIR *) 0;
130     }
131 
132   /* Make an absolute pathname.  */
133   _fullpath (szFullPath, szPath, MAX_PATH);
134 
135   /* Allocate enough space to store DIR structure and the complete
136    * directory path given. */
137   nd = (DIR *) malloc (sizeof (DIR) + (strlen(szFullPath) + strlen (SLASH) +
138 			 strlen(SUFFIX) + 1) * sizeof (CHAR));
139 
140   if (!nd)
141     {
142       /* Error, out of memory. */
143       errno = ENOMEM;
144       return (DIR *) 0;
145     }
146 
147   /* Create the search expression. */
148   strcpy (nd->dd_name, szFullPath);
149 
150   /* Add on a slash if the path does not end with one. */
151   if (nd->dd_name[0] != '\0' &&
152       nd->dd_name[strlen (nd->dd_name) - 1] != '/' &&
153       nd->dd_name[strlen (nd->dd_name) - 1] != '\\')
154     {
155       strcat (nd->dd_name, SLASH);
156     }
157 
158   /* Add on the search pattern */
159   strcat (nd->dd_name, SUFFIX);
160 
161   /* Initialize handle to -1 so that a premature closedir doesn't try
162    * to call _findclose on it. */
163   nd->dd_handle = -1;
164 
165   /* Initialize the status. */
166   nd->dd_stat = 0;
167 
168   /* Initialize the dirent structure. ino and reclen are invalid under
169    * Win32, and name simply points at the appropriate part of the
170    * findfirst_t structure. */
171   nd->dd_dir.d_ino = 0;
172   nd->dd_dir.d_reclen = 0;
173   nd->dd_dir.d_namlen = 0;
174   ZeroMemory(nd->dd_dir.d_name, FILENAME_MAX);
175 
176   return nd;
177 }
178 
179 /*
180  * readdir
181  *
182  * Return a pointer to a dirent structure filled with the information on the
183  * next entry in the directory.
184  */
185 struct dirent *
readdir(DIR * dirp)186 readdir (DIR * dirp)
187 {
188   errno = 0;
189 
190   /* Check for valid DIR struct. */
191   if (!dirp)
192     {
193       errno = EFAULT;
194       return (struct dirent *) 0;
195     }
196 
197   if (dirp->dd_stat < 0)
198     {
199       /* We have already returned all files in the directory
200        * (or the structure has an invalid dd_stat). */
201       return (struct dirent *) 0;
202     }
203   else if (dirp->dd_stat == 0)
204     {
205       /* We haven't started the search yet. */
206       /* Start the search */
207       dirp->dd_handle = _findfirst (dirp->dd_name, &(dirp->dd_dta));
208 
209 	  if (dirp->dd_handle == -1)
210 	{
211 	  /* Whoops! Seems there are no files in that
212 	   * directory. */
213 	  dirp->dd_stat = -1;
214 	}
215       else
216 	{
217 	  dirp->dd_stat = 1;
218 	}
219     }
220   else
221     {
222       /* Get the next search entry. */
223       if (_findnext (dirp->dd_handle, &(dirp->dd_dta)))
224 	{
225 	  /* We are off the end or otherwise error.
226 	     _findnext sets errno to ENOENT if no more file
227 	     Undo this. */
228 	  DWORD winerr = GetLastError();
229 	  if (winerr == ERROR_NO_MORE_FILES)
230 	    errno = 0;
231 	  _findclose (dirp->dd_handle);
232 	  dirp->dd_handle = -1;
233 	  dirp->dd_stat = -1;
234 	}
235       else
236 	{
237 	  /* Update the status to indicate the correct
238 	   * number. */
239 	  dirp->dd_stat++;
240 	}
241     }
242 
243   if (dirp->dd_stat > 0)
244     {
245       /* Successfully got an entry. Everything about the file is
246        * already appropriately filled in except the length of the
247        * file name. */
248       dirp->dd_dir.d_namlen = (unsigned short)strlen (dirp->dd_dta.name);
249       strcpy (dirp->dd_dir.d_name, dirp->dd_dta.name);
250       return &dirp->dd_dir;
251     }
252 
253   return (struct dirent *) 0;
254 }
255 
256 /*
257  * rewinddir
258  *
259  * Makes the next readdir start from the beginning.
260  */
261 int
rewinddir(DIR * dirp)262 rewinddir (DIR * dirp)
263 {
264   errno = 0;
265 
266   /* Check for valid DIR struct. */
267   if (!dirp)
268     {
269       errno = EFAULT;
270       return -1;
271     }
272 
273   dirp->dd_stat = 0;
274 
275   return 0;
276 }
277 
278 /*
279  * closedir
280  *
281  * Frees up resources allocated by opendir.
282  */
283 int
closedir(DIR * dirp)284 closedir (DIR * dirp)
285 {
286   int rc;
287 
288   errno = 0;
289   rc = 0;
290 
291   if (!dirp)
292     {
293       errno = EFAULT;
294       return -1;
295     }
296 
297   if (dirp->dd_handle != -1)
298     {
299       rc = _findclose (dirp->dd_handle);
300     }
301 
302   /* Delete the dir structure. */
303   free (dirp);
304 
305   return rc;
306 }
307 #endif
308 
309 static CV_PossibleValue_t addons_cons_t[] = {{0, "Default"},
310 #if 1
311 												{1, "HOME"}, {2, "SRB2"},
312 #endif
313 													{3, "CUSTOM"}, {0, NULL}};
314 
315 consvar_t cv_addons_option = CVAR_INIT ("addons_option", "Default", CV_SAVE|CV_CALL, addons_cons_t, Addons_option_Onchange);
316 consvar_t cv_addons_folder = CVAR_INIT ("addons_folder", "", CV_SAVE, NULL, NULL);
317 
318 static CV_PossibleValue_t addons_md5_cons_t[] = {{0, "Name"}, {1, "Contents"}, {0, NULL}};
319 consvar_t cv_addons_md5 = CVAR_INIT ("addons_md5", "Name", CV_SAVE, addons_md5_cons_t, NULL);
320 
321 consvar_t cv_addons_showall = CVAR_INIT ("addons_showall", "No", CV_SAVE, CV_YesNo, NULL);
322 
323 consvar_t cv_addons_search_case = CVAR_INIT ("addons_search_case", "No", CV_SAVE, CV_YesNo, NULL);
324 
325 static CV_PossibleValue_t addons_search_type_cons_t[] = {{0, "Start"}, {1, "Anywhere"}, {0, NULL}};
326 consvar_t cv_addons_search_type = CVAR_INIT ("addons_search_type", "Anywhere", CV_SAVE, addons_search_type_cons_t, NULL);
327 
328 char menupath[1024];
329 size_t menupathindex[menudepth];
330 size_t menudepthleft = menudepth;
331 
332 char menusearch[MAXSTRINGLENGTH+1];
333 
334 char **dirmenu, **coredirmenu; // core only local for this file
335 size_t sizedirmenu, sizecoredirmenu; // ditto
336 size_t dir_on[menudepth];
337 UINT8 refreshdirmenu = 0;
338 char *refreshdirname = NULL;
339 
340 size_t packetsizetally = 0;
341 size_t mainwadstally = 0;
342 
filesearch(char * filename,const char * startpath,const UINT8 * wantedmd5sum,boolean completepath,int maxsearchdepth)343 filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum, boolean completepath, int maxsearchdepth)
344 {
345 	filestatus_t retval = FS_NOTFOUND;
346 	DIR **dirhandle;
347 	struct dirent *dent;
348 	struct stat fsstat;
349 	int found = 0;
350 	char *searchname = strdup(filename);
351 	int depthleft = maxsearchdepth;
352 	char searchpath[1024];
353 	size_t *searchpathindex;
354 
355 	dirhandle = (DIR**) malloc(maxsearchdepth * sizeof (DIR*));
356 	searchpathindex = (size_t *) malloc(maxsearchdepth * sizeof (size_t));
357 
358 	strcpy(searchpath,startpath);
359 	searchpathindex[--depthleft] = strlen(searchpath) + 1;
360 
361 	dirhandle[depthleft] = opendir(searchpath);
362 
363 	if (dirhandle[depthleft] == NULL)
364 	{
365 		free(searchname);
366 		free(dirhandle);
367 		free(searchpathindex);
368 		return FS_NOTFOUND;
369 	}
370 
371 	if (searchpath[searchpathindex[depthleft]-2] != PATHSEP[0])
372 	{
373 		searchpath[searchpathindex[depthleft]-1] = PATHSEP[0];
374 		searchpath[searchpathindex[depthleft]] = 0;
375 	}
376 	else
377 		searchpathindex[depthleft]--;
378 
379 	while ((!found) && (depthleft < maxsearchdepth))
380 	{
381 		searchpath[searchpathindex[depthleft]]=0;
382 		dent = readdir(dirhandle[depthleft]);
383 
384 		if (!dent)
385 		{
386 			closedir(dirhandle[depthleft++]);
387 			continue;
388 		}
389 
390 		if (dent->d_name[0]=='.' &&
391 				(dent->d_name[1]=='\0' ||
392 					(dent->d_name[1]=='.' &&
393 						dent->d_name[2]=='\0')))
394 		{
395 			// we don't want to scan uptree
396 			continue;
397 		}
398 
399 		// okay, now we actually want searchpath to incorporate d_name
400 		strcpy(&searchpath[searchpathindex[depthleft]],dent->d_name);
401 
402 		if (stat(searchpath,&fsstat) < 0) // do we want to follow symlinks? if not: change it to lstat
403 			; // was the file (re)moved? can't stat it
404 		else if (S_ISDIR(fsstat.st_mode) && depthleft)
405 		{
406 			searchpathindex[--depthleft] = strlen(searchpath) + 1;
407 			dirhandle[depthleft] = opendir(searchpath);
408 			if (!dirhandle[depthleft])
409 			{
410 					// can't open it... maybe no read-permissions
411 					// go back to previous dir
412 					depthleft++;
413 			}
414 
415 			searchpath[searchpathindex[depthleft]-1]=PATHSEP[0];
416 			searchpath[searchpathindex[depthleft]]=0;
417 		}
418 		else if (!strcasecmp(searchname, dent->d_name))
419 		{
420 			switch (checkfilemd5(searchpath, wantedmd5sum))
421 			{
422 				case FS_FOUND:
423 					if (completepath)
424 						strcpy(filename,searchpath);
425 					else
426 						strcpy(filename,dent->d_name);
427 					retval = FS_FOUND;
428 					found = 1;
429 					break;
430 				case FS_MD5SUMBAD:
431 					retval = FS_MD5SUMBAD;
432 					break;
433 				default: // prevent some compiler warnings
434 					break;
435 			}
436 		}
437 	}
438 
439 	for (; depthleft < maxsearchdepth; closedir(dirhandle[depthleft++]));
440 
441 	free(searchname);
442 	free(searchpathindex);
443 	free(dirhandle);
444 
445 	return retval;
446 }
447 
448 char exttable[NUM_EXT_TABLE][7] = { // maximum extension length (currently 4) plus 3 (null terminator, stop, and length including previous two)
449 	"\5.txt", "\5.cfg", // exec
450 	"\5.wad",
451 #ifdef USE_KART
452 	"\6.kart",
453 #endif
454 	"\5.pk3", "\5.soc", "\5.lua"}; // addfile
455 
456 char filenamebuf[MAX_WADFILES][MAX_WADPATH];
457 
458 
filemenucmp(char * haystack,char * needle)459 static boolean filemenucmp(char *haystack, char *needle)
460 {
461 	static char localhaystack[128];
462 	strlcpy(localhaystack, haystack, 128);
463 	if (!cv_addons_search_case.value)
464 		strupr(localhaystack);
465 	if (cv_addons_search_type.value)
466 		return (strstr(localhaystack, needle) != 0);
467 	return (!strncmp(localhaystack, needle, menusearch[0]));
468 }
469 
closefilemenu(boolean validsize)470 void closefilemenu(boolean validsize)
471 {
472 	// search
473 	if (dirmenu)
474 	{
475 		if (dirmenu != coredirmenu)
476 		{
477 			if (dirmenu[0] && ((UINT8)(dirmenu[0][DIR_TYPE]) == EXT_NORESULTS))
478 			{
479 				Z_Free(dirmenu[0]);
480 				dirmenu[0] = NULL;
481 			}
482 			Z_Free(dirmenu);
483 		}
484 		dirmenu = NULL;
485 		sizedirmenu = 0;
486 	}
487 
488 	if (coredirmenu)
489 	{
490 		// core
491 		if (validsize)
492 		{
493 			for (; sizecoredirmenu > 0; sizecoredirmenu--)
494 			{
495 				Z_Free(coredirmenu[sizecoredirmenu-1]);
496 				coredirmenu[sizecoredirmenu-1] = NULL;
497 			}
498 		}
499 		else
500 			sizecoredirmenu = 0;
501 
502 		Z_Free(coredirmenu);
503 		coredirmenu = NULL;
504 	}
505 
506 	if (refreshdirname)
507 		Z_Free(refreshdirname);
508 	refreshdirname = NULL;
509 }
510 
searchfilemenu(char * tempname)511 void searchfilemenu(char *tempname)
512 {
513 	size_t i, first;
514 	char localmenusearch[MAXSTRINGLENGTH] = "";
515 
516 	if (dirmenu)
517 	{
518 		if (dirmenu != coredirmenu)
519 		{
520 			if (dirmenu[0] && ((UINT8)(dirmenu[0][DIR_TYPE]) == EXT_NORESULTS))
521 			{
522 				Z_Free(dirmenu[0]);
523 				dirmenu[0] = NULL;
524 			}
525 			//Z_Free(dirmenu); -- Z_Realloc later tho...
526 		}
527 		else
528 			dirmenu = NULL;
529 	}
530 
531 	first = (((UINT8)(coredirmenu[0][DIR_TYPE]) == EXT_UP) ? 1 : 0); // skip UP...
532 
533 	if (!menusearch[0])
534 	{
535 		if (dirmenu)
536 			Z_Free(dirmenu);
537 		dirmenu = coredirmenu;
538 		sizedirmenu = sizecoredirmenu;
539 
540 		if (tempname)
541 		{
542 			for (i = first; i < sizedirmenu; i++)
543 			{
544 				if (!strcmp(dirmenu[i]+DIR_STRING, tempname))
545 				{
546 					dir_on[menudepthleft] = i;
547 					break;
548 				}
549 			}
550 
551 			if (i == sizedirmenu)
552 				dir_on[menudepthleft] = first;
553 
554 			Z_Free(tempname);
555 		}
556 
557 		return;
558 	}
559 
560 	strcpy(localmenusearch, menusearch+1);
561 	if (!cv_addons_search_case.value)
562 		strupr(localmenusearch);
563 
564 	sizedirmenu = 0;
565 	for (i = first; i < sizecoredirmenu; i++)
566 	{
567 		if (filemenucmp(coredirmenu[i]+DIR_STRING, localmenusearch))
568 			sizedirmenu++;
569 	}
570 
571 	if (!sizedirmenu) // no results...
572 	{
573 		if ((!(dirmenu = Z_Realloc(dirmenu, sizeof(char *), PU_STATIC, NULL)))
574 			|| !(dirmenu[0] = Z_StrDup(va("%c\13No results...", EXT_NORESULTS))))
575 				I_Error("searchfilemenu(): could not create \"No results...\".");
576 		sizedirmenu = 1;
577 		dir_on[menudepthleft] = 0;
578 		if (tempname)
579 			Z_Free(tempname);
580 		return;
581 	}
582 
583 	if (!(dirmenu = Z_Realloc(dirmenu, sizedirmenu*sizeof(char *), PU_STATIC, NULL)))
584 		I_Error("searchfilemenu(): could not reallocate dirmenu.");
585 
586 	sizedirmenu = 0;
587 	for (i = first; i < sizecoredirmenu; i++)
588 	{
589 		if (filemenucmp(coredirmenu[i]+DIR_STRING, localmenusearch))
590 		{
591 			if (tempname && !strcmp(coredirmenu[i]+DIR_STRING, tempname))
592 			{
593 				dir_on[menudepthleft] = sizedirmenu;
594 				Z_Free(tempname);
595 				tempname = NULL;
596 			}
597 			dirmenu[sizedirmenu++] = coredirmenu[i]; // pointer reuse
598 		}
599 	}
600 
601 	if (tempname)
602 	{
603 		dir_on[menudepthleft] = 0; //first; -- can't be first, causes problems
604 		Z_Free(tempname);
605 	}
606 }
607 
preparefilemenu(boolean samedepth)608 boolean preparefilemenu(boolean samedepth)
609 {
610 	DIR *dirhandle;
611 	struct dirent *dent;
612 	struct stat fsstat;
613 	size_t pos = 0, folderpos = 0, numfolders = 0;
614 	char *tempname = NULL;
615 
616 	if (samedepth)
617 	{
618 		if (dirmenu && dirmenu[dir_on[menudepthleft]])
619 			tempname = Z_StrDup(dirmenu[dir_on[menudepthleft]]+DIR_STRING); // don't need to I_Error if can't make - not important, just QoL
620 	}
621 	else
622 		menusearch[0] = menusearch[1] = 0; // clear search
623 
624 	if (!(dirhandle = opendir(menupath))) // get directory
625 	{
626 		closefilemenu(true);
627 		return false;
628 	}
629 
630 	for (; sizecoredirmenu > 0; sizecoredirmenu--) // clear out existing items
631 	{
632 		Z_Free(coredirmenu[sizecoredirmenu-1]);
633 		coredirmenu[sizecoredirmenu-1] = NULL;
634 	}
635 
636 	while (true)
637 	{
638 		menupath[menupathindex[menudepthleft]] = 0;
639 		dent = readdir(dirhandle);
640 
641 		if (!dent)
642 			break;
643 		else if (dent->d_name[0]=='.' &&
644 				(dent->d_name[1]=='\0' ||
645 					(dent->d_name[1]=='.' &&
646 						dent->d_name[2]=='\0')))
647 			continue; // we don't want to scan uptree
648 
649 		strcpy(&menupath[menupathindex[menudepthleft]],dent->d_name);
650 
651 		if (stat(menupath,&fsstat) < 0) // do we want to follow symlinks? if not: change it to lstat
652 			; // was the file (re)moved? can't stat it
653 		else // is a file or directory
654 		{
655 			if (!S_ISDIR(fsstat.st_mode)) // file
656 			{
657 				if (!cv_addons_showall.value)
658 				{
659 					size_t len = strlen(dent->d_name)+1;
660 					UINT8 ext;
661 					for (ext = 0; ext < NUM_EXT_TABLE; ext++)
662 						if (!strcasecmp(exttable[ext]+1, dent->d_name+len-(exttable[ext][0]))) break; // extension comparison
663 					if (ext == NUM_EXT_TABLE) continue; // not an addfile-able (or exec-able) file
664 				}
665 			}
666 			else // directory
667 				numfolders++;
668 
669 			sizecoredirmenu++;
670 		}
671 	}
672 
673 	if (!sizecoredirmenu)
674 	{
675 		closedir(dirhandle);
676 		closefilemenu(false);
677 		if (tempname)
678 			Z_Free(tempname);
679 		return false;
680 	}
681 
682 	if (menudepthleft != menudepth-1) // Make room for UP...
683 	{
684 		sizecoredirmenu++;
685 		numfolders++;
686 		folderpos++;
687 	}
688 
689 	if (dirmenu && dirmenu == coredirmenu)
690 		dirmenu = NULL;
691 
692 	if (!(coredirmenu = Z_Realloc(coredirmenu, sizecoredirmenu*sizeof(char *), PU_STATIC, NULL)))
693 	{
694 		closedir(dirhandle); // just in case
695 		I_Error("preparefilemenu(): could not reallocate coredirmenu.");
696 	}
697 
698 	rewinddir(dirhandle);
699 
700 	while ((pos+folderpos) < sizecoredirmenu)
701 	{
702 		menupath[menupathindex[menudepthleft]] = 0;
703 		dent = readdir(dirhandle);
704 
705 		if (!dent)
706 			break;
707 		else if (dent->d_name[0]=='.' &&
708 				(dent->d_name[1]=='\0' ||
709 					(dent->d_name[1]=='.' &&
710 						dent->d_name[2]=='\0')))
711 			continue; // we don't want to scan uptree
712 
713 		strcpy(&menupath[menupathindex[menudepthleft]],dent->d_name);
714 
715 		if (stat(menupath,&fsstat) < 0) // do we want to follow symlinks? if not: change it to lstat
716 			; // was the file (re)moved? can't stat it
717 		else // is a file or directory
718 		{
719 			char *temp;
720 			size_t len = strlen(dent->d_name)+1;
721 			UINT8 ext = EXT_FOLDER;
722 			UINT8 folder;
723 
724 			if (!S_ISDIR(fsstat.st_mode)) // file
725 			{
726 				if (!((numfolders+pos) < sizecoredirmenu)) continue; // crash prevention
727 				for (; ext < NUM_EXT_TABLE; ext++)
728 					if (!strcasecmp(exttable[ext]+1, dent->d_name+len-(exttable[ext][0]))) break; // extension comparison
729 				if (ext == NUM_EXT_TABLE && !cv_addons_showall.value) continue; // not an addfile-able (or exec-able) file
730 				ext += EXT_START; // moving to be appropriate position
731 
732 				if (ext >= EXT_LOADSTART)
733 				{
734 					size_t i;
735 					for (i = 0; i < numwadfiles; i++)
736 					{
737 						if (!filenamebuf[i][0])
738 						{
739 							strncpy(filenamebuf[i], wadfiles[i]->filename, MAX_WADPATH);
740 							filenamebuf[i][MAX_WADPATH - 1] = '\0';
741 							nameonly(filenamebuf[i]);
742 						}
743 
744 						if (strcmp(dent->d_name, filenamebuf[i]))
745 							continue;
746 						if (cv_addons_md5.value && !checkfilemd5(menupath, wadfiles[i]->md5sum))
747 							continue;
748 
749 						ext |= EXT_LOADED;
750 					}
751 				}
752 				else if (ext == EXT_TXT)
753 				{
754 					if (!strncmp(dent->d_name, "log-", 4) || !strcmp(dent->d_name, "errorlog.txt"))
755 						ext |= EXT_LOADED;
756 				}
757 
758 				if (!strcmp(dent->d_name, configfile))
759 					ext |= EXT_LOADED;
760 
761 				folder = 0;
762 			}
763 			else // directory
764 				len += (folder = 1);
765 
766 			if (len > 255)
767 				len = 255;
768 
769 			if (!(temp = Z_Malloc((len+DIR_STRING+folder) * sizeof (char), PU_STATIC, NULL)))
770 				I_Error("preparefilemenu(): could not create file entry.");
771 			temp[DIR_TYPE] = ext;
772 			temp[DIR_LEN] = (UINT8)(len);
773 			strlcpy(temp+DIR_STRING, dent->d_name, len);
774 			if (folder)
775 			{
776 				strcpy(temp+len, PATHSEP);
777 				coredirmenu[folderpos++] = temp;
778 			}
779 			else
780 				coredirmenu[numfolders + pos++] = temp;
781 		}
782 	}
783 
784 	closedir(dirhandle);
785 
786 	if ((menudepthleft != menudepth-1) // now for UP... entry
787 		&& !(coredirmenu[0] = Z_StrDup(va("%c\5UP...", EXT_UP))))
788 			I_Error("preparefilemenu(): could not create \"UP...\".");
789 
790 	menupath[menupathindex[menudepthleft]] = 0;
791 	sizecoredirmenu = (numfolders+pos); // just in case things shrink between opening and rewind
792 
793 	if (!sizecoredirmenu)
794 	{
795 		dir_on[menudepthleft] = 0;
796 		closefilemenu(false);
797 		return false;
798 	}
799 
800 	searchfilemenu(tempname);
801 
802 	return true;
803 }
804