aboutsummaryrefslogtreecommitdiffstats
path: root/games/kmquake2
diff options
context:
space:
mode:
authoralepulver <alepulver@FreeBSD.org>2006-12-30 21:56:45 +0800
committeralepulver <alepulver@FreeBSD.org>2006-12-30 21:56:45 +0800
commit84b63683708ff239599628b5c798c8bb8afd9c45 (patch)
tree0a08add7e45ea44ba618f887da1b1facf6d70c57 /games/kmquake2
parenta0bfcc5873fd330e2b119e567925fc2870f6e278 (diff)
downloadfreebsd-ports-gnome-84b63683708ff239599628b5c798c8bb8afd9c45.tar.gz
freebsd-ports-gnome-84b63683708ff239599628b5c798c8bb8afd9c45.tar.zst
freebsd-ports-gnome-84b63683708ff239599628b5c798c8bb8afd9c45.zip
- Add support for playing .ogg music files in all search paths.
Obtained from: http://svn.quakedev.com/viewcvs.cgi/qudos/trunk/
Diffstat (limited to 'games/kmquake2')
-rw-r--r--games/kmquake2/Makefile2
-rw-r--r--games/kmquake2/files/patch-client__snd_ogg.c390
-rw-r--r--games/kmquake2/files/patch-client__snd_ogg.h92
-rw-r--r--games/kmquake2/files/patch-qcommon__files.c162
-rw-r--r--games/kmquake2/files/patch-qcommon__qcommon.h10
5 files changed, 654 insertions, 2 deletions
diff --git a/games/kmquake2/Makefile b/games/kmquake2/Makefile
index bd6fa831bec8..ce31a02477e2 100644
--- a/games/kmquake2/Makefile
+++ b/games/kmquake2/Makefile
@@ -7,7 +7,7 @@
PORTNAME= kmquake2
PORTVERSION= 0.19
-PORTREVISION= 2
+PORTREVISION= 3
CATEGORIES= games
MASTER_SITES= http://qudos.quakedev.com/linux/quake2/engines/KMQuake2/:src \
http://qexpo2005.quakedev.com/uploaded/54/:data \
diff --git a/games/kmquake2/files/patch-client__snd_ogg.c b/games/kmquake2/files/patch-client__snd_ogg.c
new file mode 100644
index 000000000000..e02ab92d5ede
--- /dev/null
+++ b/games/kmquake2/files/patch-client__snd_ogg.c
@@ -0,0 +1,390 @@
+--- ./client/snd_ogg.c.orig Sun Apr 9 09:28:58 2006
++++ ./client/snd_ogg.c Sat Dec 30 01:12:08 2006
+@@ -1,4 +1,21 @@
+ /*
++* This program is free software; you can redistribute it and/or
++* modify it under the terms of the GNU General Public License
++* as published by the Free Software Foundation; either version 2
++* of the License, or(at your option) any later version.
++*
++* This program is distributed in the hope that it will be useful,
++* but WITHOUT ANY WARRANTY; without even the implied warranty of
++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
++*
++* See the GNU General Public License for more details.
++*
++* You should have received a copy of the GNU General Public License
++* along with this program; if not, write to the Free Software
++* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++*/
++
++/*
+ ==========================================================
+
+ OGG Vorbis decoding
+@@ -8,12 +25,9 @@
+
+ #include <sys/time.h>
+ #include <errno.h>
+-
+-#include "client.h"
+-
+-#ifdef OGGVORBIS
+ #include <vorbis/vorbisfile.h>
+
++#include "client.h"
+ #include "snd_loc.h"
+ #include "snd_ogg.h"
+
+@@ -22,6 +36,7 @@
+
+ qboolean ogg_first_init = true; /* First initialization flag. */
+ qboolean ogg_started = false; /* Initialization flag. */
++byte *ogg_buffer; /* File buffer. */
+ char **ogg_filelist; /* List of Ogg Vorbis files. */
+ char ovBuf[4096]; /* Buffer for sound. */
+ int ogg_curfile; /* Index of currently played file. */
+@@ -29,6 +44,7 @@
+ int ovSection; /* Position in Ogg Vorbis file. */
+ ogg_status_t ogg_status; /* Status indicator. */
+ cvar_t *ogg_autoplay; /* Play this song when started. */
++cvar_t *ogg_check; /* Check Ogg files or not. */
+ cvar_t *ogg_playlist; /* Playlist. */
+ cvar_t *ogg_sequence; /* Sequence play indicator. */
+ cvar_t *ogg_volume; /* Music volume. */
+@@ -59,6 +75,7 @@
+
+ /* Cvars. */
+ ogg_autoplay = Cvar_Get("ogg_autoplay", "?", CVAR_ARCHIVE);
++ ogg_check = Cvar_Get("ogg_check", "0", CVAR_ARCHIVE);
+ ogg_playlist = Cvar_Get("ogg_playlist", "playlist", CVAR_ARCHIVE);
+ ogg_sequence = Cvar_Get("ogg_sequence", "next", CVAR_ARCHIVE);
+ ogg_volume = Cvar_Get("ogg_volume", "0.7", CVAR_ARCHIVE);
+@@ -67,6 +84,7 @@
+ Cmd_AddCommand("ogg_list", OGG_ListCmd);
+ Cmd_AddCommand("ogg_pause", OGG_PauseCmd);
+ Cmd_AddCommand("ogg_play", OGG_PlayCmd);
++ Cmd_AddCommand("ogg_reinit", OGG_Reinit);
+ Cmd_AddCommand("ogg_resume", OGG_ResumeCmd);
+ Cmd_AddCommand("ogg_seek", OGG_SeekCmd);
+ Cmd_AddCommand("ogg_status", OGG_StatusCmd);
+@@ -82,6 +100,7 @@
+ /* Check if we have Ogg Vorbis files. */
+ if (ogg_numfiles <= 0) {
+ Com_Printf("No Ogg Vorbis files found.\n");
++ ogg_started = true; /* For OGG_Shutdown(). */
+ OGG_Shutdown();
+ return;
+ }
+@@ -89,6 +108,7 @@
+ /* Initialize variables. */
+ if (ogg_first_init) {
+ srand(time(NULL));
++ ogg_buffer = NULL;
+ ogg_curfile = -1;
+ ogg_status = STOP;
+ ogg_first_init = false;
+@@ -112,7 +132,6 @@
+ */
+ void OGG_Shutdown(void)
+ {
+- int i; /* Loop counter. */
+
+ if (!ogg_started)
+ return;
+@@ -122,15 +141,13 @@
+ OGG_Stop();
+
+ /* Free the list of files. */
+- for (i = 0; i < ogg_numfiles; i++)
+- free(ogg_filelist[i]);
+- if (ogg_numfiles > 0)
+- free(ogg_filelist);
++ FS_FreeList(ogg_filelist, ogg_numfiles + 1);
+
+ /* Remove console commands. */
+ Cmd_RemoveCommand("ogg_list");
+ Cmd_RemoveCommand("ogg_pause");
+ Cmd_RemoveCommand("ogg_play");
++ Cmd_RemoveCommand("ogg_reinit");
+ Cmd_RemoveCommand("ogg_resume");
+ Cmd_RemoveCommand("ogg_seek");
+ Cmd_RemoveCommand("ogg_status");
+@@ -162,17 +179,21 @@
+ */
+ qboolean OGG_Check(char *name)
+ {
+- qboolean res; /* Return value. */
+- FILE *fp; /* File pointer. */
+- OggVorbis_File ovf; /* Ogg Vorbis file. */
++ qboolean res; /* Return value. */
++ byte *buffer; /* File buffer. */
++ int size; /* File size. */
++ OggVorbis_File ovf; /* Ogg Vorbis file. */
++
++ if (ogg_check->value == 0)
++ return (true);
+
+ res = false;
+- if ((fp = fopen(name, "r")) != NULL) {
+- if (ov_test(fp, &ovf, NULL, 0) == 0) {
++ if ((size = FS_LoadFile(name, (void**)&buffer)) > 0) {
++ if (ov_test(NULL, &ovf, (char *)buffer, size) == 0) {
+ res = true;
+ ov_clear(&ovf);
+- } else
+- fclose(fp);
++ }
++ FS_FreeFile(buffer);
+ }
+
+ return (res);
+@@ -229,23 +250,15 @@
+ Load list of Ogg Vorbis files in "music".
+ ==========
+ */
+-void FS_FreeList(char **list, int nfiles);
+-char **FS_ListFiles(char *findname, int *numfiles, unsigned musthave, unsigned canthave);
+ void OGG_LoadFileList(void)
+ {
+ char **list; /* List of .ogg files. */
+- char oggpath[MAX_OSPATH]; /* Path to .ogg files. */
+ int i; /* Loop counter. */
+ int j; /* Real position in list. */
+- int length; /* Name length. */
+
+- /* Set search path. */
+- strncpy(oggpath, fs_basedir->string, sizeof(oggpath)-1);
+- oggpath[sizeof(oggpath)-1] = '\0';
+- strncat(oggpath, "/music/*.ogg", sizeof(oggpath)-strlen(oggpath)-1);
+-
+ /* Get file list. */
+- list = FS_ListFiles(oggpath, &ogg_numfiles, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM);
++ list = FS_ListFiles2(va("%s/*.ogg", OGG_DIR), &ogg_numfiles, 0,
++ SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM);
+ ogg_numfiles--;
+
+ /* Check if there are posible Ogg files. */
+@@ -257,17 +270,15 @@
+
+ /* Add valid Ogg Vorbis file to the list. */
+ for (i = 0, j = 0; i < ogg_numfiles; i++) {
+- if (!OGG_Check(list[i]))
++ if (!OGG_Check(list[i])) {
++ free(list[i]);
+ continue;
+- length = strlen(list[j]);
+- ogg_filelist[j] = malloc(length+1);
+- strncpy(ogg_filelist[j], list[j], length);
+- ogg_filelist[j][length] = '\0';
+- j++;
++ }
++ ogg_filelist[j++] = list[i];
+ }
+
+ /* Free the file list. */
+- FS_FreeList(list, ogg_numfiles);
++ free(list);
+
+ /* Adjust the list size (remove space for invalid music files). */
+ ogg_numfiles = j;
+@@ -283,54 +294,40 @@
+ */
+ void OGG_LoadPlaylist(char *playlist)
+ {
+- char *buffer; /* Buffer to read the file. */
+- char *name; /* Name of music file. */
++ byte *buffer; /* Buffer to read the file. */
+ char *ptr; /* Pointer for parsing the file. */
+ int i; /* Loop counter. */
+- int j; /* Real position in list. */
+- int length; /* Length of buffer and strings. */
+- FILE *fp; /* File pointer. */
++ int size; /* Length of buffer and strings. */
+
+ /* Open playlist. */
+- if ((fp = fopen(va("%s/music/%s", fs_basedir->string, playlist), "r")) == NULL) {
++ if ((size = FS_LoadFile(va("%s/%s.lst", OGG_DIR, ogg_playlist->string),
++ (void **)&buffer)) < 0) {
+ Com_Printf("OGG_LoadPlaylist: could not open playlist: %s.\n", strerror(errno));
+ return;
+ }
+
+- /* Read playlist into buffer. */
+- fseek(fp, 0, SEEK_END);
+- length = ftell(fp);
+- fseek(fp, 0, SEEK_SET);
+- buffer = malloc(length);
+- fread(buffer, length, 1, fp);
+-
+ /* Count the files in playlist. */
+- ptr = buffer;
+- while ((name = COM_Parse(&ptr))[0] != '\0')
+- ogg_numfiles++;
++ for (ptr = strtok((char *)buffer, "\n");
++ ptr != NULL;
++ ptr = strtok(NULL, "\n")) {
++ if ((byte *)ptr != buffer)
++ ptr[-1] = '\n';
++ if (OGG_Check(va("%s/%s", OGG_DIR, ptr)))
++ ogg_numfiles++;
++ }
+
+ /* Allocate file list. */
+ ogg_filelist = malloc(sizeof(char *) * ogg_numfiles);
+
+- /* Add valid Ogg Vorbis file to the list. */
+- ptr = buffer;
+- for (i = 0, j = 0; i < ogg_numfiles; i++) {
+- name = va("%s/music/%s.ogg", fs_basedir->string, COM_Parse(&ptr));
+- if (!OGG_Check(name))
+- continue;
+- length = strlen(name);
+- ogg_filelist[j] = malloc(length+1);
+- strncpy(ogg_filelist[j], name, length);
+- ogg_filelist[j][length] = '\0';
+- j++;
+- }
+-
+- /* Adjust the list size (remove space for invalid music files). */
+- ogg_numfiles = j;
+- ogg_filelist = realloc(ogg_filelist, sizeof(char *) * ogg_numfiles);
++ i = 0;
++ for (ptr = strtok((char *)buffer, "\n");
++ ptr != NULL;
++ ptr = strtok(NULL, "\n"))
++ if (OGG_Check(va("%s/%s", OGG_DIR, ptr)))
++ ogg_filelist[i++] = strdup(va("%s/%s", OGG_DIR, ptr));
+
+ /* Free file buffer. */
+- free(buffer);
++ FS_FreeFile(buffer);
+ }
+
+ /*
+@@ -342,9 +339,9 @@
+ */
+ qboolean OGG_Open(ogg_seek_t type, int offset)
+ {
+- int pos; /* Absolute position. */
+- int res; /* Error indicator. */
+- FILE *fp; /* File pointer. */
++ int size; /* File size. */
++ int pos; /* Absolute position. */
++ int res; /* Error indicator. */
+
+ pos = -1;
+
+@@ -378,15 +375,15 @@
+ }
+
+ /* Find file. */
+- if ((fp = fopen(ogg_filelist[pos], "r")) == NULL) {
++ if ((size = FS_LoadFile(ogg_filelist[pos], (void **)&ogg_buffer)) == -1) {
+ Com_Printf("OGG_Open: could not open %d (%s): %s.\n", pos, ogg_filelist[pos], strerror(errno));
+ return (false);
+ }
+
+ /* Open ogg vorbis file. */
+- if ((res = ov_open(fp, &ovFile, NULL, 0)) < 0) {
++ if ((res = ov_open(NULL, &ovFile, (char *)ogg_buffer, size)) < 0) {
+ Com_Printf("OGG_Open: '%s' is not a valid Ogg Vorbis file (error %i).\n", ogg_filelist[pos], res);
+- fclose(fp);
++ FS_FreeFile(ogg_buffer);
+ return (false);
+ }
+
+@@ -395,9 +392,8 @@
+ ogg_curfile = pos;
+ ogg_status = PLAY;
+
+- //OGG_StatusCmd();
+ Com_Printf("Playing file %d '%s'\n", pos, ogg_filelist[pos]);
+-
++
+ return (true);
+ }
+
+@@ -413,7 +409,7 @@
+ char *name; /* File name. */
+ int i; /* Loop counter. */
+
+- name = va("%s/music/%s.ogg", fs_basedir->string, filename);
++ name = va("%s/%s.ogg", OGG_DIR, filename);
+
+ for (i = 0; i < ogg_numfiles; i++)
+ if (strcmp(name, ogg_filelist[i]) == 0)
+@@ -482,11 +478,19 @@
+ Stop playing the current file.
+ ==========
+ */
+-void OGG_Stop( void )
++void OGG_Stop(void)
+ {
+
++ if (ogg_status == STOP)
++ return;
++
+ ov_clear(&ovFile);
+ ogg_status = STOP;
++
++ if (ogg_buffer != NULL) {
++ FS_FreeFile(ogg_buffer);
++ ogg_buffer = NULL;
++ }
+ }
+
+ /*
+@@ -498,6 +502,7 @@
+ */
+ void OGG_Stream(void)
+ {
++
+ if (!ogg_started)
+ return;
+
+@@ -617,9 +622,6 @@
+ {
+ int i;
+
+- if (!ogg_started)
+- return;
+-
+ for (i = 0; i < ogg_numfiles; i++)
+ Com_Printf("%d %s\n", i+1, ogg_filelist[i]);
+
+@@ -687,9 +689,6 @@
+ void OGG_PlayCmd( void )
+ {
+
+- if (!ogg_started)
+- return;
+-
+ if (Cmd_Argc() < 2) {
+ Com_Printf("Usage: ogg_play {filename | #n | ? | >n | <n}\n");
+ return;
+@@ -722,6 +721,9 @@
+ void OGG_SeekCmd(void)
+ {
+
++ if (ogg_status != STOP)
++ return;
++
+ if (Cmd_Argc() < 2) {
+ Com_Printf("Usage: ogg_seek {n | <n | >n}\n");
+ return;
+@@ -760,9 +762,11 @@
+ ogg_curfile+1, ogg_filelist[ogg_curfile], ov_time_tell(&ovFile));
+ break;
+ case STOP:
+- Com_Printf("Stopped file %d (%s).\n",
+- ogg_curfile+1, ogg_filelist[ogg_curfile]);
++ if (ogg_curfile == -1)
++ Com_Printf("Stopped.\n");
++ else
++ Com_Printf("Stopped file %d (%s).\n",
++ ogg_curfile+1, ogg_filelist[ogg_curfile]);
+ break;
+ }
+ }
+-#endif
diff --git a/games/kmquake2/files/patch-client__snd_ogg.h b/games/kmquake2/files/patch-client__snd_ogg.h
new file mode 100644
index 000000000000..0d66828dae77
--- /dev/null
+++ b/games/kmquake2/files/patch-client__snd_ogg.h
@@ -0,0 +1,92 @@
+--- ./client/snd_ogg.h.orig Sun Apr 9 09:21:42 2006
++++ ./client/snd_ogg.h Sat Dec 30 01:12:03 2006
+@@ -1,14 +1,33 @@
+ /*
+-==========================================================
+-
+- OGG Vorbis decoding
+-
+-==========================================================
++* This program is free software; you can redistribute it and/or
++* modify it under the terms of the GNU General Public License
++* as published by the Free Software Foundation; either version 2
++* of the License, or(at your option) any later version.
++*
++* This program is distributed in the hope that it will be useful,
++* but WITHOUT ANY WARRANTY; without even the implied warranty of
++* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
++*
++* See the GNU General Public License for more details.
++*
++* You should have received a copy of the GNU General Public License
++* along with this program; if not, write to the Free Software
++* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
++/*
++ * ==========================================================
++ *
++ * OGG Vorbis decoding
++ *
++ * ==========================================================
++ */
++
+ #ifndef _SND_OGG_H_
+ #define _SND_OGG_H_
+
++#define OGG_DIR "music"
++
+ typedef enum {
+ PLAY,
+ PAUSE,
+@@ -20,29 +39,29 @@
+ REL
+ } ogg_seek_t;
+
+-void OGG_Init(void);
+-void OGG_Shutdown(void);
+-void OGG_Reinit(void);
+-qboolean OGG_Check(char *name);
+-void OGG_Seek(ogg_seek_t type, double offset);
+-void OGG_LoadFileList(void);
+-void OGG_LoadPlaylist(char *name);
+-qboolean OGG_Open(ogg_seek_t type, int offset);
+-qboolean OGG_OpenName(char *filename);
+-int OGG_Read(void);
+-void OGG_Sequence(void);
+-void OGG_Stop(void);
+-void OGG_Stream(void);
+-void S_RawSamplesVol(int samples, int rate, int width, int channels, byte *data, float volume);
++void OGG_Init (void);
++void OGG_Shutdown(void);
++void OGG_Reinit(void);
++qboolean OGG_Check(char *name);
++void OGG_Seek (ogg_seek_t type, double offset);
++void OGG_LoadFileList(void);
++void OGG_LoadPlaylist(char *name);
++qboolean OGG_Open(ogg_seek_t type, int offset);
++qboolean OGG_OpenName(char *filename);
++int OGG_Read (void);
++void OGG_Sequence(void);
++void OGG_Stop (void);
++void OGG_Stream(void);
++void S_RawSamplesVol(int samples, int rate, int width, int channels, byte * data, float volume);
+
+ /* Console commands. */
+
+-void OGG_ListCmd(void);
+-void OGG_ParseCmd(char *arg);
+-void OGG_PauseCmd(void);
+-void OGG_PlayCmd(void);
+-void OGG_ResumeCmd(void);
+-void OGG_SeekCmd(void);
+-void OGG_StatusCmd(void);
++void OGG_ListCmd(void);
++void OGG_ParseCmd(char *arg);
++void OGG_PauseCmd(void);
++void OGG_PlayCmd(void);
++void OGG_ResumeCmd(void);
++void OGG_SeekCmd(void);
++void OGG_StatusCmd(void);
+
+ #endif
diff --git a/games/kmquake2/files/patch-qcommon__files.c b/games/kmquake2/files/patch-qcommon__files.c
index d23a70ad2d3a..38e9c502047f 100644
--- a/games/kmquake2/files/patch-qcommon__files.c
+++ b/games/kmquake2/files/patch-qcommon__files.c
@@ -1,5 +1,5 @@
--- qcommon/files.c.orig Thu Mar 23 06:30:01 2006
-+++ qcommon/files.c Fri Jul 28 14:54:41 2006
++++ qcommon/files.c Sat Dec 30 01:23:45 2006
@@ -1314,7 +1314,7 @@
char *homedir; /* Home directory. */
@@ -67,3 +67,163 @@
FS_AddHomeAsGameDirectory(dir);
#endif
}
+@@ -1799,6 +1797,159 @@
+ return (list);
+ }
+ #endif
++
++/*
++ * CompareAttributesPack
++ *
++ * Compare file attributes (musthave and canthave) in packed files. If
++ * "output" is not NULL, "size" is greater than zero and the file matches the
++ * attributes then a copy of the matching string will be placed there (with
++ * SFF_SUBDIR it changes).
++ *
++ * Returns a boolean value, true if the attributes match the file.
++ */
++qboolean
++ComparePackFiles(const char *findname, const char *name,
++ unsigned musthave, unsigned canthave, char *output, int size)
++{
++ qboolean retval;
++ char *ptr;
++ char buffer[MAX_OSPATH];
++
++ Q_strncpyz(buffer, name, sizeof(buffer));
++
++ if ((canthave & SFF_SUBDIR) && name[strlen(name)-1] == '/')
++ return (false);
++
++ if (musthave & SFF_SUBDIR) {
++ if ((ptr = strrchr(buffer, '/')) != NULL)
++ *ptr = '\0';
++ else
++ return (false);
++ }
++
++ if ((musthave & SFF_HIDDEN) || (canthave & SFF_HIDDEN)) {
++ if ((ptr = strrchr(buffer, '/')) == NULL)
++ ptr = buffer;
++ if (((musthave & SFF_HIDDEN) && ptr[1] != '.') ||
++ ((canthave & SFF_HIDDEN) && ptr[1] == '.'))
++ return (false);
++ }
++
++ if (canthave & SFF_RDONLY)
++ return (false);
++
++ retval = glob_match((char *)findname, buffer);
++
++ if (retval && output != NULL)
++ Q_strncpyz(output, buffer, size);
++
++ return (retval);
++}
++
++/*
++ * FS_ListFiles2
++ *
++ * Create a list of files that match a criteria.
++ *
++ * Searchs are relative to the game directory and use all the search paths
++ * including .pak and .pk3 files.
++ */
++char **
++FS_ListFiles2(char *findname, int *numfiles, unsigned musthave, unsigned canthave)
++{
++ fsSearchPath_t *search; /* Search path. */
++ int i, j; /* Loop counters. */
++ int nfiles; /* Number of files found. */
++ int tmpnfiles; /* Temp number of files. */
++ char **tmplist; /* Temporary list of files. */
++ char **list; /* List of files found. */
++ char path[MAX_OSPATH]; /* Temporary path. */
++
++ nfiles = 0;
++ list = malloc(sizeof(char *));
++
++ for (search = fs_searchPaths; search != NULL; search = search->next) {
++ if (search->pack != NULL) {
++ if (canthave & SFF_INPACK)
++ continue;
++
++ for (i = 0, j = 0; i < search->pack->numFiles; i++)
++ if (ComparePackFiles(findname,
++ search->pack->files[i].name,
++ musthave, canthave, NULL, 0))
++ j++;
++ if (j == 0)
++ continue;
++ nfiles += j;
++ list = realloc(list, nfiles * sizeof(char *));
++ for (i = 0, j = nfiles - j;
++ i < search->pack->numFiles;
++ i++)
++ if (ComparePackFiles(findname,
++ search->pack->files[i].name,
++ musthave, canthave, path, sizeof(path)))
++ list[j++] = strdup(path);
++ } else if (search->path != NULL) {
++ if (musthave & SFF_INPACK)
++ continue;
++
++ Com_sprintf(path, sizeof(path), "%s/%s",
++ search->path, findname);
++ tmplist = FS_ListFiles(path, &tmpnfiles, musthave,
++ canthave);
++ if (tmplist != NULL) {
++ tmpnfiles--;
++ nfiles += tmpnfiles;
++ list = realloc(list, nfiles * sizeof(char *));
++ for (i = 0, j = nfiles - tmpnfiles;
++ i < tmpnfiles;
++ i++, j++)
++ list[j] = strdup(tmplist[i] +
++ strlen(search->path) + 1);
++ FS_FreeList(tmplist, tmpnfiles + 1);
++ }
++ }
++ }
++
++ /* Delete duplicates. */
++ tmpnfiles = 0;
++ for (i = 0; i < nfiles; i++) {
++ if (list[i] == NULL)
++ continue;
++ for (j = i + 1; j < nfiles; j++)
++ if (list[j] != NULL &&
++ strcmp(list[i], list[j]) == 0) {
++ free(list[j]);
++ list[j] = NULL;
++ tmpnfiles++;
++ }
++ }
++
++ if (tmpnfiles > 0) {
++ nfiles -= tmpnfiles;
++ tmplist = malloc(nfiles * sizeof(char *));
++ for (i = 0, j = 0; i < nfiles + tmpnfiles; i++)
++ if (list[i] != NULL)
++ tmplist[j++] = list[i];
++ free(list);
++ list = tmplist;
++ }
++
++ /* Add a guard. */
++ if (nfiles > 0) {
++ nfiles++;
++ list = realloc(list, nfiles * sizeof(char *));
++ list[nfiles - 1] = NULL;
++ } else {
++ free(list);
++ list = NULL;
++ }
++
++ *numfiles = nfiles;
++
++ return (list);
++}
+
+ /*
+ ================
diff --git a/games/kmquake2/files/patch-qcommon__qcommon.h b/games/kmquake2/files/patch-qcommon__qcommon.h
new file mode 100644
index 000000000000..c0bd3590d1f2
--- /dev/null
+++ b/games/kmquake2/files/patch-qcommon__qcommon.h
@@ -0,0 +1,10 @@
+--- qcommon/qcommon.h.orig Wed Dec 28 21:01:15 2005
++++ qcommon/qcommon.h Sat Dec 30 01:22:07 2006
+@@ -758,6 +758,7 @@
+ ==============================================================
+ */
+
++#define SFF_INPACK 0x20 /* For FS_ListFiles2(). */
+
+ typedef int fileHandle_t;
+