mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2025-02-22 03:31:24 +00:00
Added new FS_GetFileList() function to retrieve lists of files (by extension or filter) from both paks and game dirs.
Changed UI_LoadArenas() to use FS_GetFileList(). Exported FS_GetFileList() to game DLL.
This commit is contained in:
parent
a57b260b7b
commit
01d78fcaf7
7 changed files with 542 additions and 3 deletions
|
@ -206,6 +206,7 @@ typedef struct
|
|||
char *(*GameDir) (void);
|
||||
char *(*SaveGameDir) (void);
|
||||
void (*CreatePath) (char *path);
|
||||
char **(*GetFileList) (const char *path, const char *extension, int *num);
|
||||
#endif
|
||||
|
||||
} game_import_t;
|
||||
|
|
262
game/q_shared.c
262
game/q_shared.c
|
@ -1587,6 +1587,195 @@ void Com_PageInMemory (byte *buffer, int size)
|
|||
============================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
=================
|
||||
Q_GlobMatchAfterStar
|
||||
|
||||
From Q2E
|
||||
Like Q_GlobMatch, but match pattern against any final segment of text
|
||||
=================
|
||||
*/
|
||||
static qboolean Q_GlobMatchAfterStar (const char *pattern, const char *text, qboolean caseSensitive){
|
||||
|
||||
const char *p = pattern;
|
||||
const char *t = text;
|
||||
char c1, c2;
|
||||
|
||||
while ((c1 = *p++) == '?' || c1 == '*'){
|
||||
if (c1 == '?' && *t++ == '\0')
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c1 == '\0')
|
||||
return true;
|
||||
|
||||
if (c1 == '\\')
|
||||
c2 = *p;
|
||||
else
|
||||
c2 = c1;
|
||||
|
||||
while (1){
|
||||
if (caseSensitive){
|
||||
if (c1 == '[' || *t == c2){
|
||||
if (Q_GlobMatch(p - 1, t, caseSensitive))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (c1 == '[' || tolower(*t) == tolower(c2)){
|
||||
if (Q_GlobMatch(p - 1, t, caseSensitive))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (*t++ == '\0')
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Q_GlobMatch
|
||||
|
||||
From Q2E
|
||||
Matches the pattern against text.
|
||||
Returns true if matches, false otherwise.
|
||||
|
||||
A match means the entire text is used up in matching.
|
||||
|
||||
In the pattern string, '*' matches any sequence of characters, '?'
|
||||
matches any character, '[SET]' matches any character in the specified
|
||||
set, '[!SET]' matches any character not in the specified set.
|
||||
|
||||
A set is composed of characters or ranges. A range looks like character
|
||||
hyphen character (as in 0-9 or A-Z).
|
||||
[0-9a-zA-Z_] is the set of characters allowed in C identifiers.
|
||||
Any other character in the pattern must be matched exactly.
|
||||
|
||||
To suppress the special syntactic significance of any of '[]*?!-\', and
|
||||
match the character exactly, precede it with a '\'.
|
||||
=================
|
||||
*/
|
||||
qboolean Q_GlobMatch (const char *pattern, const char *text, qboolean caseSensitive){
|
||||
|
||||
const char *p = pattern;
|
||||
const char *t = text;
|
||||
char c1, c2, start, end;
|
||||
qboolean invert;
|
||||
|
||||
while ((c1 = *p++) != '\0'){
|
||||
switch (c1){
|
||||
case '?':
|
||||
if (*t == '\0')
|
||||
return false;
|
||||
else
|
||||
++t;
|
||||
|
||||
break;
|
||||
case '\\':
|
||||
if (caseSensitive){
|
||||
if (*p++ != *t++)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if (tolower(*p++) != tolower(*t++))
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
case '*':
|
||||
return Q_GlobMatchAfterStar(p, t, caseSensitive);
|
||||
|
||||
case '[':
|
||||
c2 = *t++;
|
||||
if (!c2)
|
||||
return false;
|
||||
|
||||
invert = (*p == '!');
|
||||
if (invert)
|
||||
p++;
|
||||
|
||||
c1 = *p++;
|
||||
while (1){
|
||||
start = c1;
|
||||
end = c1;
|
||||
|
||||
if (c1 == '\\'){
|
||||
start = *p++;
|
||||
end = start;
|
||||
}
|
||||
if (c1 == '\0')
|
||||
return false;
|
||||
|
||||
c1 = *p++;
|
||||
if (c1 == '-' && *p != ']'){
|
||||
end = *p++;
|
||||
if (end == '\\')
|
||||
end = *p++;
|
||||
if (end == '\0')
|
||||
return false;
|
||||
|
||||
c1 = *p++;
|
||||
}
|
||||
|
||||
if (caseSensitive){
|
||||
if (c2 >= start && c2 <= end)
|
||||
goto match;
|
||||
}
|
||||
else {
|
||||
if (tolower(c2) >= tolower(start) && tolower(c2) <= tolower(end))
|
||||
goto match;
|
||||
}
|
||||
|
||||
if (c1 == ']')
|
||||
break;
|
||||
}
|
||||
|
||||
if (!invert)
|
||||
return false;
|
||||
|
||||
break;
|
||||
|
||||
match:
|
||||
while (c1 != ']'){
|
||||
if (c1 == '\0')
|
||||
return false;
|
||||
|
||||
c1 = *p++;
|
||||
if (c1 == '\0')
|
||||
return false;
|
||||
else if (c1 == '\\')
|
||||
++p;
|
||||
}
|
||||
|
||||
if (invert)
|
||||
return false;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if (caseSensitive){
|
||||
if (c1 != *t++)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if (tolower(c1) != tolower(*t++))
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (*t == '\0');
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
Q_stricmp
|
||||
=================
|
||||
*/
|
||||
// FIXME: replace all Q_stricmp with Q_strcasecmp
|
||||
int Q_stricmp (char *s1, char *s2)
|
||||
{
|
||||
|
@ -1598,6 +1787,74 @@ int Q_stricmp (char *s1, char *s2)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
Q_strncmp
|
||||
|
||||
From Q2E
|
||||
=================
|
||||
*/
|
||||
int Q_strncmp (const char *string1, const char *string2, int n)
|
||||
{
|
||||
int c1, c2;
|
||||
|
||||
if (string1 == NULL)
|
||||
{
|
||||
if (string2 == NULL)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
else if (string2 == NULL)
|
||||
return 1;
|
||||
|
||||
do
|
||||
{
|
||||
c1 = *string1++;
|
||||
c2 = *string2++;
|
||||
|
||||
if (!n--)
|
||||
return 0; // Strings are equal until end point
|
||||
|
||||
if (c1 != c2)
|
||||
return c1 < c2 ? -1 : 1;
|
||||
} while (c1);
|
||||
|
||||
return 0; // Strings are equal
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
Q_strcmp
|
||||
|
||||
From Q2E
|
||||
=================
|
||||
*/
|
||||
int Q_strcmp (const char *string1, const char *string2)
|
||||
{
|
||||
return Q_strncmp(string1, string2, 99999);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
Q_SortStrcmp
|
||||
|
||||
From Q2E
|
||||
=================
|
||||
*/
|
||||
int Q_SortStrcmp (const char **arg1, const char **arg2)
|
||||
{
|
||||
return Q_strcmp(*arg1, *arg2);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
Q_strncasecmp
|
||||
=================
|
||||
*/
|
||||
int Q_strncasecmp (char *s1, char *s2, int n)
|
||||
{
|
||||
int c1, c2;
|
||||
|
@ -1625,6 +1882,11 @@ int Q_strncasecmp (char *s1, char *s2, int n)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
=================
|
||||
Q_strcasecmp
|
||||
=================
|
||||
*/
|
||||
int Q_strcasecmp (char *s1, char *s2)
|
||||
{
|
||||
return Q_strncasecmp (s1, s2, 99999);
|
||||
|
|
|
@ -445,7 +445,17 @@ void Com_PageInMemory (byte *buffer, int size);
|
|||
|
||||
//=============================================
|
||||
|
||||
// portable case insensitive compare
|
||||
// Matches the pattern against text
|
||||
qboolean Q_GlobMatch (const char *pattern, const char *text, qboolean caseSensitive);
|
||||
|
||||
// portable string compare
|
||||
int Q_strncmp (const char *string1, const char *string2, int n);
|
||||
int Q_strcmp (const char *string1, const char *string2);
|
||||
|
||||
// string compare for qsort calls
|
||||
int Q_SortStrcmp (const char **arg1, const char **arg2);
|
||||
|
||||
// portable case insensitive string compare
|
||||
int Q_stricmp (char *s1, char *s2);
|
||||
int Q_strcasecmp (char *s1, char *s2);
|
||||
int Q_strncasecmp (char *s1, char *s2, int n);
|
||||
|
|
206
qcommon/files.c
206
qcommon/files.c
|
@ -1201,6 +1201,212 @@ char **FS_ListPak (char *find, int *num)
|
|||
return list;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
FS_FindFiles
|
||||
|
||||
Generates a listing of files in the given path with an optional extension.
|
||||
Lists all files if extension is NULL.
|
||||
=================
|
||||
*/
|
||||
char **FS_FindFiles (const char *path, const char *extension, int *num)
|
||||
{
|
||||
fsSearchPath_t *search;
|
||||
fsPack_t *pak;
|
||||
char dir[MAX_OSPATH], ext[16], findName[1024];
|
||||
char *name, **itemFiles, *tmpList[MAX_FIND_FILES], **outList = NULL;
|
||||
int nFound = 0;
|
||||
int i, nItems; // len, extLen;
|
||||
|
||||
memset (tmpList, 0, sizeof(tmpList));
|
||||
|
||||
for (search = fs_searchPaths; search; search = search->next)
|
||||
{
|
||||
if (search->pack) // search inside a pak/pk3 file
|
||||
{
|
||||
pak = search->pack;
|
||||
for (i=0 ; i<pak->numFiles ; i++)
|
||||
{
|
||||
// check path
|
||||
COM_FilePath (pak->files[i].name, dir, sizeof(dir));
|
||||
if ( Q_stricmp((char *)path, dir) )
|
||||
continue;
|
||||
|
||||
// check extension
|
||||
if ( (extension != NULL) && (strlen(extension) > 0) ) {
|
||||
Com_FileExtension(pak->files[i].name, ext, sizeof(ext));
|
||||
if ( Q_stricmp((char *)extension, ext) )
|
||||
continue;
|
||||
}
|
||||
|
||||
// found something
|
||||
name = pak->files[i].name;
|
||||
if (nFound < (MAX_FIND_FILES-1))
|
||||
{
|
||||
if (!FS_ItemInList(name, nFound, tmpList)) // check if already in list
|
||||
{
|
||||
tmpList[nFound] = strdup(name);
|
||||
nFound++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else // search in a directory tree
|
||||
{
|
||||
if ( (extension != NULL) && (strlen(extension) > 0) )
|
||||
Com_sprintf (findName, sizeof(findName), "%s/%s/*.%s", search->path, path, extension);
|
||||
else
|
||||
Com_sprintf (findName, sizeof(findName), "%s/%s/*.*", search->path, path);
|
||||
|
||||
itemFiles = FS_ListFiles(findName, &nItems, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM);
|
||||
|
||||
for (i=0; i < nItems; i++)
|
||||
{
|
||||
if (!itemFiles || !itemFiles[i])
|
||||
continue;
|
||||
|
||||
// check extension
|
||||
/* if ( (extension != NULL) && (strlen(extension) > 0) ) {
|
||||
len = (int)strlen(itemFiles[i]);
|
||||
extLen = (int)strlen(extension);
|
||||
if ( strcmp(itemFiles[i]+max(len-(extLen+1),0), va(".%s", extension)) )
|
||||
continue;
|
||||
}*/
|
||||
|
||||
// found something
|
||||
name = itemFiles[i] + strlen(search->path) + 1; // skip over search path and /
|
||||
if (nFound < (MAX_FIND_FILES-1))
|
||||
{
|
||||
if (!FS_ItemInList(name, nFound, tmpList)) // check if already in list
|
||||
{
|
||||
tmpList[nFound] = strdup(name);
|
||||
nFound++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nItems)
|
||||
FS_FreeFileList (itemFiles, nItems);
|
||||
}
|
||||
}
|
||||
|
||||
// sort the list
|
||||
qsort(tmpList, nFound, sizeof(char *), Q_SortStrcmp);
|
||||
|
||||
// alloc and copy output list
|
||||
outList = malloc(sizeof(char *) * (nFound+1));
|
||||
memset(outList, 0, sizeof(char *) * (nFound+1));
|
||||
for (i=0; i<nFound; i++) {
|
||||
outList[i] = tmpList[i];
|
||||
}
|
||||
|
||||
if (num)
|
||||
*num = nFound;
|
||||
return outList;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
FS_FilteredFindFiles
|
||||
|
||||
Generates a listing of files that matches the given filter/widlcards.
|
||||
=================
|
||||
*/
|
||||
char **FS_FilteredFindFiles (const char *pattern, int *num)
|
||||
{
|
||||
fsSearchPath_t *search;
|
||||
fsPack_t *pak;
|
||||
char findName[1024];
|
||||
char *name, **itemFiles, *tmpList[MAX_FIND_FILES], **outList = NULL;
|
||||
int nFound = 0;
|
||||
int i, nItems;
|
||||
|
||||
memset (tmpList, 0, sizeof(tmpList));
|
||||
|
||||
for (search = fs_searchPaths; search; search = search->next)
|
||||
{
|
||||
if (search->pack) // search inside a pak/pk3 file
|
||||
{
|
||||
pak = search->pack;
|
||||
for (i=0 ; i<pak->numFiles ; i++)
|
||||
{
|
||||
// match pattern
|
||||
if ( !Q_GlobMatch(pattern, pak->files[i].name, false) )
|
||||
continue;
|
||||
|
||||
// found something
|
||||
name = pak->files[i].name;
|
||||
if (nFound < (MAX_FIND_FILES-1))
|
||||
{
|
||||
if (!FS_ItemInList(name, nFound, tmpList)) // check if already in list
|
||||
{
|
||||
tmpList[nFound] = strdup(name);
|
||||
nFound++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else // search in a directory tree
|
||||
{
|
||||
Com_sprintf (findName, sizeof(findName), "%s/%s", search->path, pattern);
|
||||
itemFiles = FS_ListFiles(findName, &nItems, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM);
|
||||
|
||||
for (i=0; i < nItems; i++)
|
||||
{
|
||||
if (!itemFiles || !itemFiles[i])
|
||||
continue;
|
||||
|
||||
// match pattern
|
||||
if ( !Q_GlobMatch(pattern, itemFiles[i] + strlen(search->path) + 1, false) )
|
||||
continue;
|
||||
|
||||
// found something
|
||||
name = itemFiles[i] + strlen(search->path) + 1; // skip over search path and /
|
||||
if (nFound < (MAX_FIND_FILES-1))
|
||||
{
|
||||
if (!FS_ItemInList(name, nFound, tmpList)) // check if already in list
|
||||
{
|
||||
tmpList[nFound] = strdup(name);
|
||||
nFound++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nItems)
|
||||
FS_FreeFileList (itemFiles, nItems);
|
||||
}
|
||||
}
|
||||
|
||||
// sort the list
|
||||
qsort(tmpList, nFound, sizeof(char *), Q_SortStrcmp);
|
||||
|
||||
// alloc and copy output list
|
||||
outList = malloc(sizeof(char *) * (nFound+1));
|
||||
memset(outList, 0, sizeof(char *) * (nFound+1));
|
||||
for (i=0; i<nFound; i++) {
|
||||
outList[i] = tmpList[i];
|
||||
}
|
||||
|
||||
if (num)
|
||||
*num = nFound;
|
||||
return outList;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
FS_GetFileList
|
||||
|
||||
Generates a listing of files in the given path with the specified optional extension
|
||||
If extension is NULL, retuns all files in the path. Also does filtered search based on wildcards.
|
||||
=================
|
||||
*/
|
||||
char **FS_GetFileList (const char *path, const char *extension, int *num)
|
||||
{
|
||||
// If wildcards are in path, use filtered search instead of extension
|
||||
if ( strchr(path, '*') || strchr(path, '?') || strchr(path, '[') || strchr(path, ']') )
|
||||
return FS_FilteredFindFiles (path, num);
|
||||
else
|
||||
return FS_FindFiles (path, extension, num);
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
FS_Seek
|
||||
|
|
|
@ -834,12 +834,12 @@ void FS_InsertInList (char **list, char *insert, int len, int start);
|
|||
void FS_Dir_f (void);
|
||||
|
||||
void FS_ExecAutoexec (void);
|
||||
int FS_GetFileList (const char *path, const char *extension, char *buffer, int size, fsSearchType_t searchType);
|
||||
|
||||
int FS_LoadFile (char *path, void **buffer);
|
||||
void FS_AddPAKFile (const char *packPath); // add pak file function
|
||||
void FS_AddPK3File (const char *packPath); // add pk3 file function
|
||||
char **FS_ListPak (char *find, int *num); // pak list function
|
||||
char **FS_GetFileList (const char *path, const char *extension, int *num);
|
||||
void FS_SetGamedir (char *dir);
|
||||
char *FS_Gamedir (void);
|
||||
void FS_FreeFile (void *buffer);
|
||||
|
|
|
@ -413,6 +413,7 @@ void SV_InitGameProgs (void)
|
|||
import.GameDir = FS_GameDir;
|
||||
import.SaveGameDir = FS_GameDir;
|
||||
import.CreatePath = FS_CreatePath;
|
||||
import.GetFileList = FS_GetFileList;
|
||||
|
||||
import.SetAreaPortalState = CM_SetAreaPortalState;
|
||||
import.AreasConnected = CM_AreasConnected;
|
||||
|
|
|
@ -218,7 +218,7 @@ void UI_LoadArenas (void)
|
|||
char **arenafiles;
|
||||
char **tmplist = 0;
|
||||
char *path = NULL;
|
||||
char findName[1024];
|
||||
// char findName[1024];
|
||||
char shortname[MAX_TOKEN_CHARS];
|
||||
char longname[MAX_TOKEN_CHARS];
|
||||
char gametypes[MAX_TOKEN_CHARS];
|
||||
|
@ -241,6 +241,63 @@ void UI_LoadArenas (void)
|
|||
tmplist = malloc( sizeof( char * ) * MAX_ARENAS );
|
||||
memset( tmplist, 0, sizeof( char * ) * MAX_ARENAS );
|
||||
|
||||
#if 1
|
||||
arenafiles = FS_GetFileList ("scripts", "arena", &narenas);
|
||||
// arenafiles = FS_GetFileList ("scripts/*.arena", NULL, &narenas);
|
||||
for (i = 0; i < narenas && narenanames < MAX_ARENAS; i++)
|
||||
{
|
||||
if (!arenafiles || !arenafiles[i])
|
||||
continue;
|
||||
|
||||
len = (int)strlen(arenafiles[i]);
|
||||
if ( strcmp(arenafiles[i]+max(len-6,0), ".arena") )
|
||||
continue;
|
||||
|
||||
p = arenafiles[i];
|
||||
|
||||
if (!FS_ItemInList(p, narenanames, tmplist)) // check if already in list
|
||||
{
|
||||
if (UI_ParseArenaFromFile (p, shortname, longname, gametypes, MAX_TOKEN_CHARS))
|
||||
{
|
||||
// Com_sprintf(scratch, sizeof(scratch), MAPLIST_FORMAT, longname, shortname);
|
||||
Com_sprintf(scratch, sizeof(scratch), "%s\n%s", longname, shortname);
|
||||
|
||||
for (j=0; j<NUM_MAPTYPES; j++)
|
||||
type_supported[j] = false;
|
||||
s = gametypes;
|
||||
tok = strdup(COM_Parse (&s));
|
||||
while (s != NULL)
|
||||
{
|
||||
for (j=0; j<NUM_MAPTYPES; j++)
|
||||
{
|
||||
s2 = gametype_names[j].tokens;
|
||||
tok2 = COM_Parse (&s2);
|
||||
while (s2 != NULL) {
|
||||
if ( !Q_strcasecmp(tok, tok2) )
|
||||
type_supported[j] = true;
|
||||
tok2 = COM_Parse (&s2);
|
||||
}
|
||||
}
|
||||
if (tok) free (tok);
|
||||
tok = strdup(COM_Parse(&s));
|
||||
}
|
||||
if (tok) free (tok);
|
||||
|
||||
for (j=0; j<NUM_MAPTYPES; j++)
|
||||
if (type_supported[j]) {
|
||||
ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]] = malloc(strlen(scratch) + 1);
|
||||
// strncpy(ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]], scratch);
|
||||
Q_strncpyz(ui_svr_arena_mapnames[j][ui_svr_arena_nummaps[j]], scratch, strlen(scratch) + 1);
|
||||
ui_svr_arena_nummaps[j]++;
|
||||
}
|
||||
|
||||
// Com_Printf ("UI_LoadArenas: successfully loaded arena file %s: mapname: %s levelname: %s gametypes: %s\n", p, shortname, longname, gametypes);
|
||||
narenanames++;
|
||||
FS_InsertInList(tmplist, p, narenanames, 0); // add to list
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
//
|
||||
// search in searchpaths for .arena files
|
||||
//
|
||||
|
@ -366,6 +423,8 @@ void UI_LoadArenas (void)
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (narenas)
|
||||
FS_FreeFileList (arenafiles, narenas);
|
||||
|
||||
|
|
Loading…
Reference in a new issue