mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-10 06:32:00 +00:00
Tweak QI plugin to translate quaddicted's map database to fte's meta format. Make 'map package:map' download+run a map with the specified package enabled.
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5976 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
parent
9e54944bde
commit
0907e66cff
11 changed files with 750 additions and 170 deletions
|
@ -5378,7 +5378,6 @@ extern int waitingformanifest;
|
|||
void Host_DoRunFile(hrf_t *f);
|
||||
void CL_PlayDemoStream(vfsfile_t *file, char *filename, qboolean issyspath, int demotype, float bufferdelay);
|
||||
void CL_ParseQTVDescriptor(vfsfile_t *f, const char *name);
|
||||
qboolean FS_PathURLCache(char *url, char *path, size_t pathsize);
|
||||
|
||||
//guesses the file type based upon its file extension. mdl/md3/iqm distinctions are not important, so we can usually get away with this in the context of quake.
|
||||
unsigned int Host_GuessFileType(const char *mimetype, const char *filename)
|
||||
|
|
|
@ -78,6 +78,7 @@ cvar_t pkg_autoupdate = CVARFD("pkg_autoupdate", "-1", CVAR_NOTFROMSERVER|CVAR_N
|
|||
#define DPF_SIGNATUREREJECTED (1u<<17) //signature is bad
|
||||
#define DPF_SIGNATUREACCEPTED (1u<<18) //signature is good (required for dll/so/exe files)
|
||||
#define DPF_SIGNATUREUNKNOWN (1u<<19) //signature is unknown
|
||||
#define DPF_HIDEUNLESSPRESENT (1u<<20) //hidden in the ui, unless present.
|
||||
|
||||
#define DPF_MARKED (DPF_USERMARKED|DPF_AUTOMARKED) //flags that will enable it
|
||||
#define DPF_ALLMARKED (DPF_USERMARKED|DPF_AUTOMARKED|DPF_MANIMARKED) //flags that will download it without necessarily enabling it.
|
||||
|
@ -133,6 +134,7 @@ typedef struct package_s {
|
|||
char version[16];
|
||||
char *arch;
|
||||
char *qhash;
|
||||
char *packprefix; //extra weirdness to skip embedded gamedirs or force extra maps/ nesting
|
||||
|
||||
quint64_t filesize; //in bytes, as part of verifying the hash.
|
||||
char *filesha1;
|
||||
|
@ -168,10 +170,12 @@ typedef struct package_s {
|
|||
DEP_NEEDFEATURE, //requires a specific feature to be available (typically controlled via a cvar)
|
||||
// DEP_MIRROR,
|
||||
// DEP_FAILEDMIRROR,
|
||||
DEP_MAP, //This package contains this map. woo.
|
||||
|
||||
DEP_SOURCE, //which source url we found this package from
|
||||
DEP_EXTRACTNAME, //a file that will be installed
|
||||
DEP_FILE //a file that will be installed
|
||||
DEP_FILE, //a file that will be installed. will be loaded as a package, where appropriate.
|
||||
DEP_CACHEFILE //an installed file that's relative to the downloads/ subdir
|
||||
} dtype;
|
||||
char name[1];
|
||||
} *deps;
|
||||
|
@ -194,6 +198,12 @@ static char *manifestpackages; //metapackage named by the manicfest.
|
|||
static char *declinedpackages; //metapackage named by the manicfest.
|
||||
static int domanifestinstall; //SECURITY_MANIFEST_*
|
||||
|
||||
static struct
|
||||
{
|
||||
char *package; //package to load. don't forget its dependancies too.
|
||||
char *map; //the map to load.
|
||||
} pm_onload;
|
||||
|
||||
#ifndef SERVERONLY
|
||||
//static qboolean pluginpromptshown; //so we only show prompts for new externally-installed plugins once, instead of every time the file is reloaded.
|
||||
#endif
|
||||
|
@ -209,7 +219,7 @@ static qboolean pm_packagesinstalled;
|
|||
|
||||
//FIXME: these are allocated for the life of the exe. changing basedir should purge the list.
|
||||
static size_t pm_numsources = 0;
|
||||
static struct
|
||||
static struct pm_source_s
|
||||
{
|
||||
char *url; //url to query. unique.
|
||||
char *prefix; //category prefix for packages from this source.
|
||||
|
@ -229,13 +239,17 @@ static struct
|
|||
#define SRCFL_NESTED (1u<<1) //discovered from a different source. always disabled.
|
||||
#define SRCFL_MANIFEST (1u<<2) //not saved. often default to enabled.
|
||||
#define SRCFL_USER (1u<<3) //user explicitly added it. included into installed.lst. enabled (if trusted).
|
||||
#define SRCFLMASK_FROM (SRCFL_HISTORIC|SRCFL_NESTED|SRCFL_MANIFEST|SRCFL_USER) //mask of flags, forming priority for replacements.
|
||||
#define SRCFL_DISABLED (1u<<4) //source was explicitly disabled.
|
||||
#define SRCFL_ENABLED (1u<<5) //source was explicitly enabled.
|
||||
#define SRCFL_PROMPTED (1u<<6) //source was explicitly enabled.
|
||||
#define SRCFL_ONCE (1u<<7) //autoupdates are disabled, but the user is viewing packages anyway. enabled via a prompt.
|
||||
#define SRCFL_PLUGIN (1u<<4) //user explicitly added it. included into installed.lst. enabled (if trusted).
|
||||
#define SRCFLMASK_FROM (SRCFL_HISTORIC|SRCFL_NESTED|SRCFL_MANIFEST|SRCFL_USER|SRCFL_PLUGIN) //mask of flags, forming priority for replacements.
|
||||
#define SRCFL_DISABLED (1u<<5) //source was explicitly disabled.
|
||||
#define SRCFL_ENABLED (1u<<6) //source was explicitly enabled.
|
||||
#define SRCFL_PROMPTED (1u<<7) //source was explicitly enabled.
|
||||
#define SRCFL_ONCE (1u<<8) //autoupdates are disabled, but the user is viewing packages anyway. enabled via a prompt.
|
||||
unsigned int flags;
|
||||
struct dl_download *curdl; //the download context
|
||||
|
||||
void *module; //plugins
|
||||
plugupdatesourcefuncs_t *funcs;
|
||||
} *pm_source/*[pm_maxsources]*/;
|
||||
static int downloadablessequence; //bumped any time any package is purged
|
||||
|
||||
|
@ -478,33 +492,47 @@ static void PM_ValidatePackage(package_t *p)
|
|||
struct packagedep_s *dep;
|
||||
vfsfile_t *pf;
|
||||
char temp[MAX_OSPATH];
|
||||
p->flags &=~ (DPF_NATIVE|DPF_CACHED|DPF_CORRUPT);
|
||||
p->flags &=~ (DPF_NATIVE|DPF_CACHED|DPF_CORRUPT|DPF_HIDEUNLESSPRESENT);
|
||||
if (p->flags & DPF_ENABLED)
|
||||
{
|
||||
for (dep = p->deps; dep; dep = dep->next)
|
||||
{
|
||||
char *n;
|
||||
if (dep->dtype != DEP_FILE)
|
||||
continue;
|
||||
if (*p->gamedir)
|
||||
n = va("%s/%s", p->gamedir, dep->name);
|
||||
else
|
||||
n = dep->name;
|
||||
pf = FS_OpenVFS(n, "rb", p->fsroot);
|
||||
if (pf)
|
||||
if (dep->dtype == DEP_CACHEFILE)
|
||||
{
|
||||
VFS_CLOSE(pf);
|
||||
p->flags |= DPF_NATIVE;
|
||||
}
|
||||
else if (PM_TryGenCachedName(n, p, temp, sizeof(temp)))
|
||||
{
|
||||
pf = FS_OpenVFS(temp, "rb", p->fsroot);
|
||||
// p->flags |= DPF_HIDEUNLESSPRESENT;
|
||||
n = va("downloads/%s", dep->name);
|
||||
pf = FS_OpenVFS(n, "rb", p->fsroot);
|
||||
if (pf)
|
||||
{
|
||||
VFS_CLOSE(pf);
|
||||
p->flags |= DPF_CACHED;
|
||||
}
|
||||
}
|
||||
else if (dep->dtype == DEP_FILE)
|
||||
{
|
||||
if (*p->gamedir)
|
||||
n = va("%s/%s", p->gamedir, dep->name);
|
||||
else
|
||||
n = dep->name;
|
||||
pf = FS_OpenVFS(n, "rb", p->fsroot);
|
||||
if (pf)
|
||||
{
|
||||
VFS_CLOSE(pf);
|
||||
p->flags |= DPF_NATIVE;
|
||||
}
|
||||
else if (PM_TryGenCachedName(n, p, temp, sizeof(temp)))
|
||||
{
|
||||
pf = FS_OpenVFS(temp, "rb", p->fsroot);
|
||||
if (pf)
|
||||
{
|
||||
VFS_CLOSE(pf);
|
||||
p->flags |= DPF_CACHED;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
continue;
|
||||
if (!(p->flags & (DPF_NATIVE|DPF_CACHED)))
|
||||
Con_Printf("WARNING: %s (%s) no longer exists\n", p->name, n);
|
||||
}
|
||||
|
@ -516,19 +544,29 @@ static void PM_ValidatePackage(package_t *p)
|
|||
char *n;
|
||||
struct packagedep_s *odep;
|
||||
unsigned int fl = DPF_NATIVE;
|
||||
if (dep->dtype != DEP_FILE)
|
||||
continue;
|
||||
if (*p->gamedir)
|
||||
n = va("%s/%s", p->gamedir, dep->name);
|
||||
else
|
||||
n = dep->name;
|
||||
pf = FS_OpenVFS(n, "rb", p->fsroot);
|
||||
if (!pf && PM_TryGenCachedName(n, p, temp, sizeof(temp)))
|
||||
if (dep->dtype == DEP_FILE)
|
||||
{
|
||||
pf = FS_OpenVFS(temp, "rb", p->fsroot);
|
||||
fl = DPF_CACHED;
|
||||
//fixme: skip any archive checks
|
||||
if (*p->gamedir)
|
||||
n = va("%s/%s", p->gamedir, dep->name);
|
||||
else
|
||||
n = dep->name;
|
||||
pf = FS_OpenVFS(n, "rb", p->fsroot);
|
||||
if (!pf && PM_TryGenCachedName(n, p, temp, sizeof(temp)))
|
||||
{
|
||||
pf = FS_OpenVFS(temp, "rb", p->fsroot);
|
||||
fl = DPF_CACHED;
|
||||
//fixme: skip any archive checks
|
||||
}
|
||||
}
|
||||
else if (dep->dtype == DEP_CACHEFILE)
|
||||
{
|
||||
// p->flags |= DPF_HIDEUNLESSPRESENT;
|
||||
fl = DPF_CACHED;
|
||||
n = va("downloads/%s", dep->name);
|
||||
pf = FS_OpenVFS(n, "rb", p->fsroot);
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
if (pf)
|
||||
{
|
||||
|
@ -714,10 +752,56 @@ static qboolean PM_MergePackage(package_t *oldp, package_t *newp)
|
|||
return false;
|
||||
}
|
||||
|
||||
static int QDECL PM_PackageSortOrdering(const void *l, const void *r)
|
||||
{ //for qsort.
|
||||
const package_t *a=*(package_t*const*)l, *b=*(package_t*const*)r;
|
||||
const char *ac, *bc;
|
||||
int order;
|
||||
|
||||
//sort by categories
|
||||
ac = a->category?a->category:"";
|
||||
bc = b->category?b->category:"";
|
||||
order = Q_strcasecmp(ac,bc);
|
||||
if (order)
|
||||
return order;
|
||||
|
||||
//otherwise sort by title.
|
||||
ac = a->title?a->title:a->name;
|
||||
bc = b->title?b->title:b->name;
|
||||
order = Q_strcasecmp(ac,bc);
|
||||
return order;
|
||||
}
|
||||
static void PM_ResortPackages(void)
|
||||
{
|
||||
int i, count;
|
||||
package_t **sorted;
|
||||
package_t *p;
|
||||
for (count = 0, p = availablepackages; p; p=p->next)
|
||||
count++;
|
||||
if (!count)
|
||||
return;
|
||||
sorted = Z_Malloc(sizeof(*sorted)*count);
|
||||
for (count = 0, p = availablepackages; p; p=p->next)
|
||||
sorted[count++] = p;
|
||||
qsort(sorted, count, sizeof(*sorted), PM_PackageSortOrdering);
|
||||
availablepackages = NULL;
|
||||
for (i = count; i --> 0; )
|
||||
{
|
||||
sorted[i]->next = availablepackages;
|
||||
sorted[i]->link = &availablepackages;
|
||||
|
||||
if (availablepackages)
|
||||
availablepackages->link = &sorted[i]->next;
|
||||
availablepackages = sorted[i];
|
||||
}
|
||||
Z_Free(sorted);
|
||||
}
|
||||
|
||||
static package_t *PM_InsertPackage(package_t *p)
|
||||
{
|
||||
package_t **link;
|
||||
int v;
|
||||
|
||||
for (link = &availablepackages; *link; link = &(*link)->next)
|
||||
{
|
||||
package_t *prev = *link;
|
||||
|
@ -755,7 +839,7 @@ static package_t *PM_InsertPackage(package_t *p)
|
|||
//FIXME: replace prev...
|
||||
}
|
||||
}
|
||||
v = strcmp(prev->name, p->name);
|
||||
v = PM_PackageSortOrdering(&prev, &p);
|
||||
if (v > 0)
|
||||
break; //insert before this one
|
||||
else if (v == 0)
|
||||
|
@ -870,7 +954,7 @@ static qboolean PM_CheckFile(const char *filename, enum fs_relative base)
|
|||
return false;
|
||||
}
|
||||
|
||||
static void PM_AddSubList(const char *url, const char *prefix, unsigned int flags)
|
||||
static void PM_AddSubListModule(void *module, plugupdatesourcefuncs_t *funcs, const char *url, const char *prefix, unsigned int flags)
|
||||
{
|
||||
size_t i;
|
||||
if (!prefix)
|
||||
|
@ -888,6 +972,11 @@ static void PM_AddSubList(const char *url, const char *prefix, unsigned int flag
|
|||
{
|
||||
unsigned int newpri = flags&SRCFLMASK_FROM;
|
||||
unsigned int oldpri = pm_source[i].flags&SRCFLMASK_FROM;
|
||||
if (module)
|
||||
{
|
||||
pm_source[i].module = module;
|
||||
pm_source[i].funcs = funcs;
|
||||
}
|
||||
if (newpri > oldpri)
|
||||
{ //replacing an historic package should stomp on most of it, retaining only its enablement status.
|
||||
pm_source[i].flags &= ~SRCFLMASK_FROM;
|
||||
|
@ -903,6 +992,8 @@ static void PM_AddSubList(const char *url, const char *prefix, unsigned int flag
|
|||
{
|
||||
Z_ReallocElements((void*)&pm_source, &pm_numsources, i+1, sizeof(*pm_source));
|
||||
|
||||
pm_source[i].module = module;
|
||||
pm_source[i].funcs = funcs;
|
||||
pm_source[i].status = SRCSTAT_UNTRIED;
|
||||
pm_source[i].flags = flags;
|
||||
|
||||
|
@ -915,6 +1006,10 @@ static void PM_AddSubList(const char *url, const char *prefix, unsigned int flag
|
|||
downloadablessequence++;
|
||||
}
|
||||
}
|
||||
static void PM_AddSubList(const char *url, const char *prefix, unsigned int flags)
|
||||
{
|
||||
PM_AddSubListModule(NULL, NULL, url, prefix, flags);
|
||||
}
|
||||
#ifdef WEBCLIENT
|
||||
static void PM_RemSubList(const char *url)
|
||||
{
|
||||
|
@ -962,52 +1057,6 @@ static const char *PM_ParsePackage(struct packagesourceinfo_s *source, const cha
|
|||
qboolean invariation = false;
|
||||
|
||||
|
||||
#if 0
|
||||
if (version < 2)
|
||||
{
|
||||
char pathname[256];
|
||||
const char *fullname = Cmd_Argv(0);
|
||||
if (argc > 5 || argc < 3)
|
||||
{
|
||||
Con_Printf("Package list is bad - %s\n", line);
|
||||
continue; //but try the next line away
|
||||
}
|
||||
|
||||
p = Z_Malloc(sizeof(*p));
|
||||
if (*prefix)
|
||||
Q_snprintfz(pathname, sizeof(pathname), "%s/%s", prefix, fullname);
|
||||
else
|
||||
Q_snprintfz(pathname, sizeof(pathname), "%s", fullname);
|
||||
p->name = Z_StrDup(COM_SkipPath(pathname));
|
||||
p->title = Z_StrDup(p->name);
|
||||
*COM_SkipPath(pathname) = 0;
|
||||
p->category = Z_StrDup(pathname);
|
||||
p->mirror[0] = Z_StrDup(p->name);
|
||||
|
||||
p->priority = PM_DEFAULTPRIORITY;
|
||||
p->flags = parseflags;
|
||||
|
||||
p->mirror[0] = Z_StrDup(Cmd_Argv(1));
|
||||
PM_AddDep(p, DEP_FILE, Cmd_Argv(2));
|
||||
Q_strncpyz(p->version, Cmd_Argv(3), sizeof(p->version));
|
||||
Q_strncpyz(p->gamedir, Cmd_Argv(4), sizeof(p->gamedir));
|
||||
if (!strcmp(p->gamedir, "../"))
|
||||
{
|
||||
p->fsroot = FS_ROOT;
|
||||
*p->gamedir = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!*p->gamedir)
|
||||
{
|
||||
strcpy(p->gamedir, FS_GetGamedir(false));
|
||||
// p->fsroot = FS_GAMEONLY;
|
||||
}
|
||||
p->fsroot = FS_ROOT;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
char pathname[256];
|
||||
char *fullname = (source->version >= 3)?NULL:Z_StrDup(com_token);
|
||||
|
@ -1107,6 +1156,8 @@ static const char *PM_ParsePackage(struct packagesourceinfo_s *source, const cha
|
|||
p->priority = atoi(val);
|
||||
else if (!strcmp(key, "qhash"))
|
||||
Z_StrDupPtr(&p->qhash, val);
|
||||
else if (!strcmp(key, "packprefix"))
|
||||
Z_StrDupPtr(&p->packprefix, val);
|
||||
else if (!strcmp(key, "desc") || !strcmp(key, "description"))
|
||||
{
|
||||
if (p->description)
|
||||
|
@ -1132,6 +1183,12 @@ static const char *PM_ParsePackage(struct packagesourceinfo_s *source, const cha
|
|||
Z_StrDupPtr(&file, val);
|
||||
PM_AddDep(p, DEP_FILE, val);
|
||||
}
|
||||
else if (!strcmp(key, "cachefile"))
|
||||
{ //installed file
|
||||
if (!file)
|
||||
Z_StrDupPtr(&file, val);
|
||||
PM_AddDep(p, DEP_CACHEFILE, val);
|
||||
}
|
||||
else if (!strcmp(key, "extract"))
|
||||
{
|
||||
if (!strcmp(val, "xz"))
|
||||
|
@ -1145,6 +1202,8 @@ static const char *PM_ParsePackage(struct packagesourceinfo_s *source, const cha
|
|||
else
|
||||
Con_Printf("Unknown decompression method: %s\n", val);
|
||||
}
|
||||
else if (!strcmp(key, "map"))
|
||||
PM_AddDep(p, DEP_MAP, val);
|
||||
else if (!strcmp(key, "depend"))
|
||||
PM_AddDep(p, DEP_REQUIRE, val);
|
||||
else if (!strcmp(key, "conflict"))
|
||||
|
@ -1227,7 +1286,15 @@ static const char *PM_ParsePackage(struct packagesourceinfo_s *source, const cha
|
|||
p->flags = flags;
|
||||
|
||||
if (url && (!strncmp(url, "http://", 7) || !strncmp(url, "https://", 8)))
|
||||
{
|
||||
p->mirror[0] = Z_StrDup(url);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
FS_PathURLCache(url, pathname, sizeof(pathname));
|
||||
PM_AddDep(p, DEP_CACHEFILE, pathname+10);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int m;
|
||||
|
@ -1589,6 +1656,42 @@ void PM_EnumeratePlugins(void (*callback)(const char *name))
|
|||
}
|
||||
}
|
||||
#endif
|
||||
void PM_EnumerateMaps(const char *partial, struct xcommandargcompletioncb_s *ctx)
|
||||
{
|
||||
package_t *p;
|
||||
struct packagedep_s *d;
|
||||
size_t partiallen = strlen(partial);
|
||||
char mname[256];
|
||||
const char *sep = strchr(partial, ':');
|
||||
size_t pkgpartiallen = sep?sep-partial:partiallen;
|
||||
|
||||
PM_PreparePackageList();
|
||||
|
||||
for (p = availablepackages; p; p = p->next)
|
||||
{
|
||||
if (!Q_strncasecmp(p->name, partial, pkgpartiallen))
|
||||
{
|
||||
for (d = p->deps; d; d = d->next)
|
||||
{
|
||||
if (d->dtype == DEP_MAP)
|
||||
{
|
||||
/*if (!strchr(partial, ':'))
|
||||
{ //try to expand to only one...
|
||||
Q_snprintfz(mname, sizeof(mname), "%s:", p->name);
|
||||
ctx->cb(mname, NULL, NULL, ctx);
|
||||
break;
|
||||
}
|
||||
else*/
|
||||
{
|
||||
Q_snprintfz(mname, sizeof(mname), "%s:%s", p->name, d->name);
|
||||
if (!Q_strncasecmp(mname, partial, partiallen))
|
||||
ctx->cb(mname, NULL, NULL, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static qboolean QDECL Host_StubClose (struct vfsfile_s *file)
|
||||
{
|
||||
|
@ -1722,7 +1825,10 @@ static qboolean PM_FileInstalled_Internal(const char *package, const char *categ
|
|||
p->flags |= DPF_USERMARKED|DPF_ENABLED;
|
||||
|
||||
if (PM_InsertPackage(p))
|
||||
{
|
||||
PM_ResortPackages();
|
||||
PM_WriteInstalledPackages();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1911,7 +2017,12 @@ void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const cha
|
|||
if (d->dtype == DEP_FILE)
|
||||
{
|
||||
Q_snprintfz(temp, sizeof(temp), "%s/%s", p->gamedir, d->name);
|
||||
FS_AddHashedPackage(oldpaths, parent_pure, parent_logical, search, loadstuff, temp, p->qhash, NULL, SPF_COPYPROTECTED|SPF_UNTRUSTED);
|
||||
FS_AddHashedPackage(oldpaths, parent_pure, parent_logical, search, loadstuff, temp, *p->qhash?p->qhash:NULL, p->packprefix, SPF_COPYPROTECTED|SPF_UNTRUSTED);
|
||||
}
|
||||
else if (d->dtype == DEP_CACHEFILE)
|
||||
{
|
||||
Q_snprintfz(temp, sizeof(temp), "downloads/%s", d->name);
|
||||
FS_AddHashedPackage(oldpaths, parent_pure, parent_logical, NULL, loadstuff, temp, *p->qhash?p->qhash:NULL, p->packprefix, SPF_COPYPROTECTED|SPF_UNTRUSTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1921,14 +2032,14 @@ void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const cha
|
|||
|
||||
void PM_Shutdown(qboolean soft)
|
||||
{
|
||||
size_t i, pm_numoldsources = pm_numsources;
|
||||
//free everything...
|
||||
|
||||
downloadablessequence++;
|
||||
|
||||
while(pm_numsources > 0)
|
||||
pm_numsources = 0;
|
||||
for (i = 0; i < pm_numoldsources; i++)
|
||||
{
|
||||
size_t i = --pm_numsources;
|
||||
|
||||
#ifdef WEBCLIENT
|
||||
if (pm_source[i].curdl)
|
||||
{
|
||||
|
@ -1937,13 +2048,24 @@ void PM_Shutdown(qboolean soft)
|
|||
}
|
||||
#endif
|
||||
pm_source[i].status = SRCSTAT_UNTRIED;
|
||||
Z_Free(pm_source[i].url);
|
||||
pm_source[i].url = NULL;
|
||||
Z_Free(pm_source[i].prefix);
|
||||
pm_source[i].prefix = NULL;
|
||||
|
||||
if (pm_source[i].module && soft)
|
||||
{ //added via a plugin. reset rather than forget.
|
||||
pm_source[pm_numsources++] = pm_source[i];
|
||||
}
|
||||
else
|
||||
{ //forget it, oh noes.
|
||||
Z_Free(pm_source[i].url);
|
||||
pm_source[i].url = NULL;
|
||||
Z_Free(pm_source[i].prefix);
|
||||
pm_source[i].prefix = NULL;
|
||||
}
|
||||
}
|
||||
if (!pm_numsources)
|
||||
{
|
||||
Z_Free(pm_source);
|
||||
pm_source = NULL;
|
||||
}
|
||||
Z_Free(pm_source);
|
||||
pm_source = NULL;
|
||||
|
||||
if (!soft)
|
||||
{
|
||||
|
@ -2482,6 +2604,7 @@ static void PM_ListDownloaded(struct dl_download *dl)
|
|||
{
|
||||
pm_source[listidx].status = SRCSTAT_OBTAINED;
|
||||
PM_ParsePackageList(f, 0, dl->url, pm_source[listidx].prefix);
|
||||
PM_ResortPackages();
|
||||
}
|
||||
else if (dl->replycode == HTTP_DNSFAILURE)
|
||||
pm_source[listidx].status = SRCSTAT_FAILED_DNS;
|
||||
|
@ -2560,6 +2683,22 @@ static void PM_ListDownloaded(struct dl_download *dl)
|
|||
}
|
||||
}
|
||||
}
|
||||
static void PM_Plugin_Source_Finished(void *ctx, vfsfile_t *f)
|
||||
{
|
||||
struct pm_source_s *src = ctx;
|
||||
COM_AssertMainThread("PM_Plugin_Source_Finished");
|
||||
if (!src->curdl)
|
||||
{
|
||||
struct dl_download dl;
|
||||
dl.file = f;
|
||||
dl.status = DL_FINISHED;
|
||||
dl.user_num = src-pm_source;
|
||||
dl.url = src->url;
|
||||
src->curdl = &dl;
|
||||
PM_ListDownloaded(&dl);
|
||||
}
|
||||
VFS_CLOSE(f);
|
||||
}
|
||||
#endif
|
||||
#if defined(HAVE_CLIENT) && defined(WEBCLIENT)
|
||||
static void PM_UpdatePackageList(qboolean autoupdate, int retry);
|
||||
|
@ -2633,19 +2772,27 @@ static void PM_UpdatePackageList(qboolean autoupdate, int retry)
|
|||
if (pm_source[i].status == SRCSTAT_OBTAINED)
|
||||
return; //already successful once. no need to do it again.
|
||||
pm_source[i].flags &= ~SRCFL_ONCE;
|
||||
pm_source[i].curdl = HTTP_CL_Get(pm_source[i].url, NULL, PM_ListDownloaded);
|
||||
if (pm_source[i].curdl)
|
||||
{
|
||||
pm_source[i].curdl->user_num = i;
|
||||
|
||||
pm_source[i].curdl->file = VFSPIPE_Open(1, false);
|
||||
pm_source[i].curdl->isquery = true;
|
||||
DL_CreateThread(pm_source[i].curdl, NULL, NULL);
|
||||
if (pm_source[i].funcs)
|
||||
{
|
||||
pm_source[i].funcs->Update(pm_source[i].url, VFS_OpenPipeCallback(PM_Plugin_Source_Finished, &pm_source[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf("Could not contact updates server - %s\n", pm_source[i].url);
|
||||
pm_source[i].status = SRCSTAT_FAILED_DNS;
|
||||
pm_source[i].curdl = HTTP_CL_Get(pm_source[i].url, NULL, PM_ListDownloaded);
|
||||
if (pm_source[i].curdl)
|
||||
{
|
||||
pm_source[i].curdl->user_num = i;
|
||||
|
||||
pm_source[i].curdl->file = VFSPIPE_Open(1, false);
|
||||
pm_source[i].curdl->isquery = true;
|
||||
DL_CreateThread(pm_source[i].curdl, NULL, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf("Could not contact updates server - %s\n", pm_source[i].url);
|
||||
pm_source[i].status = SRCSTAT_FAILED_DNS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2665,7 +2812,24 @@ static void PM_UpdatePackageList(qboolean autoupdate, int retry)
|
|||
#endif
|
||||
}
|
||||
|
||||
|
||||
qboolean PM_RegisterUpdateSource(void *module, plugupdatesourcefuncs_t *funcs)
|
||||
{
|
||||
size_t i;
|
||||
if (!funcs)
|
||||
{
|
||||
for (i = 0; i < pm_numsources; i++)
|
||||
{
|
||||
if (pm_source[i].module == module)
|
||||
{
|
||||
pm_source[i].module = NULL;
|
||||
pm_source[i].funcs = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
PM_AddSubListModule(module, funcs, va("plug:%s", funcs->description), NULL, SRCFL_PLUGIN);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void COM_QuotedConcat(const char *cat, char *buf, size_t bufsize)
|
||||
{
|
||||
|
@ -2798,11 +2962,17 @@ static void PM_WriteInstalledPackages(void)
|
|||
|
||||
if (p->fsroot == FS_BINARYPATH)
|
||||
COM_QuotedKeyVal("root", "bin", buf, sizeof(buf));
|
||||
if (p->packprefix)
|
||||
COM_QuotedKeyVal("packprefix", p->packprefix, buf, sizeof(buf));
|
||||
|
||||
for (dep = p->deps; dep; dep = dep->next)
|
||||
{
|
||||
if (dep->dtype == DEP_FILE)
|
||||
COM_QuotedKeyVal("file", dep->name, buf, sizeof(buf));
|
||||
else if (dep->dtype == DEP_CACHEFILE)
|
||||
COM_QuotedKeyVal("cachefile", dep->name, buf, sizeof(buf));
|
||||
else if (dep->dtype == DEP_MAP)
|
||||
COM_QuotedKeyVal("map", dep->name, buf, sizeof(buf));
|
||||
else if (dep->dtype == DEP_REQUIRE)
|
||||
COM_QuotedKeyVal("depend", dep->name, buf, sizeof(buf));
|
||||
else if (dep->dtype == DEP_CONFLICT)
|
||||
|
@ -2910,6 +3080,11 @@ static void PM_WriteInstalledPackages(void)
|
|||
Q_strncatz(buf, " ", sizeof(buf));
|
||||
COM_QuotedConcat("root=bin", buf, sizeof(buf));
|
||||
}
|
||||
if (p->packprefix)
|
||||
{
|
||||
Q_strncatz(buf, " ", sizeof(buf));
|
||||
COM_QuotedConcat(va("packprefix=%s", p->packprefix), buf, sizeof(buf));
|
||||
}
|
||||
|
||||
for (dep = p->deps; dep; dep = dep->next)
|
||||
{
|
||||
|
@ -2918,6 +3093,16 @@ static void PM_WriteInstalledPackages(void)
|
|||
Q_strncatz(buf, " ", sizeof(buf));
|
||||
COM_QuotedConcat(va("file=%s", dep->name), buf, sizeof(buf));
|
||||
}
|
||||
else if (dep->dtype == DEP_CACHEFILE)
|
||||
{
|
||||
Q_strncatz(buf, " ", sizeof(buf));
|
||||
COM_QuotedConcat(va("cachefile=%s", dep->name), buf, sizeof(buf));
|
||||
}
|
||||
else if (dep->dtype == DEP_MAP)
|
||||
{
|
||||
Q_strncatz(buf, " ", sizeof(buf));
|
||||
COM_QuotedConcat(va("map=%s", dep->name), buf, sizeof(buf));
|
||||
}
|
||||
else if (dep->dtype == DEP_REQUIRE)
|
||||
{
|
||||
Q_strncatz(buf, " ", sizeof(buf));
|
||||
|
@ -2984,10 +3169,10 @@ static void PM_PackageEnabled(package_t *p)
|
|||
FS_FlushFSHashFull();
|
||||
for (dep = p->deps; dep; dep = dep->next)
|
||||
{
|
||||
if (dep->dtype != DEP_FILE)
|
||||
if (dep->dtype != DEP_FILE && dep->dtype != DEP_CACHEFILE)
|
||||
continue;
|
||||
COM_FileExtension(dep->name, ext, sizeof(ext));
|
||||
if (!stricmp(ext, "pak") || !stricmp(ext, "pk3"))
|
||||
if (!stricmp(ext, "pak") || !stricmp(ext, "pk3") || !stricmp(ext, "zip"))
|
||||
{
|
||||
if (pm_packagesinstalled)
|
||||
{
|
||||
|
@ -3172,33 +3357,47 @@ static void PM_Download_Got(int iarg, void *data)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for (dep = p->deps; dep; dep = dep->next)
|
||||
{
|
||||
unsigned int nfl;
|
||||
if (dep->dtype != DEP_FILE)
|
||||
if (dep->dtype != DEP_FILE && dep->dtype != DEP_CACHEFILE)
|
||||
continue;
|
||||
|
||||
COM_FileExtension(dep->name, ext, sizeof(ext));
|
||||
if (!stricmp(ext, "pak") || !stricmp(ext, "pk3"))
|
||||
if (!stricmp(ext, "pak") || !stricmp(ext, "pk3") || !stricmp(ext, "zip"))
|
||||
FS_UnloadPackFiles(); //we reload them after
|
||||
#ifdef PLUGINS
|
||||
if ((!stricmp(ext, "dll") || !stricmp(ext, "so")) && !Q_strncmp(dep->name, PLUGINPREFIX, strlen(PLUGINPREFIX)))
|
||||
Cmd_ExecuteString(va("plug_close %s\n", dep->name), RESTRICT_LOCAL); //try to purge plugins so there's no files left open
|
||||
#endif
|
||||
|
||||
nfl = DPF_NATIVE;
|
||||
if (*p->gamedir)
|
||||
if (dep->dtype == DEP_CACHEFILE)
|
||||
{
|
||||
char temp[MAX_OSPATH];
|
||||
destname = va("%s/%s", p->gamedir, dep->name);
|
||||
if (PM_TryGenCachedName(destname, p, temp, sizeof(temp)))
|
||||
{
|
||||
nfl = DPF_CACHED;
|
||||
destname = va("%s", temp);
|
||||
}
|
||||
nfl = DPF_CACHED;
|
||||
destname = va("downloads/%s", dep->name);
|
||||
}
|
||||
else
|
||||
destname = dep->name;
|
||||
{
|
||||
nfl = DPF_NATIVE;
|
||||
if (!*p->gamedir) //basedir
|
||||
destname = dep->name;
|
||||
else
|
||||
{
|
||||
char temp[MAX_OSPATH];
|
||||
destname = va("%s/%s", p->gamedir, dep->name);
|
||||
if (PM_TryGenCachedName(destname, p, temp, sizeof(temp)))
|
||||
{
|
||||
nfl = DPF_CACHED;
|
||||
destname = va("%s", temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (p->flags & DPF_MARKED)
|
||||
nfl |= DPF_ENABLED;
|
||||
nfl |= (p->flags & ~(DPF_CACHED|DPF_NATIVE|DPF_CORRUPT));
|
||||
|
@ -3319,18 +3518,21 @@ static char *PM_GetTempName(package_t *p)
|
|||
//always favour the file so that we can rename safely without needing a copy.
|
||||
for (dep = p->deps, fdep = NULL; dep; dep = dep->next)
|
||||
{
|
||||
if (dep->dtype != DEP_FILE)
|
||||
continue;
|
||||
if (fdep)
|
||||
if (dep->dtype == DEP_FILE || dep->dtype == DEP_CACHEFILE)
|
||||
{
|
||||
fdep = NULL;
|
||||
break;
|
||||
if (fdep)
|
||||
{
|
||||
fdep = NULL;
|
||||
break;
|
||||
}
|
||||
fdep = dep;
|
||||
}
|
||||
fdep = dep;
|
||||
}
|
||||
if (fdep)
|
||||
{
|
||||
if (*p->gamedir)
|
||||
if (fdep->dtype == DEP_CACHEFILE)
|
||||
destname = va("downloads/%s.tmp", fdep->name);
|
||||
else if (*p->gamedir)
|
||||
destname = va("%s/%s.tmp", p->gamedir, fdep->name);
|
||||
else
|
||||
destname = va("%s.tmp", fdep->name);
|
||||
|
@ -3478,12 +3680,12 @@ static qboolean PM_SignatureOkay(package_t *p)
|
|||
|
||||
for (dep = p->deps; dep; dep = dep->next)
|
||||
{
|
||||
if (dep->dtype != DEP_FILE)
|
||||
if (dep->dtype != DEP_FILE && dep->dtype != DEP_CACHEFILE)
|
||||
continue;
|
||||
|
||||
//only allow .pak/.pk3/.zip without a signature, and only when they have a qhash specified (or the .fmf specified it without a qhash...).
|
||||
COM_FileExtension(dep->name, ext, sizeof(ext));
|
||||
if ((!stricmp(ext, "pak") || !stricmp(ext, "pk3") || !stricmp(ext, "zip")) && (p->qhash || (p->flags&DPF_MANIFEST)))
|
||||
if ((!stricmp(ext, "pak") || !stricmp(ext, "pk3") || !stricmp(ext, "zip")) && (p->qhash || dep->dtype == DEP_CACHEFILE || (p->flags&DPF_MANIFEST)))
|
||||
;
|
||||
else
|
||||
return false;
|
||||
|
@ -3537,12 +3739,64 @@ int PM_IsApplying(qboolean listsonly)
|
|||
}
|
||||
|
||||
#ifdef WEBCLIENT
|
||||
static size_t PM_AddFilePackage(const char *packagename, struct gamepacks *gp, size_t numgp)
|
||||
{
|
||||
size_t found = 0;
|
||||
struct packagedep_s *dep;
|
||||
package_t *p = PM_FindPackage(packagename);
|
||||
if (!p)
|
||||
return 0;
|
||||
|
||||
if (found < numgp)
|
||||
{
|
||||
gp[found].path = NULL;
|
||||
gp[found].url = p->mirror[0];
|
||||
for (dep = p->deps; dep; dep = dep->next)
|
||||
{
|
||||
if (dep->dtype == DEP_CACHEFILE)
|
||||
gp[found].path = Z_StrDupf("downloads/%s", dep->name);
|
||||
}
|
||||
if (gp[found].path && gp[found].url)
|
||||
{
|
||||
gp[found].subpath = p->packprefix;
|
||||
found++;
|
||||
}
|
||||
}
|
||||
for (dep = p->deps; dep; dep = dep->next)
|
||||
{
|
||||
if (dep->dtype == DEP_REQUIRE)
|
||||
found += PM_AddFilePackage(dep->name, gp+found, numgp);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
static void PM_DownloadsCompleted(int iarg, void *data)
|
||||
{ //if something installed, then make sure everything is reconfigured properly.
|
||||
if (pm_packagesinstalled)
|
||||
if (pm_packagesinstalled || pm_onload.package || pm_onload.map)
|
||||
{
|
||||
pm_packagesinstalled = false;
|
||||
FS_ChangeGame(fs_manifest, true, false);
|
||||
|
||||
if (pm_onload.package)
|
||||
{
|
||||
package_t *p = PM_FindPackage(pm_onload.package);
|
||||
char *map = pm_onload.map;
|
||||
|
||||
struct gamepacks packs[64];
|
||||
size_t usedpacks;
|
||||
pm_onload.map = NULL;
|
||||
|
||||
usedpacks = PM_AddFilePackage(pm_onload.package, packs, countof(packs)-1);
|
||||
packs[usedpacks].path = NULL;
|
||||
packs[usedpacks].subpath = NULL;
|
||||
packs[usedpacks].url = NULL;
|
||||
|
||||
Z_Free(pm_onload.package);
|
||||
pm_onload.package = NULL;
|
||||
COM_Gamedir(p->gamedir, packs);
|
||||
Cbuf_InsertText(va("map %s\n", map), RESTRICT_LOCAL, false);
|
||||
Z_Free(map);
|
||||
}
|
||||
else
|
||||
FS_ChangeGame(fs_manifest, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3696,6 +3950,39 @@ static void PM_StartADownload(void)
|
|||
//clear the updating flag once there's no more activity needed
|
||||
pkg_updating = downloading;
|
||||
}
|
||||
qboolean PM_LoadMapPackage(const char *package)
|
||||
{
|
||||
struct packagedep_s *dep;
|
||||
package_t *p = PM_FindPackage(package);
|
||||
if (!p)
|
||||
return false;
|
||||
|
||||
if (p->flags & DPF_PRESENT)
|
||||
return true; //okay, already installed.
|
||||
if (p->extract == EXTRACT_ZIP)
|
||||
return false;
|
||||
|
||||
//refuse to use weird packages.
|
||||
for (dep = p->deps; dep; dep = dep->next)
|
||||
{
|
||||
if (dep->dtype == DEP_FILE)
|
||||
return false;
|
||||
}
|
||||
|
||||
//make sure its downloaded. fs code will be asked to explicitly use it, so no need to activate it.
|
||||
p->trymirrors = ~0u;
|
||||
return true;
|
||||
}
|
||||
void PM_LoadMap(const char *package, const char *map)
|
||||
{
|
||||
if (!PM_LoadMapPackage(package))
|
||||
return;
|
||||
pm_onload.package = Z_StrDup(package);
|
||||
pm_onload.map = Z_StrDup(map);
|
||||
|
||||
pkg_updating = true;
|
||||
PM_StartADownload();
|
||||
}
|
||||
#endif
|
||||
//'just' starts doing all the things needed to remove/install selected packages
|
||||
void PM_ApplyChanges(void)
|
||||
|
@ -3726,11 +4013,11 @@ void PM_ApplyChanges(void)
|
|||
|
||||
for (dep = p->deps; dep; dep = dep->next)
|
||||
{
|
||||
if (dep->dtype == DEP_FILE)
|
||||
if (dep->dtype == DEP_FILE || dep->dtype == DEP_CACHEFILE)
|
||||
{
|
||||
char ext[8];
|
||||
COM_FileExtension(dep->name, ext, sizeof(ext));
|
||||
if (!stricmp(ext, "pak") || !stricmp(ext, "pk3"))
|
||||
if (!stricmp(ext, "pak") || !stricmp(ext, "pk3") || !stricmp(ext, "zip"))
|
||||
reloadpacks = true;
|
||||
|
||||
#ifdef PLUGINS //when disabling/purging plugins, be sure to unload them first (unfortunately there might be some latency before this can actually happen).
|
||||
|
@ -3748,7 +4035,13 @@ void PM_ApplyChanges(void)
|
|||
Con_Printf("Purging package %s\n", p->name);
|
||||
for (dep = p->deps; dep; dep = dep->next)
|
||||
{
|
||||
if (dep->dtype == DEP_FILE)
|
||||
if (dep->dtype == DEP_CACHEFILE)
|
||||
{
|
||||
char *f = va("downloads/%s", dep->name);
|
||||
if (!FS_Remove(f, p->fsroot))
|
||||
p->flags |= DPF_CACHED;
|
||||
}
|
||||
else if (dep->dtype == DEP_FILE)
|
||||
{
|
||||
if (*p->gamedir)
|
||||
{
|
||||
|
@ -3856,7 +4149,7 @@ void PM_ApplyChanges(void)
|
|||
if (p->mirror[i])
|
||||
break;
|
||||
for (dep = p->deps; dep; dep=dep->next)
|
||||
if (dep->dtype == DEP_FILE)
|
||||
if (dep->dtype == DEP_FILE||dep->dtype == DEP_CACHEFILE)
|
||||
break;
|
||||
if (!dep && i == countof(p->mirror))
|
||||
{ //this appears to be a meta package with no download
|
||||
|
@ -4047,25 +4340,6 @@ qboolean PM_CanInstall(const char *packagename)
|
|||
return false;
|
||||
}
|
||||
|
||||
static int QDECL sortpackages(const void *l, const void *r)
|
||||
{
|
||||
const package_t *a=*(package_t*const*)l, *b=*(package_t*const*)r;
|
||||
const char *ac, *bc;
|
||||
int order;
|
||||
|
||||
//sort by categories
|
||||
ac = a->category?a->category:"";
|
||||
bc = b->category?b->category:"";
|
||||
order = strcmp(ac,bc);
|
||||
if (order)
|
||||
return order;
|
||||
|
||||
//otherwise sort by title.
|
||||
ac = a->title?a->title:a->name;
|
||||
bc = b->title?b->title:b->name;
|
||||
order = strcmp(ac,bc);
|
||||
return order;
|
||||
}
|
||||
void PM_Command_f(void)
|
||||
{
|
||||
package_t *p;
|
||||
|
@ -4155,7 +4429,7 @@ void PM_Command_f(void)
|
|||
continue;
|
||||
sorted[count++] = p;
|
||||
}
|
||||
qsort(sorted, count, sizeof(*sorted), sortpackages);
|
||||
qsort(sorted, count, sizeof(*sorted), PM_PackageSortOrdering);
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
char quoted[8192];
|
||||
|
@ -4673,6 +4947,7 @@ void PM_AddManifestPackages(ftemanifest_t *man)
|
|||
continue;
|
||||
}
|
||||
|
||||
PM_ResortPackages();
|
||||
PM_ApplyChanges();
|
||||
}
|
||||
|
||||
|
@ -4821,6 +5096,10 @@ static void MD_Draw (int x, int y, struct menucustom_s *c, struct emenu_s *m)
|
|||
{
|
||||
package_t *p;
|
||||
char *n;
|
||||
|
||||
if (y + 8 < 0 || y >= vid.height) //small optimisation.
|
||||
return;
|
||||
|
||||
if (c->dint != downloadablessequence)
|
||||
return; //probably stale
|
||||
p = c->dptr;
|
||||
|
@ -5027,18 +5306,21 @@ static qboolean MD_Key (struct menucustom_s *c, struct emenu_s *m, int key, unsi
|
|||
{
|
||||
if (p == p2)
|
||||
continue;
|
||||
if (strcmp(p->gamedir, p2->gamedir))
|
||||
continue; //different gamedirs. don't screw up.
|
||||
for (dep = p->deps; dep; dep = dep->next)
|
||||
{
|
||||
if (dep->dtype != DEP_FILE)
|
||||
continue;
|
||||
for (dep2 = p2->deps; dep2; dep2 = dep2->next)
|
||||
if (dep->dtype == DEP_FILE)
|
||||
{
|
||||
if (dep2->dtype != DEP_FILE)
|
||||
continue;
|
||||
if (!strcmp(dep->name, dep2->name))
|
||||
for (dep2 = p2->deps; dep2; dep2 = dep2->next)
|
||||
{
|
||||
PM_UnmarkPackage(p2, DPF_MARKED);
|
||||
break;
|
||||
if (dep2->dtype != DEP_FILE)
|
||||
continue;
|
||||
if (!strcmp(dep->name, dep2->name))
|
||||
{
|
||||
PM_UnmarkPackage(p2, DPF_MARKED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5242,6 +5524,8 @@ static int MD_AddItemsToDownloadMenu(emenu_t *m, int y, const char *pathprefix)
|
|||
continue;
|
||||
if ((p->flags & DPF_HIDDEN) && (p->arch || !(p->flags & DPF_ENABLED)))
|
||||
continue;
|
||||
if ((p->flags & DPF_HIDEUNLESSPRESENT) && !(p->flags & DPF_PRESENT))
|
||||
continue;
|
||||
slash = strchr(p->category+prefixlen, '/');
|
||||
if (!slash)
|
||||
{
|
||||
|
@ -5317,6 +5601,8 @@ static int MD_AddItemsToDownloadMenu(emenu_t *m, int y, const char *pathprefix)
|
|||
continue;
|
||||
if ((p->flags & DPF_HIDDEN) && (p->arch || !(p->flags & DPF_ENABLED)))
|
||||
continue;
|
||||
if ((p->flags & DPF_HIDEUNLESSPRESENT) && !(p->flags & DPF_PRESENT))
|
||||
continue;
|
||||
|
||||
slash = strchr(p->category+prefixlen, '/');
|
||||
if (slash)
|
||||
|
|
|
@ -798,6 +798,7 @@ struct gamepacks
|
|||
char *subpath; //within the package (for zips)
|
||||
};
|
||||
void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths);
|
||||
qboolean FS_PathURLCache(const char *url, char *path, size_t pathsize); //converts a url to something that can be shoved into a filesystem
|
||||
qboolean FS_GamedirIsOkay(const char *path);
|
||||
char *FS_GetGamedir(qboolean publicpathonly);
|
||||
char *FS_GetManifestArgs(void);
|
||||
|
|
|
@ -3139,12 +3139,19 @@ void FS_AddHashedPackage(searchpath_t **oldpaths, const char *parentpath, const
|
|||
continue;
|
||||
if ((loadstuff & (1<<fmt)) && !Q_strcasecmp(ext, searchpathformats[fmt].extension))
|
||||
{
|
||||
|
||||
//figure out the logical path names
|
||||
if (!FS_GenCachedPakName(pakpath, qhash, pname, sizeof(pname)))
|
||||
return; //file name was invalid, panic.
|
||||
snprintf (lname, sizeof(lname), "%s%s", logicalpaths, pname+ptlen+1);
|
||||
snprintf (lname2, sizeof(lname), "%s%s", logicalpaths, pakpath+ptlen+1);
|
||||
if (!search)
|
||||
{
|
||||
FS_NativePath(pname, FS_ROOT, lname, sizeof(lname));
|
||||
FS_NativePath(pakpath, FS_ROOT, lname2, sizeof(lname2));
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf (lname, sizeof(lname), "%s%s", logicalpaths, pname+ptlen+1);
|
||||
snprintf (lname2, sizeof(lname), "%s%s", logicalpaths, pakpath+ptlen+1);
|
||||
}
|
||||
|
||||
//see if we already added it
|
||||
for (oldp = com_searchpaths; oldp; oldp = oldp->next)
|
||||
|
|
|
@ -76,6 +76,9 @@ void PM_LoadPackages(searchpath_t **oldpaths, const char *parent_pure, const cha
|
|||
void *PM_GeneratePackageFromMeta(vfsfile_t *file, char *fname, size_t fnamesize, enum fs_relative *fsroot);
|
||||
void PM_FileInstalled(const char *filename, enum fs_relative fsroot, void *metainfo, qboolean enable); //we finished installing a file via some other mechanism (drag+drop or from server. insert it into the updates menu.
|
||||
void PM_EnumeratePlugins(void (*callback)(const char *name));
|
||||
struct xcommandargcompletioncb_s;
|
||||
void PM_EnumerateMaps(const char *partial, struct xcommandargcompletioncb_s *ctx);
|
||||
void PM_LoadMap(const char *package, const char *map);
|
||||
int PM_IsApplying(qboolean listsonly);
|
||||
unsigned int PM_MarkUpdates (void); //mark new/updated packages as needing install.
|
||||
void PM_ApplyChanges(void); //for -install/-doinstall args
|
||||
|
@ -85,6 +88,14 @@ qboolean PM_FindUpdatedEngine(char *syspath, size_t syspathsize); //names the en
|
|||
void PM_AddManifestPackages(ftemanifest_t *man);
|
||||
void Menu_Download_Update(void);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *description;
|
||||
void (*Update) (const char *url, vfsfile_t *out);
|
||||
#define plugupdatesourcefuncs_name "UpdateSource"
|
||||
} plugupdatesourcefuncs_t;
|
||||
qboolean PM_RegisterUpdateSource(void *module, plugupdatesourcefuncs_t *funcs);
|
||||
|
||||
int FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man), void *usr);
|
||||
|
||||
struct modlist_s
|
||||
|
|
|
@ -406,6 +406,10 @@ static qboolean QDECL PlugBI_ExportInterface(const char *name, void *interfacept
|
|||
#ifdef HAVE_CLIENT
|
||||
if (!strcmp(name, plugvrfuncs_name))
|
||||
return R_RegisterVRDriver(currentplug, interfaceptr);
|
||||
#endif
|
||||
#ifdef PACKAGEMANAGER
|
||||
if (!strcmp(name, plugupdatesourcefuncs_name))
|
||||
return PM_RegisterUpdateSource(currentplug, interfaceptr);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
@ -1117,7 +1121,6 @@ static void QDECL Plug_Net_Close(qhandle_t handle)
|
|||
}
|
||||
|
||||
#if defined(HAVE_SERVER) && defined(HAVE_CLIENT)
|
||||
qboolean FS_PathURLCache(const char *url, char *path, size_t pathsize);
|
||||
static qboolean QDECL Plug_MapLog_Query(const char *packagename, const char *mapname, float *vals)
|
||||
{
|
||||
if (!strncmp(packagename, "http://", 7) || !strncmp(packagename, "https://", 8))
|
||||
|
@ -1532,6 +1535,9 @@ void Plug_Close(plugin_t *plug)
|
|||
S_UnregisterSoundInputModule(plug);
|
||||
#endif
|
||||
NET_RegisterCrypto(plug, NULL);
|
||||
#ifdef PACKAGEMANAGER
|
||||
PM_RegisterUpdateSource(currentplug, NULL);
|
||||
#endif
|
||||
FS_UnRegisterFileSystemModule(plug);
|
||||
Mod_UnRegisterAllModelFormats(plug);
|
||||
|
||||
|
@ -1791,11 +1797,14 @@ static void *QDECL PlugBI_GetEngineInterface(const char *interfacename, size_t s
|
|||
{
|
||||
static plugcmdfuncs_t funcs =
|
||||
{
|
||||
Plug_Cmd_AddCommand,
|
||||
COM_QuotedString,
|
||||
COM_ParseType,
|
||||
COM_ParseTokenOut,
|
||||
Plug_Cmd_TokenizeString,
|
||||
Plug_Cmd_Args,
|
||||
Plug_Cmd_Argv,
|
||||
Plug_Cmd_Argc,
|
||||
Plug_Cmd_AddCommand,
|
||||
Plug_Cmd_AddText,
|
||||
};
|
||||
if (structsize == sizeof(funcs))
|
||||
|
|
|
@ -1798,6 +1798,9 @@ typedef struct
|
|||
void *mutex;
|
||||
int refs;
|
||||
qboolean terminate; //one end has closed, make the other report failures now that its no longer needed.
|
||||
|
||||
void *ctx;
|
||||
void (*callback) (void *ctx, vfsfile_t *pipe);
|
||||
} vfspipe_t;
|
||||
|
||||
static qboolean QDECL VFSPIPE_Close(vfsfile_t *f)
|
||||
|
@ -1814,6 +1817,8 @@ static qboolean QDECL VFSPIPE_Close(vfsfile_t *f)
|
|||
Sys_DestroyMutex(p->mutex);
|
||||
free(p);
|
||||
}
|
||||
else if (p->callback)
|
||||
p->callback(p->ctx, f);
|
||||
return true;
|
||||
}
|
||||
static qofs_t QDECL VFSPIPE_GetLen(vfsfile_t *f)
|
||||
|
@ -1892,7 +1897,7 @@ static int QDECL VFSPIPE_WriteBytes(vfsfile_t *f, const void *buffer, int len)
|
|||
return len;
|
||||
}
|
||||
|
||||
vfsfile_t *VFSPIPE_Open(int refs, qboolean seekable)
|
||||
vfsfile_t *VFS_OpenPipeInternal(int refs, qboolean seekable, void (*callback)(void *ctx, vfsfile_t *file), void *ctx)
|
||||
{
|
||||
vfspipe_t *newf;
|
||||
newf = malloc(sizeof(*newf));
|
||||
|
@ -1909,6 +1914,9 @@ vfsfile_t *VFSPIPE_Open(int refs, qboolean seekable)
|
|||
newf->funcs.ReadBytes = VFSPIPE_ReadBytes;
|
||||
newf->funcs.WriteBytes = VFSPIPE_WriteBytes;
|
||||
|
||||
newf->ctx = ctx;
|
||||
newf->callback = callback;
|
||||
|
||||
if (seekable)
|
||||
{ //if this is set, then we allow changing the readpos at the expense of buffering the ENTIRE file. no more fifo.
|
||||
newf->funcs.Seek = VFSPIPE_Seek;
|
||||
|
@ -1924,3 +1932,12 @@ vfsfile_t *VFSPIPE_Open(int refs, qboolean seekable)
|
|||
|
||||
return &newf->funcs;
|
||||
}
|
||||
vfsfile_t *VFS_OpenPipeCallback(void (*callback)(void*ctx, vfsfile_t *file), void *ctx)
|
||||
{
|
||||
return VFS_OpenPipeInternal(2, false, callback, ctx);
|
||||
}
|
||||
|
||||
vfsfile_t *VFSPIPE_Open(int refs, qboolean seekable)
|
||||
{
|
||||
return VFS_OpenPipeInternal(refs, seekable, NULL, NULL);
|
||||
}
|
||||
|
|
|
@ -139,6 +139,7 @@ struct dl_download
|
|||
};
|
||||
|
||||
vfsfile_t *VFSPIPE_Open(int refs, qboolean seekable); //refs should be 1 or 2, to say how many times it must be closed before its actually closed, so both ends can close separately
|
||||
vfsfile_t *VFS_OpenPipeCallback(void (*callback)(void*ctx, vfsfile_t *file), void *ctx);
|
||||
void HTTP_CL_Think(const char **fname, float *percent);
|
||||
void HTTP_CL_Terminate(void); //kills all active downloads
|
||||
unsigned int HTTP_CL_GetActiveDownloads(void);
|
||||
|
|
|
@ -19,6 +19,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
*/
|
||||
#include "quakedef.h"
|
||||
#include "pr_common.h"
|
||||
#include "fs.h"
|
||||
|
||||
#ifndef CLIENTONLY
|
||||
|
||||
|
@ -491,6 +492,10 @@ static void SV_Map_c(int argn, const char *partial, struct xcommandargcompletion
|
|||
COM_EnumerateFiles(va("maps/%s*.map.gz", partial), CompleteMapListExt, ctx);
|
||||
COM_EnumerateFiles(va("maps/%s*.cm", partial), CompleteMapList, ctx);
|
||||
COM_EnumerateFiles(va("maps/%s*.hmp", partial), CompleteMapList, ctx);
|
||||
|
||||
#ifdef PACKAGEMANAGER
|
||||
PM_EnumerateMaps(partial, ctx);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -534,6 +539,9 @@ quake2:
|
|||
quake:
|
||||
+ is used in certain map names. * cannot be, but $ potentially could be.
|
||||
|
||||
fte:
|
||||
'map package:mapname' should download the specified map package and load up its maps.
|
||||
|
||||
mvdsv:
|
||||
basemap#modifier.ent files
|
||||
|
||||
|
@ -616,6 +624,20 @@ void SV_Map_f (void)
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef PACKAGEMANAGER
|
||||
if (Cmd_Argc() == 2)
|
||||
{
|
||||
char *mangled = Cmd_Argv(1);
|
||||
char *sep = strchr(mangled, ':');
|
||||
if (sep)
|
||||
{
|
||||
*sep++ = 0;
|
||||
PM_LoadMap(mangled, sep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Q_strncpyz (level, Cmd_Argv(1), sizeof(level));
|
||||
startspot = ((Cmd_Argc() == 2)?NULL:Cmd_Argv(2));
|
||||
}
|
||||
|
|
|
@ -216,15 +216,21 @@ typedef struct //subconsole handling
|
|||
#define plugsubconsolefuncs_name "SubConsole"
|
||||
} plugsubconsolefuncs_t;
|
||||
|
||||
enum com_tokentype_e;
|
||||
typedef struct //console command/tokenizing/cbuf functions
|
||||
{
|
||||
F(qboolean, AddCommand, (const char *buffer)); //Registers a console command.
|
||||
F(const char *,QuotedString, (const char *string, char *buf, int buflen, qboolean omitquotes)); //generates a string with c-style markup and relevant quote types.
|
||||
F(char *, ParseToken, (const char *data, char *token, size_t tokenlen, enum com_tokentype_e *tokentype)); //standard quake-style token parsing.
|
||||
F(char *, ParsePunctuation, (const char *data, const char *punctuation, char *token, size_t tokenlen, enum com_tokentype_e *tokentype)); //use explicit punctuation.
|
||||
|
||||
F(void, TokenizeString, (const char *msg)); //tokenize a string.
|
||||
|
||||
F(void, Args, (char *buffer, int bufsize)); //Gets the extra args
|
||||
F(void, Argv, (int argnum, char *buffer, size_t bufsize)); //Gets a 0-based token
|
||||
F(int, Argc, (void)); //gets the number of tokens available.
|
||||
|
||||
F(qboolean, AddCommand, (const char *cmdname)); //Registers a console command.
|
||||
|
||||
F(void, AddText, (const char *text, qboolean insert));
|
||||
#define plugcmdfuncs_name "Cmd"
|
||||
} plugcmdfuncs_t;
|
||||
|
|
229
plugins/qi/qi.c
229
plugins/qi/qi.c
|
@ -1,3 +1,5 @@
|
|||
#include "quakedef.h"
|
||||
#include "fs.h"
|
||||
#include "../plugin.h"
|
||||
static plugsubconsolefuncs_t *confuncs;
|
||||
static plugfsfuncs_t *filefuncs;
|
||||
|
@ -8,6 +10,7 @@ static plugclientfuncs_t *clientfuncs;
|
|||
#define DATABASEURL "https://www.quaddicted.com/reviews/quaddicted_database.xml"
|
||||
#define FILEIMAGEURL "https://www.quaddicted.com/reviews/screenshots/%s_injector.jpg"
|
||||
#define FILEDOWNLOADURL "https://www.quaddicted.com/filebase/%s.zip"
|
||||
#define FILEREVIEWURL "https://www.quaddicted.com/reviews/%s.html"
|
||||
#define WINDOWTITLE "Quaddicted Map+Mod Archive"
|
||||
#define WINDOWNAME "QI"
|
||||
|
||||
|
@ -30,6 +33,7 @@ static plugclientfuncs_t *clientfuncs;
|
|||
|
||||
static xmltree_t *thedatabase;
|
||||
static qhandle_t dlcontext = -1;
|
||||
static vfsfile_t *packagemanager;
|
||||
|
||||
static struct
|
||||
{
|
||||
|
@ -102,7 +106,7 @@ static qboolean QI_SetupWindow(const char *console, qboolean force)
|
|||
confuncs->SetActive(console);
|
||||
return true;
|
||||
}
|
||||
static void QI_DeHTML(const char *in, char *out, size_t outsize)
|
||||
static void QI_DeHTML(const char *in, qboolean escapes, char *out, size_t outsize)
|
||||
{
|
||||
outsize--;
|
||||
while(*in && outsize > 0)
|
||||
|
@ -175,6 +179,12 @@ static void QI_DeHTML(const char *in, char *out, size_t outsize)
|
|||
outsize--;
|
||||
}
|
||||
}
|
||||
else if ((*in == '\"') && escapes && outsize >= 2)
|
||||
{
|
||||
*out++ = '\\';
|
||||
*out++ = *in++;
|
||||
outsize-=2;
|
||||
}
|
||||
else
|
||||
{
|
||||
*out++ = *in++;
|
||||
|
@ -339,7 +349,7 @@ static void QI_RefreshMapList(qboolean forcedisplay)
|
|||
year += 1900;
|
||||
Q_snprintf(descbuf, sizeof(descbuf), "^aId:^a %s\n^aAuthor(s):^a %s\n^aDate:^a %04u-%02u-%02u\n^aRating:^a %s\n\n", id, author, year, month, day, ratingtext);
|
||||
|
||||
QI_DeHTML(desc, descbuf + strlen(descbuf), sizeof(descbuf) - strlen(descbuf));
|
||||
QI_DeHTML(desc, false, descbuf + strlen(descbuf), sizeof(descbuf) - strlen(descbuf));
|
||||
desc = descbuf;
|
||||
|
||||
Con_SubPrintf(console, "%s %s ^[^4%s: ^1%s\\tip\\%s\\tipimg\\"FILEIMAGEURL"\\id\\%s^]", type, ratingtext, id, XML_GetChildBody(file, "title", "<NO TITLE>"), desc, id, id);
|
||||
|
@ -526,6 +536,187 @@ static void QI_RunMap(xmltree_t *qifile, const char *map)
|
|||
cmdfuncs->AddText("\n", false);
|
||||
}
|
||||
|
||||
|
||||
void VARGS VFS_PRINTF(vfsfile_t *vf, const char *format, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
char string[1024];
|
||||
|
||||
va_start (argptr, format);
|
||||
vsnprintf (string,sizeof(string)-1, format,argptr);
|
||||
va_end (argptr);
|
||||
|
||||
VFS_PUTS(vf, string);
|
||||
}
|
||||
static void QI_WriteUpdateList(vfsfile_t *pminfo)
|
||||
{
|
||||
xmltree_t *file;
|
||||
char descbuf[1024], *d, *nl;
|
||||
|
||||
if (thedatabase)
|
||||
for (file = thedatabase->child; file; file = file->sibling)
|
||||
{
|
||||
const char *id = XML_GetParameter(file, "id", "unnamed");
|
||||
const char *rating = XML_GetParameter(file, "rating", "");
|
||||
int ratingnum = atoi(rating);
|
||||
const char *author = XML_GetChildBody(file, "author", "unknown");
|
||||
const char *desc = XML_GetChildBody(file, "description", "<NO DESCRIPTION>");
|
||||
const char *type = XML_GetParameter(file, "type", "");
|
||||
const char *date = XML_GetChildBody(file, "date", "1.1.1990");
|
||||
xmltree_t *tech = XML_ChildOfTree(file, "techinfo", 0);
|
||||
const char *cmdline = XML_GetChildBody(tech, "commandline", "");
|
||||
const char *zipbasedir = XML_GetChildBody(tech, "zipbasedir", "");
|
||||
int year, month, day;
|
||||
int startmapnum, i;
|
||||
char ratingtext[65];
|
||||
char gamedir[65];
|
||||
char prefix[MAX_QPATH];
|
||||
xmltree_t *startmap;
|
||||
if (strcmp(file->name, "file"))
|
||||
continue; //erk?
|
||||
if (atoi(XML_GetParameter(file, "hide", "")) || atoi(XML_GetParameter(file, "fte_hide", "")))
|
||||
continue;
|
||||
switch(atoi(type))
|
||||
{
|
||||
case 1:
|
||||
type = "map"; //'single map file(s)'
|
||||
break;
|
||||
case 2:
|
||||
type = "mod"; //'Partial conversion'
|
||||
break;
|
||||
case 4:
|
||||
type = "spd"; //'speedmapping'
|
||||
break;
|
||||
case 5:
|
||||
type = "otr"; //'misc files'
|
||||
break;
|
||||
default:
|
||||
type = "???"; //no idea
|
||||
break;
|
||||
}
|
||||
|
||||
if (ratingnum > (sizeof(ratingtext)-5)/6)
|
||||
ratingnum = (sizeof(ratingtext)-5)/6;
|
||||
if (ratingnum)
|
||||
{
|
||||
Q_snprintf(ratingtext, sizeof(ratingtext), "^a");
|
||||
for (i = 0; i < ratingnum; i++)
|
||||
Q_snprintf(ratingtext + i+2, sizeof(ratingtext)-i*2+2, "*");
|
||||
Q_snprintf(ratingtext + i+2, sizeof(ratingtext)-i*2+2, "^a");
|
||||
}
|
||||
else if (*rating)
|
||||
Q_snprintf(ratingtext, sizeof(ratingtext), "%s", rating);
|
||||
else
|
||||
Q_snprintf(ratingtext, sizeof(ratingtext), "%s", "unrated");
|
||||
|
||||
|
||||
day = atoi(date?date:"1");
|
||||
date = date?strchr(date, '.'):NULL;
|
||||
month = atoi(date?date+1:"1");
|
||||
date = date?strchr(date+1, '.'):NULL;
|
||||
year = atoi(date?date+1:"1990");
|
||||
if (year < 90)
|
||||
year += 2000;
|
||||
else if (year < 1900)
|
||||
year += 1900;
|
||||
|
||||
strcpy(gamedir, "id1");
|
||||
while (cmdline)
|
||||
{
|
||||
cmdline = cmdfuncs->ParseToken(cmdline, descbuf, sizeof(descbuf), NULL);
|
||||
if (!strcmp(descbuf, "-game"))
|
||||
cmdline = cmdfuncs->ParseToken(cmdline, gamedir, sizeof(gamedir), NULL);
|
||||
else if (!strcmp(descbuf, "-hipnotic") || !strcmp(descbuf, "-rogue") || !strcmp(descbuf, "-quoth"))
|
||||
{
|
||||
if (!*gamedir)
|
||||
strcpy(gamedir, descbuf+1);
|
||||
}
|
||||
}
|
||||
if (!*gamedir)
|
||||
continue; //bad package
|
||||
|
||||
|
||||
*descbuf = 0;
|
||||
QI_DeHTML(desc, true, descbuf + strlen(descbuf), sizeof(descbuf) - strlen(descbuf));
|
||||
desc = descbuf;
|
||||
|
||||
VFS_PRINTF(pminfo, "{\n"
|
||||
"\tpackage \"qi_%s\"\n"
|
||||
"\ttitle \"%s %s\"\n"
|
||||
"\tcategory \"Quaddicted - %i\"\n"
|
||||
"\tlicense \"Unknown\"\n"
|
||||
"\tauthor \"Unknown\"\n"
|
||||
"\tqhash \"\"\n"
|
||||
,id, ratingtext, XML_GetChildBody(file, "title", "<NO TITLE>"), year);
|
||||
|
||||
VFS_PRINTF(pminfo, "\tgamedir \"%s\"\n", gamedir);
|
||||
|
||||
VFS_PRINTF(pminfo, "\tver %04u-%02u-%02u\n", year, month, day);
|
||||
if (!strchr(author, '\"') && !strchr(author, '\n'))
|
||||
VFS_PRINTF(pminfo, "\tauthor \"%s\"\n", author);
|
||||
VFS_PRINTF(pminfo, "\twebsite \""FILEREVIEWURL"\"\n", id); //in lieu of an actual site, lets fill it with quaddicted's reviews.
|
||||
|
||||
VFS_PRINTF(pminfo, "\turl \""FILEDOWNLOADURL"\"\n", id);
|
||||
|
||||
|
||||
//skip any dodgy leading slashes
|
||||
while (*zipbasedir == '/' || *zipbasedir == '\\')
|
||||
zipbasedir++;
|
||||
if (!*zipbasedir)
|
||||
strcpy(prefix, ".."); //err, there wasn't a directory... we still need to 'strip' it though.
|
||||
else
|
||||
{
|
||||
//skip the zip's gamedir
|
||||
while (*zipbasedir && *zipbasedir != '/' && *zipbasedir != '\\')
|
||||
zipbasedir++;
|
||||
//skip any trailing
|
||||
while (*zipbasedir == '/' || *zipbasedir == '\\')
|
||||
zipbasedir++;
|
||||
for (i = 0; *zipbasedir; i++)
|
||||
{
|
||||
if (i >= sizeof(prefix)-1)
|
||||
break;
|
||||
if (*zipbasedir == '\\') //sigh
|
||||
prefix[i] = '/';
|
||||
else if (*zipbasedir == '\"' || *zipbasedir == '\n' || *zipbasedir == '\r')
|
||||
break; //bad char...
|
||||
else
|
||||
prefix[i] = *zipbasedir;
|
||||
zipbasedir++;
|
||||
}
|
||||
while (i > 0 && prefix[i-1] == '/')
|
||||
i--;
|
||||
prefix[i] = 0;
|
||||
}
|
||||
if (*prefix)
|
||||
VFS_PRINTF(pminfo, "\tpackprefix \"%s\"\n", prefix);
|
||||
|
||||
for(d = descbuf;;)
|
||||
{
|
||||
nl = strchr(d, '\n');
|
||||
if (nl)
|
||||
*nl++ = 0;
|
||||
VFS_PRINTF(pminfo, "\tdesc \"%s\"\n", d);
|
||||
if (!nl)
|
||||
break;
|
||||
d = nl;
|
||||
}
|
||||
|
||||
//VFS_PRINTF(pminfo, "\tpreview \""FILEIMAGEURL"\"\n", id);
|
||||
for (startmapnum = 0; ; startmapnum++)
|
||||
{
|
||||
startmap = XML_ChildOfTree(tech, "startmap", startmapnum);
|
||||
if (!startmap)
|
||||
break;
|
||||
|
||||
VFS_PRINTF(pminfo, "\tmap \"%s\"\n", startmap->body);
|
||||
}
|
||||
if (!startmapnum) //if there's no start maps listed, use the package's id instead. these are not intended for anything else.
|
||||
VFS_PRINTF(pminfo, "\tmap \"%s\"\n", id);
|
||||
VFS_PRINTF(pminfo, "}\n");
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int QI_GetDate(xmltree_t *file)
|
||||
{
|
||||
unsigned int day, month, year;
|
||||
|
@ -685,8 +876,8 @@ static void QDECL QI_Tick(double realtime, double gametime)
|
|||
{
|
||||
if (dlcontext != -1)
|
||||
{
|
||||
qofs_t flen;
|
||||
if (filefuncs->GetLen(dlcontext, &flen))
|
||||
qofs_t flen=0;
|
||||
if (dlcontext < 0 || filefuncs->GetLen(dlcontext, &flen))
|
||||
{
|
||||
int ofs = 0;
|
||||
char *file;
|
||||
|
@ -729,6 +920,22 @@ static void QDECL QI_Tick(double realtime, double gametime)
|
|||
// XML_ConPrintTree(thedatabase, "quadicted_xml", 0);
|
||||
}
|
||||
}
|
||||
else if (packagemanager)
|
||||
{
|
||||
if (!thedatabase && dlcontext == -1)
|
||||
{
|
||||
dlcontext = -2;
|
||||
//if (filefuncs->Open(DATABASEURL, &dlcontext, 1) >= 0)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
VFS_PRINTF(packagemanager, "version 3\n");
|
||||
QI_WriteUpdateList(packagemanager);
|
||||
}
|
||||
VFS_CLOSE(packagemanager);
|
||||
packagemanager = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int QDECL QI_ConExecuteCommand(qboolean isinsecure)
|
||||
|
@ -774,12 +981,26 @@ static qboolean QI_ExecuteCommand(qboolean isinsecure)
|
|||
return false;
|
||||
}
|
||||
|
||||
void QI_GenPackages(const char *url, vfsfile_t *pipe)
|
||||
{
|
||||
if (packagemanager)
|
||||
VFS_CLOSE(packagemanager);
|
||||
packagemanager = pipe;
|
||||
}
|
||||
static plugupdatesourcefuncs_t sourcefuncs =
|
||||
{
|
||||
"Quaddicted Maps",
|
||||
QI_GenPackages
|
||||
};
|
||||
|
||||
qboolean Plug_Init(void)
|
||||
{
|
||||
confuncs = plugfuncs->GetEngineInterface(plugsubconsolefuncs_name, sizeof(*confuncs));
|
||||
filefuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*filefuncs));
|
||||
clientfuncs = plugfuncs->GetEngineInterface(plugclientfuncs_name, sizeof(*clientfuncs));
|
||||
|
||||
plugfuncs->ExportInterface(plugupdatesourcefuncs_name, &sourcefuncs, sizeof(sourcefuncs));
|
||||
|
||||
if (confuncs && filefuncs && clientfuncs)
|
||||
{
|
||||
filters.minrating = filters.maxrating = -1;
|
||||
|
|
Loading…
Reference in a new issue