Update infrastruture updates.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5904 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2021-06-21 13:46:23 +00:00
parent a12c453c30
commit 26d9d89f24
8 changed files with 729 additions and 118 deletions

112
build_qc.sh Executable file
View file

@ -0,0 +1,112 @@
#!/bin/bash
#FTEQCC and FTEQW must be defined.
#QSS should be defined...
BUILDFOLDER=~/htdocs
BUILDLOGFOLDER=$BUILDFOLDER/build_logs
#this really should use the native cpu type... until then we use 32bit in case anyone's still using a 32bit kernel.
if [ "FTEQW" != "" ]; then
echo "--- QC builds ---"
echo "Making fteextensions.qc"
BASEDIR=~/.fte
GAMEDIR=fte
mkdir -p $BASEDIR/$GAMEDIR/src
#generate fte's extensions
CFG=$BASEDIR/fte/minusargsaresilly.cfg
echo "pr_dumpplatform -o fteextensions" > $CFG
echo "pr_dumpplatform -o csqcsysdefs -Tcs" >> $CFG
echo "pr_dumpplatform -o menusysdefs -Tmenu" >> $CFG
$FTEQW -basedir $BASEDIR -nohome -quake -game $GAMEDIR +set snd_device none -nosound +set vid_renderer sv +exec minusargsaresilly.cfg +quit >> /dev/null
#if we have fteqcc available then try to generate some symbol lists for our generic defs
if [ "$FTEQCC" != "" ]; then
#get QSS to spit out its defs for completeness.
if [ "$QSS" != "" ] && [ -e "$BASEDIR/id1/pak1.pak" ] && [ -e "$QSS" ]; then
CFG=$BASEDIR/$GAMEDIR/gen.cfg
echo "pr_dumpplatform -Oqscsextensions -Tcs" > $CFG
echo "pr_dumpplatform -Oqsextensions -Tss" >> $CFG
echo "pr_dumpplatform -Oqsmenuextensions -Tmenu" >> $CFG
$QSS -dedicated -nohome -game $GAMEDIR -basedir $BASEDIR +exec gen.cfg +quit >>/dev/null
else
echo "QSS not available?"
fi
if [ ! -e "$BASEDIR/fte/src/dpsymbols.src" ]; then
ln -sr quakec/dpsymbols.src $BASEDIR/$GAMEDIR/src/
fi
if [ ! -e "$BASEDIR/fte/src/dpdefs/" ]; then
echo "no dpdefs subdir found in $BASEDIR/fte/src/ ... manual intervention required"
fi
#generate symbol tables from the various engines's defs
( cd $BASEDIR/$GAMEDIR/src
$FTEQCC -Fdumpsymbols -oqsscs.dat qscsextensions.qc && sort -o qss_cs.sym qsscs.dat.sym
$FTEQCC -Fdumpsymbols -oqssss.dat qsextensions.qc && sort -o qss_ss.sym qssss.dat.sym
$FTEQCC -Fdumpsymbols -oqssmn.dat qsmenuextensions.qc && sort -o qss_menu.sym qssmn.dat.sym
$FTEQCC -Fdumpsymbols -oftecs.dat -DCSQC fteextensions.qc && sort -o fte_cs.sym ftecs.dat.sym
$FTEQCC -Fdumpsymbols -oftess.dat -DSSQC fteextensions.qc && sort -o fte_ss.sym ftess.dat.sym
$FTEQCC -Fdumpsymbols -oftemn.dat -DMENU fteextensions.qc && sort -o fte_menu.sym ftemn.dat.sym
$FTEQCC -Fdumpsymbols -odpcs.dat -DCSQC dpsymbols.src && sort -o dp_cs.sym dpcs.dat.sym
$FTEQCC -Fdumpsymbols -odpss.dat -DSSQC dpsymbols.src && sort -o dp_ss.sym dpss.dat.sym
$FTEQCC -Fdumpsymbols -odpmn.dat -DMENU dpsymbols.src && sort -o dp_menu.sym dpmn.dat.sym
) >>/dev/null
#generate generic extensions
CFG=$BASEDIR/fte/minusargsaresilly.cfg
echo "pr_dumpplatform -Ocsqc_api -Fdepfilter -Tsimplecs" > $CFG
echo "pr_dumpplatform -Ofte_csqc_api -Fdepfilter -Tcs" >> $CFG
echo "pr_dumpplatform -Odp_csqc_api -Fdepfilter -Tdpcs" >> $CFG
echo "pr_dumpplatform -Ossqc_api -Fdepfilter -Tnq" >> $CFG
echo "pr_dumpplatform -Omenu_api -Fdepfilter -Tmenu" >> $CFG
$FTEQW -basedir $BASEDIR -nohome -quake -game $GAMEDIR +set snd_device none -nosound +set vid_renderer sv +exec minusargsaresilly.cfg +quit >> /dev/null
fi
#fix up and copy the results somewhere useful
rm $CFG
mkdir -p $BUILDFOLDER/ftedefs $BUILDFOLDER/genericdefs
mv $BASEDIR/$GAMEDIR/src/fteextensions.qc $BUILDFOLDER/ftedefs
mv $BASEDIR/$GAMEDIR/src/csqcsysdefs.qc $BUILDFOLDER/ftedefs
mv $BASEDIR/$GAMEDIR/src/menusysdefs.qc $BUILDFOLDER/ftedefs
mv $BASEDIR/$GAMEDIR/src/*_api.qc $BUILDFOLDER/genericdefs
fi
if [ "$FTEQCC" != "" ]; then
mkdir -p $BUILDFOLDER/csaddon/
( cd quakec/csaddon/src
echo -n "Making csaddon... "
$FTEQCC -srcfile csaddon.src > $BUILDLOGFOLDER/csaddon.txt
if [ $? -eq 0 ]; then
echo "done"
cp ../csaddon.dat $BUILDFOLDER/csaddon/
cd ..
zip -9 $BUILDFOLDER/csaddon/csaddon.pk3 csaddon.dat
else
echo "failed"
fi
)
( cd quakec/menusys
echo -n "Making menusys... "
$FTEQCC -srcfile menu.src > $BUILDLOGFOLDER/menu.txt
if [ $? -eq 0 ]; then
echo "done"
zip -q -9 -o -r $BUILDFOLDER/csaddon/menusys_src.zip .
cp ../menu.dat $BUILDFOLDER/csaddon/
cd ..
zip -9 $BUILDFOLDER/csaddon/menusys.pk3 menu.dat
else
echo "failed"
fi
)
else
echo "Skiping csaddon + qcmenu, no compiler build"
fi

View file

@ -300,57 +300,24 @@ if [ -e "$HOME/nocompat_readme.html" ]; then
cp $HOME/nocompat_readme.html $BUILDFOLDER/nocompat/README.html
fi
#this really should use the native cpu type... until then we use 32bit in case anyone's still using a 32bit kernel.
if [ "$BUILD_LINUXx86" != "n" ]; then
echo "--- QC builds ---"
rm -rf $QCCBUILDFOLDER 2>&1
mkdir -p $QCCBUILDFOLDER
if [ -e "$BUILDFOLDER/linux_x86/fteqw32" ]; then
echo "Making fteextensions.qc"
mkdir -p ~/.fte/fte
echo "pr_dumpplatform -o fteextensions" > ~/.fte/fte/minusargsaresilly.cfg
echo "pr_dumpplatform -o csqcsysdefs -Tcs" >> ~/.fte/fte/minusargsaresilly.cfg
echo "pr_dumpplatform -o menusysdefs -Tmenu" >> ~/.fte/fte/minusargsaresilly.cfg
$BUILDFOLDER/linux_x86/fteqw32 -basedir ~/.fte -nohome -quake +set snd_device none -nosound +set vid_renderer sv +exec minusargsaresilly.cfg +quit >> /dev/null
mv ~/.fte/fte/src/fteextensions.qc $QCCBUILDFOLDER
mv ~/.fte/fte/src/csqcsysdefs.qc $QCCBUILDFOLDER
mv ~/.fte/fte/src/menusysdefs.qc $QCCBUILDFOLDER
else
echo "Skipping FTE Extensions, no Linux x86 (merged) build located"
fi
if [ -e $BUILDFOLDER/linux_x86/fteqcc32 ]; then
mkdir -p $BUILDFOLDER/csaddon/
cd $SVNROOT/quakec
cd csaddon/src
echo -n "Making csaddon... "
$BUILDFOLDER/linux_x86/fteqcc32 -srcfile csaddon.src > $BUILDLOGFOLDER/csaddon.txt
if [ $? -eq 0 ]; then
echo "done"
cp ../csaddon.dat $BUILDFOLDER/csaddon/
cd ..
zip -9 $BUILDFOLDER/csaddon/csaddon.pk3 csaddon.dat
else
echo "failed"
fi
cd $SVNROOT/quakec
cd menusys
echo -n "Making menusys... "
$BUILDFOLDER/linux_x86/fteqcc32 -srcfile menu.src > $BUILDLOGFOLDER/menu.txt
if [ $? -eq 0 ]; then
echo "done"
zip -q -9 -o -r $BUILDFOLDER/csaddon/menusys_src.zip .
cp ../menu.dat $BUILDFOLDER/csaddon/
cd ..
zip -9 $BUILDFOLDER/csaddon/menusys.pk3 menu.dat
else
echo "failed"
fi
else
echo "Skiping csaddon + qcmenu, no compiler build"
#call out to build_qc.sh to invoke native builds as appropriate.
case "$(uname -m)" in
x86_64)
if [ "$BUILD_LINUXx64" != "n" ]; then
rm -rf $QCCBUILDFOLDER 2>&1
mkdir -p $QCCBUILDFOLDER
FTEQCC=$BUILDFOLDER/linux_amd64/fteqcc64 FTEQW=$BUILDFOLDER/linux_amd64/fteqw64 QUAKESPASM=quakespasm-spiked-linux64 ./build_qc.sh
fi
fi
;;
i386 | i486 | i586)
if [ "$BUILD_LINUXx86" != "n" ]; then
rm -rf $QCCBUILDFOLDER 2>&1
mkdir -p $QCCBUILDFOLDER
FTEQCC=$BUILDFOLDER/linux_x86/fteqcc32 FTEQW=$BUILDFOLDER/linux_x86/fteqw32 QUAKESPASM= ./build_qc.sh
fi
;;
esac
cd $SVNROOT/engine/
svn info > $BUILDFOLDER/version.txt

View file

@ -15,7 +15,7 @@
#define ENABLEPLUGINSBYDEFAULT //auto-enable plugins that the user installs. this risks other programs installing dlls (but we can't really protect our listing file so this is probably not any worse in practise).
#ifdef PACKAGEMANAGER
#if !defined(NOBUILTINMENUS) && !defined(SERVERONLY)
#if !defined(NOBUILTINMENUS) && defined(HAVE_CLIENT)
#define DOWNLOADMENU
#endif
#endif
@ -24,16 +24,10 @@
#include "fs.h"
//some extra args for the downloads menu (for the downloads menu to handle engine updates).
#if defined(_DEBUG) || defined(DEBUG)
#define PHPDBG "&dbg=1"
#else
#define PHPDBG
#endif
#ifndef SVNREVISION
#define SVNREVISION -
#endif
static char enginerevision[256] = STRINGIFY(SVNREVISION);
#define DOWNLOADABLESARGS PHPDBG
@ -355,7 +349,7 @@ static qboolean PM_PurgeOnDisable(package_t *p)
return true;
}
void PM_ValidateAuthenticity(package_t *p, enum hashvalidation_e validated)
static void PM_ValidateAuthenticity(package_t *p, enum hashvalidation_e validated)
{
qbyte hashdata[512];
size_t hashsize = 0;
@ -609,6 +603,14 @@ static qboolean PM_MergePackage(package_t *oldp, package_t *newp)
if (ignorefiles || (om && nm))
return false;
}
//packages are merged according to how it should look when its actually installed.
//this means we might be merging two packages from different sources with different installation methods/files/etc...
if ((newp->signature && oldp->signature && strcmp(newp->signature, oldp->signature)) ||
(newp->filesha512 && oldp->filesha512 && strcmp(newp->filesha512, oldp->filesha512)) ||
(newp->filesha1 && oldp->filesha1 && strcmp(newp->filesha1, oldp->filesha1)))
return false;
for (od = oldp->deps, nd = newp->deps; od && nd; )
{
//if its a zip then the 'remote' file list will be blank while the local list is not (we can just keep the local list).
@ -641,10 +643,18 @@ static qboolean PM_MergePackage(package_t *oldp, package_t *newp)
if (newp->website){Z_Free(oldp->website); oldp->website = Z_StrDup(newp->website);}
if (newp->previewimage){Z_Free(oldp->previewimage); oldp->previewimage = Z_StrDup(newp->previewimage);}
if (newp->signature){Z_Free(oldp->signature); oldp->signature = Z_StrDup(newp->signature);}
if (newp->filesha1){Z_Free(oldp->filesha1); oldp->filesha1 = Z_StrDup(newp->filesha1);}
if (newp->filesha512){Z_Free(oldp->filesha512); oldp->filesha512 = Z_StrDup(newp->filesha512);}
if (newp->filesize){oldp->filesize = newp->filesize;}
//use the new package's auth settings. this affects downloading rather than reactivation.
if (newp->signature || newp->filesha1 || newp->filesha512)
{
Z_Free(oldp->signature); oldp->signature = newp->signature?Z_StrDup(newp->signature):NULL;
Z_Free(oldp->filesha1); oldp->filesha1 = newp->filesha1?Z_StrDup(newp->filesha1):NULL;
Z_Free(oldp->filesha512); oldp->filesha512 = newp->filesha512?Z_StrDup(newp->filesha512):NULL;
oldp->filesize = newp->filesize;
oldp->flags &= ~(DPF_SIGNATUREACCEPTED|DPF_SIGNATUREREJECTED|DPF_SIGNATUREUNKNOWN);
oldp->flags |= newp->flags&(DPF_SIGNATUREACCEPTED|DPF_SIGNATUREREJECTED|DPF_SIGNATUREUNKNOWN);
}
else
oldp->flags &= ~DPF_SIGNATUREACCEPTED;
oldp->priority = newp->priority;
@ -883,6 +893,8 @@ static void PM_AddSubList(const char *url, const char *prefix, unsigned int flag
pm_source[i].prefix = BZ_Malloc(strlen(prefix)+1);
strcpy(pm_source[i].prefix, prefix);
downloadablessequence++;
}
}
#ifdef WEBCLIENT
@ -1344,6 +1356,8 @@ static qboolean PM_ParsePackageList(const char *f, unsigned int parseflags, cons
return forcewrite; //it's not the right version.
}
downloadablessequence++;
while(*f)
{
linestart = f;
@ -1570,6 +1584,7 @@ static int QDECL PM_EnumeratedPlugin (const char *name, qofs_t size, time_t mtim
int len, l, a;
char *dot;
const char *synthver = "??""??";
char *pkgname;
if (!strncmp(name, PLUGINPREFIX, strlen(PLUGINPREFIX)))
Q_strncpyz(vmname, name+strlen(PLUGINPREFIX), sizeof(vmname));
else
@ -1614,6 +1629,10 @@ static int QDECL PM_EnumeratedPlugin (const char *name, qofs_t size, time_t mtim
}
}
pkgname = va("fteplug_%s", vmname);
if (PM_FindExactPackage(pkgname, NULL, NULL, 0))
return true; //don't include it if its a dupe anyway.
if (PM_FindExactPackage(vmname, NULL, NULL, 0))
return true; //don't include it if its a dupe anyway.
//FIXME: should be checking whether there's a package that provides the file...
@ -1623,7 +1642,7 @@ static int QDECL PM_EnumeratedPlugin (const char *name, qofs_t size, time_t mtim
p->deps->dtype = DEP_FILE;
strcpy(p->deps->name, name);
p->arch = Z_StrDup(THISARCH);
p->name = Z_StrDup(vmname);
p->name = Z_StrDup(pkgname);
p->title = Z_StrDup(vmname);
p->category = Z_StrDup("Plugins/");
p->priority = PM_DEFAULTPRIORITY;
@ -1684,9 +1703,9 @@ static void PM_PreparePackageList(void)
if (fs_manifest && fs_manifest->downloadsurl && *fs_manifest->downloadsurl)
{
if (fs_manifest->security==MANIFEST_SECURITY_NOT)
PM_AddSubList(fs_manifest->downloadsurl, NULL, SRCFL_MANIFEST); //don't trust it, don't even prompt.
PM_AddSubList(fs_manifest->downloadsurl, NULL, SRCFL_MANIFEST|SRCFL_DISABLED); //don't trust it, don't even prompt.
else
PM_AddSubList(fs_manifest->downloadsurl, NULL, SRCFL_MANIFEST|SRCFL_ENABLED); //enable it by default. functionality is kinda broken otherwise.
PM_AddSubList(fs_manifest->downloadsurl, NULL, SRCFL_MANIFEST); //enable it by default. functionality is kinda broken otherwise.
}
#ifdef PLUGINS
@ -2395,7 +2414,7 @@ static void PM_AllowPackageListQuery_Callback(void *ctx, promptbutton_t opt)
for (i = 0; i < pm_numsources; i++)
{
if (pm_source[i].flags & SRCFL_MANIFEST)
if ((pm_source[i].flags & SRCFL_MANIFEST) && !(pm_source[i].flags & SRCFL_DISABLED))
pm_source[i].flags |= SRCFL_ONCE;
}
}
@ -2455,7 +2474,7 @@ 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(va("%s%s"DOWNLOADABLESARGS, pm_source[i].url, strchr(pm_source[i].url,'?')?"&":"?"), NULL, PM_ListDownloaded);
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;
@ -3205,10 +3224,10 @@ typedef struct {
char *fname;
qbyte ctx[1];
} hashfile_t;
static int QDECL SHA1File_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestowrite)
static int QDECL HashFile_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestowrite)
{
hashfile_t *f = (hashfile_t*)file;
f->hashfunc->process(&f->ctx, buffer, bytestowrite);
f->hashfunc->process(f->ctx, buffer, bytestowrite);
if (bytestowrite != VFS_WRITE(f->f, buffer, bytestowrite))
f->fail = true; //something went wrong.
if (f->fail)
@ -3216,12 +3235,12 @@ static int QDECL SHA1File_WriteBytes (struct vfsfile_s *file, const void *buffer
f->sz += bytestowrite;
return bytestowrite;
}
static void QDECL SHA1File_Flush (struct vfsfile_s *file)
static void QDECL HashFile_Flush (struct vfsfile_s *file)
{
hashfile_t *f = (hashfile_t*)file;
VFS_FLUSH(f->f);
}
static qboolean QDECL SHA1File_Close (struct vfsfile_s *file)
static qboolean QDECL HashFile_Close (struct vfsfile_s *file)
{
qbyte digest[256];
hashfile_t *f = (hashfile_t*)file;
@ -3229,9 +3248,9 @@ static qboolean QDECL SHA1File_Close (struct vfsfile_s *file)
f->fail = true; //something went wrong.
f->f = NULL;
f->hashfunc->terminate(digest, &f->ctx);
f->hashfunc->terminate(digest, f->ctx);
if (f->fail)
Con_Printf("Filesystem problems downloading %s\n", f->fname); //don't error if we failed on actual disk problems
Con_Printf("Filesystem problem saving %s during download\n", f->fname); //don't error if we failed on actual disk problems
else if (f->sz != f->needsize)
{
Con_Printf("Download truncated: %s\n", f->fname); //don't error if we failed on actual disk problems
@ -3239,20 +3258,27 @@ static qboolean QDECL SHA1File_Close (struct vfsfile_s *file)
}
else if (memcmp(digest, f->need, f->hashfunc->digestsize))
{
qbyte base64[512];
Con_Printf("Invalid hash for downloaded file %s, try again later?\n", f->fname);
Base64_EncodeBlock(digest, f->hashfunc->digestsize, base64, sizeof(base64)-1);
base64[sizeof(base64)-1] = 0;
Con_Printf("%s vs", base64);
Base64_EncodeBlock(f->need, f->hashfunc->digestsize, base64, sizeof(base64)-1);
Con_Printf("%s\n", base64);
f->fail = true;
}
return !f->fail; //true if all okay!
}
static vfsfile_t *FS_Sha1_ValidateWrites(vfsfile_t *f, const char *fname, qofs_t needsize, hashfunc_t *hashfunc, const char *hash)
static vfsfile_t *FS_Hash_ValidateWrites(vfsfile_t *f, const char *fname, qofs_t needsize, hashfunc_t *hashfunc, const char *hash)
{ //wraps a writable file with a layer that'll cause failures when the hash differs from what we expect.
if (f)
{
hashfile_t *n = Z_Malloc(sizeof(*n) + hashfunc->contextsize + strlen(fname));
n->pub.WriteBytes = SHA1File_WriteBytes;
n->pub.Flush = SHA1File_Flush;
n->pub.Close = SHA1File_Close;
n->pub.WriteBytes = HashFile_WriteBytes;
n->pub.Flush = HashFile_Flush;
n->pub.Close = HashFile_Close;
n->pub.seekstyle = SS_UNSEEKABLE;
n->f = f;
n->hashfunc = hashfunc;
@ -3262,7 +3288,7 @@ static vfsfile_t *FS_Sha1_ValidateWrites(vfsfile_t *f, const char *fname, qofs_t
Base16_DecodeBlock(hash, n->need, sizeof(n->need));
n->fail = false;
n->hashfunc->init(&n->ctx);
n->hashfunc->init(n->ctx);
f = &n->pub;
}
@ -3276,23 +3302,28 @@ static qboolean PM_SignatureOkay(package_t *p)
struct packagedep_s *dep;
char ext[MAX_QPATH];
// if (p->flags & (DPF_SIGNATUREREJECTED|DPF_SIGNATUREUNKNOWN)) //the sign key didn't match its sha512 hash
// return false; //just block it entirely.
if (p->flags & DPF_SIGNATUREACCEPTED) //sign value is present and correct
return true; //go for it.
if (p->flags & DPF_PRESENT)
return true; //we don't know where it came from, but someone manually installed it...
if (p->flags & (DPF_SIGNATUREREJECTED|DPF_SIGNATUREUNKNOWN)) //the sign key didn't match its sha512 hash
return false; //just block it entirely.
if (p->flags & DPF_SIGNATUREACCEPTED) //sign value is present and correct
return true; //go for it.
//packages without a signature are only allowed under some limited conditions.
//basically we only allow meta packages, pk3s, and paks.
//metadata doesn't specify all file names for zips.
if (p->extract == EXTRACT_ZIP)
return false; //extracting files is bad (there might be some weird .pif or whatever file in there, don't risk it)
return false;
if (!p->gamedir || !*p->gamedir)
return false;
for (dep = p->deps; dep; dep = dep->next)
{
if (dep->dtype != DEP_FILE)
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)))
;
@ -3464,9 +3495,9 @@ static void PM_StartADownload(void)
}
if (p->filesha512 && tmpfile)
tmpfile = FS_Sha1_ValidateWrites(tmpfile, p->name, p->filesize, &hash_sha512, p->filesha512);
tmpfile = FS_Hash_ValidateWrites(tmpfile, p->name, p->filesize, &hash_sha512, p->filesha512);
else if (p->filesha1 && tmpfile)
tmpfile = FS_Sha1_ValidateWrites(tmpfile, p->name, p->filesize, &hash_sha1, p->filesha1);
tmpfile = FS_Hash_ValidateWrites(tmpfile, p->name, p->filesize, &hash_sha1, p->filesha1);
if (tmpfile)
{
@ -3824,6 +3855,7 @@ static void PM_AddSubList_Callback(void *ctx, promptbutton_t opt)
{
PM_AddSubList(ctx, "", SRCFL_USER|SRCFL_ENABLED);
PM_WriteInstalledPackages();
PM_UpdatePackageList(false, 0);
}
Z_Free(ctx);
}
@ -4478,6 +4510,11 @@ void QCBUILTIN PF_cl_getpackagemanagerinfo(pubprogfuncs_t *prinst, struct global
enum packagemanagerinfo_e fieldidx = G_INT(OFS_PARM1);
package_t *p;
G_INT(OFS_RETURN) = 0;
if (packageidx < 0)
return;
if (packageidx == 0)
PM_AreSourcesNew(true);
for (p = availablepackages; p; p = p->next)
{
if ((p->flags & DPF_HIDDEN) && !(p->flags & (DPF_MARKED|DPF_ENABLED|DPF_PURGE|DPF_CACHED)))
@ -4849,7 +4886,12 @@ static void MD_Source_Draw (int x, int y, struct menucustom_s *c, struct emenu_s
{
char *text;
if (!(pm_source[c->dint].flags & SRCFL_ENABLED))
Draw_FunStringWidth (x, y, "^&04 ", 48, 2, false); //red
{
if (!(pm_source[c->dint].flags & SRCFL_DISABLED))
Draw_FunStringWidth (x, y, "??", 48, 2, false);
else
Draw_FunStringWidth (x, y, "^&04 ", 48, 2, false); //red
}
else switch(pm_source[c->dint].status)
{
case SRCSTAT_OBTAINED:
@ -4893,23 +4935,41 @@ static void MD_Source_Draw (int x, int y, struct menucustom_s *c, struct emenu_s
break;
}
text = va("Source %s", pm_source[c->dint].url);
text = va("%s%s", (pm_source[c->dint].flags & (SRCFL_ENABLED|SRCFL_DISABLED))?"":"^b",
pm_source[c->dint].url);
Draw_FunString (x+48, y, text);
}
static qboolean MD_Source_Key (struct menucustom_s *c, struct emenu_s *m, int key, unsigned int unicode)
{
if (key == K_ENTER || key == K_KP_ENTER || key == K_GP_START || key == K_MOUSE1)
{
if (pm_source[c->dint].flags & SRCFL_DISABLED)
{
pm_source[c->dint].flags = (pm_source[c->dint].flags&~SRCFL_DISABLED)|SRCFL_ENABLED;
pm_source[c->dint].status = SRCSTAT_UNTRIED;
}
else
{
pm_source[c->dint].flags = (pm_source[c->dint].flags&~SRCFL_ENABLED)|SRCFL_DISABLED;
pm_source[c->dint].status = SRCSTAT_PENDING;
}
PM_WriteInstalledPackages();
PM_UpdatePackageList(true, 2);
}
if (key == K_DEL || key == K_BACKSPACE)
{
if (pm_source[c->dint].flags & SRCFL_ENABLED)
{
pm_source[c->dint].flags = (pm_source[c->dint].flags&~SRCFL_ENABLED)|SRCFL_DISABLED;
pm_source[c->dint].status = SRCSTAT_PENDING;
}
else
else if (pm_source[c->dint].flags & SRCFL_USER)
{
pm_source[c->dint].flags = (pm_source[c->dint].flags&~SRCFL_DISABLED)|SRCFL_ENABLED;
pm_source[c->dint].status = SRCSTAT_UNTRIED;
pm_source[c->dint].flags &= ~(SRCFL_ENABLED|SRCFL_DISABLED);
pm_source[c->dint].flags |= SRCFL_HISTORIC;
}
else
return false; //will just be re-added anyway... :(
PM_WriteInstalledPackages();
PM_UpdatePackageList(true, 2);
}
@ -5123,6 +5183,7 @@ static void MD_Download_UpdateStatus(struct emenu_s *m)
int i;
unsigned int downloads=0;
menucustom_t *c;
qboolean sources;
#endif
if (info->downloadablessequence != downloadablessequence || !info->populated)
@ -5212,10 +5273,13 @@ static void MD_Download_UpdateStatus(struct emenu_s *m)
info->populated = true;
MC_AddFrameStart(m, 48);
#ifdef WEBCLIENT
for (i = 0; i < pm_numsources; i++)
for (i = 0, sources=false; i < pm_numsources; i++)
{
if (pm_source[i].flags & SRCFL_HISTORIC)
continue; //historic... ignore it.
if (!sources)
MC_AddBufferedText(m, 48, 320-16, y, "Sources", false, true), y += 8;
sources=true;
c = MC_AddCustom(m, 0, y, p, i, NULL);
c->draw = MD_Source_Draw;
c->key = MD_Source_Key;
@ -5228,6 +5292,7 @@ static void MD_Download_UpdateStatus(struct emenu_s *m)
}
y+=4; //small gap
#endif
MC_AddBufferedText(m, 48, 320-16, y, "Options", false, true), y += 8;
b = MC_AddCommand(m, 48, 320-16, y, info->applymessage, MD_ApplyDownloads);
b->rightalign = false;
b->common.tooltip = "Enable/Disable/Download/Delete packages to match any changes made (you will be prompted with a list of the changes that will be made).";
@ -5254,6 +5319,7 @@ static void MD_Download_UpdateStatus(struct emenu_s *m)
y += 8;
#endif
y+=4; //small gap
MC_AddBufferedText(m, 48, 320-16, y, "Packages", false, true), y += 8;
MD_AddItemsToDownloadMenu(m, y, info->pathprefix);
if (!m->selecteditem)
m->selecteditem = (menuoption_t*)d;
@ -5281,6 +5347,7 @@ void Menu_DownloadStuff_f (void)
emenu_t *menu;
dlmenu_t *info;
Key_Dest_Remove(kdm_console|kdm_cwindows);
menu = M_CreateMenu(sizeof(dlmenu_t));
info = menu->data;
@ -5296,45 +5363,88 @@ void Menu_DownloadStuff_f (void)
info->populated = false; //will add any headers as needed
}
#ifndef SERVERONLY
#ifdef WEBCLIENT
static void PM_ConfirmSource(void *ctx, promptbutton_t button)
{
size_t i;
if (button == PROMPT_YES || button == PROMPT_NO)
if (button == PROMPT_YES)
{
for (i = 0; i < pm_numsources; i++)
{
if (!strcmp(pm_source[i].url, ctx))
{
pm_source[i].flags |= (button == PROMPT_YES)?SRCFL_ENABLED:SRCFL_DISABLED;
PM_WriteInstalledPackages();
Menu_Download_Update();
return;
}
}
}
else //cancel or 'customize'
{
for (i = 0; i < pm_numsources; i++)
pm_source[i].flags |= SRCFL_PROMPTED;
if (button == PROMPT_NO)
Cmd_ExecuteString("menu_download\n", RESTRICT_LOCAL);
}
}
#endif
//should only be called AFTER the filesystem etc is inited.
void Menu_Download_Update(void)
//given a url, try to chop it down to just a hostname
static const char *PrettyHostFromURL(const char *origurl)
{
if (pkg_autoupdate.ival <= 0)
return;
char *url, *end;
url = va("%s", origurl);
if (!strnicmp(url, "https://", 8))
url+=8;
else if (!strnicmp(url, "http://", 7))
url+=7;
else
return origurl;
PM_UpdatePackageList(true, 2);
url = va("%s", url);
if (*url == '[')
{ //ipv6 host
end = strchr(url+1, ']');
if (!end)
return origurl;
*end = 0;
}
else
{ //strip any resource part.
end = strchr(url, '/');
if (end)
*end = 0;
//strip any explicit port number.
end = strrchr(url, ':');
if (end)
*end = 0;
}
#ifndef SERVERONLY
if (fs_manifest && pkg_autoupdate.ival > 0)
return url;
}
qboolean PM_AreSourcesNew(qboolean doprompt)
{
qboolean ret = false;
#ifdef WEBCLIENT
if (pkg_autoupdate.ival > 0)
{ //only prompt if autoupdate is actually enabled.
int i;
for (i = 0; i < pm_numsources; i++)
{
if (pm_source[i].flags & SRCFL_HISTORIC)
continue; //hidden anyway
if (!(pm_source[i].flags & (SRCFL_ENABLED|SRCFL_DISABLED)))
if (!(pm_source[i].flags & (SRCFL_ENABLED|SRCFL_DISABLED|SRCFL_PROMPTED)))
{
Menu_Prompt(PM_ConfirmSource, Z_StrDup(pm_source[i].url), va("Enable update source\n\n^x66F%s", pm_source[i].url), "Enable", "Disable", "Later");
pm_source[i].flags |= SRCFL_PROMPTED;
ret = true; //something is dirty.
if (doprompt)
{
const char *msg = va("Enable update source\n\n^x66F%s", (pm_source[i].flags&SRCFL_MANIFEST)?PrettyHostFromURL(pm_source[i].url):pm_source[i].url);
Menu_Prompt(PM_ConfirmSource, Z_StrDup(pm_source[i].url), msg, "Enable", "Configure", "Later");
pm_source[i].flags |= SRCFL_PROMPTED;
}
break;
}
}
@ -5345,6 +5455,16 @@ void Menu_Download_Update(void)
}*/
}
#endif
return ret;
}
//should only be called AFTER the filesystem etc is inited.
void Menu_Download_Update(void)
{
if (pkg_autoupdate.ival <= 0)
return;
PM_UpdatePackageList(true, 2);
}
#else
void Menu_Download_Update(void)

View file

@ -2,6 +2,7 @@
#include "quakedef.h"
#include "winquake.h"
#include "fs.h"
static const char *res4x3[] =
@ -1246,6 +1247,14 @@ static void M_Menu_Preset_Predraw(emenu_t *menu)
((char*)op->button.text)[1] = (preset==0)?'m':'7';
preset--;
}
#if defined(WEBCLIENT) && defined(PACKAGEMANAGER)
else if (!strcmp(op->button.command, "menu_download\n"))
{
op->button.text = PM_AreSourcesNew(false)?"^bPackages (New!)":"Packages";
op->common.posx = op->common.next->common.posx;
op->common.width = 216-op->common.posx;
}
#endif
}
else if (!filtering)
{
@ -1322,7 +1331,7 @@ void M_Menu_Preset_f (void)
MB_SPACING(16),
MB_CHECKBOXCVARTIP("Auto-save Settings", cfg_save_auto, 1, "If this is disabled, you will need to explicitly save your settings."),
#if defined(WEBCLIENT) && defined(PACKAGEMANAGER)
MB_CONSOLECMD("Updates", "menu_download\n", "Configure sources and packages."),
MB_CONSOLECMD("Packages", "menu_download\n", "Configure sources and packages."),
#endif
MB_SPACING(16),
MB_CONSOLECMD("Accept", "menupop\n", "Continue with selected settings."),
@ -4191,7 +4200,6 @@ void M_Menu_ModelViewer_f(void)
}
#endif
#include "fs.h"
static void Mods_Draw(int x, int y, struct menucustom_s *c, struct emenu_s *m)
{
int i = c->dint;

View file

@ -1102,9 +1102,20 @@ static void DoSign(const char *fname, int signtype)
auth = com_argv[i+1];
f = FS_OpenVFS(fname, "rb", FS_SYSTEM);
if (f)
if (f && signtype == -1)
{ //just report the qhash
searchpathfuncs_t *search = FS_OpenPackByExtension(f, NULL, fname, fname);
if (search)
{
printf("%#08x", search->GeneratePureCRC(search, 0, 0));
search->ClosePath(search);
}
else
printf("-");
}
else if (f)
{
hashfunc_t *h = &hash_sha512;
hashfunc_t *h = (signtype==1)?&hash_sha256:&hash_sha512;
size_t l, ts = 0;
void *ctx = alloca(h->contextsize);
qbyte data[65536*16];
@ -1133,7 +1144,12 @@ static void DoSign(const char *fname, int signtype)
sigsize = Crypto_GenerateSignature(digest, h->digestsize, signature, sizeof(signature));
Base64_EncodeBlock(signature, sigsize, base64, sizeof(base64));
printf("%s\n", base64);
printf("%s", base64);
}
else
{ //just spits out the hash
Base16_EncodeBlock(digest, h->digestsize, base64, sizeof(base64));
printf("%s", base64);
}
}
}
@ -1294,19 +1310,36 @@ int main (int c, const char **v)
//begin meta generation helpers
//fteqw -privcert privcert.key -pubcert pubcert.key -sign binaryfile.pk3
i = COM_CheckParm("-sign");
if (!i)
i = COM_CheckParm("-sign2");
if (i)
{
//init some useless crap
host_parms = parms;
Cvar_Init();
Memory_Init ();
COM_Init ();
static struct
{
const char *arg;
int type;
} signarg[] =
{
{"-sign", 0},
{"-sign2", 2},
{"-qhash", -1},
{"-sha1", 1},
{"-sha256", 256},
{"-sha512", 512},
};
int j;
for (j = 0; j < countof(signarg); j++)
{
i = COM_CheckParm(signarg[j].arg);
if (i)
{
//init some useless crap
host_parms = parms;
Cvar_Init();
Memory_Init ();
COM_Init ();
DoSign(com_argv[i+1], atoi(com_argv[i+0]+5));
return EXIT_SUCCESS;
DoSign(com_argv[i+1], signarg[j].type);
return EXIT_SUCCESS;
}
}
}
//end

View file

@ -78,6 +78,7 @@ 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
void PM_ManifestPackage(const char *name, int security);
qboolean PM_AreSourcesNew(qboolean doprompt);
qboolean PM_FindUpdatedEngine(char *syspath, size_t syspathsize); //names the engine we should be running
void PM_AddManifestPackages(ftemanifest_t *man);
void Menu_Download_Update(void);

262
fte.m4 Normal file
View file

@ -0,0 +1,262 @@
dnl
dnl This file will be processed to provide relevant package lists for the various packages built from FTE's source.
dnl The output will need to be combined with any other packages, and then signed for these packages to be considered valid.
dnl Users can add extra sources with the `pkg addsource URL' console command (will show a prompt, to try to avoid exploits).
dnl
define(`DATE',FTE_DATE)dnl
define(`REVISION',FTE_REVISION)dnl
define(`DLSIZE',`esyscmd(`stat --printf="%s" $1')')dnl
define(`SHA512',`esyscmd(`fteqw -sha512 $1')')dnl
define(`URL',`url "$1"
dlsize "DLSIZE($1)"
sha512 "SHA512($1)" ')dnl
define(`CAT',`$1$2$3$4$5$6$7$8$9')dnl
define(`ZIP',`unzipfile "$2"
URL($1$3)')dnl
define(`FILE',`file "$1$2$3$4$5$6$7$8$9"')dnl
define(`WINENGINE',`category "Engine"
ver "REVISION"
gamedir ""
license "GPLv2"
{
arch "win_x86-FTE$1"
FILE(fteqw_,REVISION,_32.exe)
ZIP(win32/,CAT($2,.exe),CAT($2,_win32.zip))
}
{
arch "win_x64-FTE$1"
FILE(fteqw_,REVISION,_64.exe)
ZIP(win64/,CAT($2,64.exe),CAT($2,_win64.zip))
}')dnl
define(`LINENGINE2',`{
arch "linux_amd64-FTE$1"
FILE(fteqw_,REVISION,_64.bin)
ZIP(linux_amd64/,CAT($2,64),$3)
}')dnl
define(`LINENGINE',`LINENGINE2($1,$2,CAT($2_lin64.zip))')dnl
define(`HIDE',)dnl
define(`GAME',`ifelse(FTE_GAME,$1,$2
,)')dnl
define(`TEST',`ifelse(FTE_TEST,`1',` test "1"
',` test "0"
')')dnl
{
package "fte_cl"
WINENGINE(-m,fteqw)
LINENGINE(-m,fteqw)
title "CAT(`FTE Engine ',DATE)"
desc "The awesome FTE engine (multi-renderer build)"
TEST()dnl
}
HIDE(`
{
package "fte_cl_gl"
dnl WINENGINE(-gl,fteglqw)
dnl //don't bother advertising it on linux
dnl title "CAT(`FTE Engine ',DATE,` - OpenGL'")
desc "The awesome FTE engine (OpenGL-only build)"
TEST()dnl
}
{
package "fte_cl_vk"
dnl WINENGINE(-vk,ftevkqw)
dnl //don't bother advertising it on linux
dnl title "CAT(`FTE Engine ',DATE,` - Vulkan')"
desc "The awesome FTE engine (Vulkan-only build)"
TEST()dnl
}
{
package "fte_cl_d3d"
dnl WINENGINE(-d3d,fted3dqw)
//no d3d on linux
dnl title "CAT(`FTE Engine ',DATE,` - Direct3D')"
desc "The awesome FTE engine (Direct3D-only build)"
TEST()dnl
}')dnl
{
package "fte_sv"
WINENGINE(-sv,fteqwsv)
LINENGINE2(-sv,fteqw-sv,fteqwsv_lin64.zip)
title "CAT(`FTE Engine ',DATE,` - Server')"
desc "The awesome FTE engine (server-only build)"
TEST()dnl
}
define(`WINPLUG',`category "Plugins"
ver "REVISION"
gamedir ""
license "GPLv2"
{
arch "win_x86"
FILE(fteplug_$1_x86.REVISION.dll)
URL(win32/fteplug_$1_x86.dll)
}
{
arch "win_x64"
FILE(fteplug_$1_x64.REVISION.dll)
URL(win64/fteplug_$1_x64.dll)
}')dnl
define(`LINPLUG',`{
arch "linux_amd64"
FILE(fteplug_$1_amd64.REVISION,.so)
URL(linux_amd64/fteplug_$1_amd64.so)
}')dnl
GAME(quake,
{
package "fteplug_ezhud"
WINPLUG(ezhud)
LINPLUG(ezhud)
title "EzHud Plugin"
replace "ezhud"
desc "Some lame alternative configurable hud."
TEST()dnl
})dnl
GAME(quake,
{
package "fteplug_qi"
WINPLUG(qi)
LINPLUG(qi)
category "Plugins"
title "Quake Injector Plugin"
replace "Quake Injector Plugin"
author "Spike"
website "https://www.quaddicted.com/reviews/"
desc "Provides a way to quickly list+install+load numerous different maps and mods. Some better than others."
desc "If you're a single-player fan then these will keep you going for quite some time."
desc "The database used is from quaddicted.com."
TEST()dnl
})dnl
{
package "fteplug_irc"
WINPLUG(irc)
LINPLUG(irc)
title "IRC Plugin"
replace "IRC Plugin"
desc "Allows you to converse on IRC servers in-game."
TEST()dnl
}
{
package "fteplug_xmpp"
WINPLUG(xmpp)
LINPLUG(xmpp)
title "XMPP Plugin"
desc "Allows you to converse on XMPP servers. This also includes a method for NAT holepunching between contacts."
TEST()dnl
}
HIDE(`
{
package "fteplug_models"
WINPLUG(models)
LINPLUG(models)
title "Model exporter plugin"
desc ""
TEST()dnl
}
{
package fteplug_ode
TEST()dnl
}
{
package fteplug_cef
TEST()dnl
}
{
package "fteplug_ffmpeg"
{
arch "win_x86"
file "fteplug_ffmpeg_x86."#REVISION#".dll"
url "win32/fteplug_ffmpeg_x86.dll"
}
{
arch "win_x64"
file "fteplug_ffmpeg_x64."#REVISION#".dll"
url "win64/fteplug_ffmpeg_x64.dll"
}
// {
// arch "linux_x64"
// file "fteplug_ffmpeg_amd64."#REVISION#".so"
// url "linux_amd64/fteplug_ffmpeg_amd64.so"
// }
ver REVISION
category "Plugins"
title "FFmpeg Plugin"
file "fteplug_ffmpeg"
gamedir ""
TEST()dnl
}
{
package "libffmpeg"
{
arch "win_x86"
url "win32/ffmpeg-4.0-x86.zip"
}
{
arch "win_x64"
url "win64/ffmpeg-4.0-x64.zip"
}
// {
// arch "linux_x64"
// url "linux_amd64/ffmpeg-4.0-fteplug_ezhud_amd64.so"
// }
ver "4.0"
category "Plugins"
title "FFmpeg Library"
extract "zip"
gamedir ""
}
')dnl
GAME(quake2,
{
package "q2game_baseq2"
{
arch "win_x86"
FILE(q2gamex86_baseq2.dll)
URL(win32/q2gamex86_baseq2.dll)
}
{
arch "win_x64"
FILE(q2gamex64_baseq2.dll)
URL(win64/q2gamex64_baseq2.dll)
}
{
arch "linux_amd64"
FILE(q2gameamd64_baseq2.so)
URL(linux_amd64/q2gameamd64_baseq2.so)
}
ver "20190606"
category "Mods"
title "Gamecode: Base Game"
license "GPLv2"
website "https://github.com/yquake2/yquake2"
desc "Quake2 Gamecode (from yamagiq2). Required for single player or servers."
TEST()dnl
})dnl
GAME(quake,
{
package "fte_csaddon"
category "Plugins"
title "Ingame Map Editor"
ver REVISION
gamedir "fte"
FILE(csaddon.dat)
URL(csaddon/csaddon.dat)
desc "This is Spike's map editing user interface. It is only active while running singleplayer (or sv_cheats is enabled)."
desc "To activate, set the ca_show cvar to 1 (suggestion: ^abind c toggle ca_show^a)."
license "GPLv2, source on fte's svn"
author "Spike"
TEST()dnl
})dnl
GAME(quake,
{
package "fte_menusys"
category "AfterQuake"
title "Replacement Menus"
ver REVISION
gamedir "fte"
FILE(menu.dat)
URL(csaddon/menu.dat)
desc "This provides a more modern mouse-first menu system."
license "GPLv2, source on fte's svn"
author "Spike"
TEST()dnl
})dnl

108
quakec/dpsymbols.src Normal file
View file

@ -0,0 +1,108 @@
//NOTE: This file exists purely for generation of the genericdefs/*_api.qc files distributed in ftetools.zip.
//It is consumed by FTE's build scripts and exists to normalize DP's misnamed defs to match FTE's, and some other fixups/omissions with DP defs.
//Patches welcome...
#pragma noref 1
#ifdef TEST
#include "fteextensions.qc"
#endif
//attempt to normalize things.
#define ReadAngle readangle
#define ReadByte readbyte
#define ReadChar readchar
#define ReadCoord readcoord
#define ReadFloat readfloat
#define ReadLong readlong
#define ReadShort readshort
#define ReadString readstring
#define draw_getimagesize drawgetimagesize
#define skel_mul_bone skel_premul_bone
#define skel_mul_bones skel_premul_bones
#define setsensitivityscale setsensitivityscaler
#define centerprint cprint
#define cs_project project
#define cs_unproject unproject
#define ChangeYaw changeyaw
#define entitybyindex edict_num
#define adddynamiclight2 dynamiclight_add
#define FIELD_ENTITY EV_ENTITY
#define FIELD_FLOAT EV_FLOAT
#define FIELD_FUNCTION EV_FUNCTION
#define FIELD_STRING EV_STRING
#define FIELD_VECTOR EV_VECTOR
#define STAT_MONSTERS STAT_KILLEDMONSTERS
#define STAT_SECRETS STAT_FOUNDSECRETS
#define STAT_WEAPONMODEL STAT_WEAPONMODELI
#define E_ABSMAX GE_ABSMAX
#define E_ABSMIN GE_ABSMIN
#define E_ACTIVE GE_ACTIVE
#define E_ALPHA GE_ALPHA
#define E_COLORMOD GE_COLORMOD
#define E_FORWARD GE_FORWARD
#define E_MAXS GE_MAXS
#define E_MINS GE_MINS
#define E_ORIGIN GE_ORIGIN
#define E_ORIGINANDVECTORS GE_ORIGINANDVECTORS
#define E_PANTSCOLOR GE_PANTSCOLOR
#define E_RIGHT GE_RIGHT
#define E_SCALE GE_SCALE
#define E_SHIRTCOLOR GE_SHIRTCOLOR
#define E_SKIN GE_SKIN
#define E_UP GE_UP
#define VF_FOV_X VF_FOVX
#define VF_FOV_Y VF_FOVX
#define PI M_PI
#define MASK_NORMAL MASK_ENGINE
#define MASK_ENGINEVIEWMODELS MASK_VIEWMODEL
#define false FALSE
#define true TRUE
#ifdef SSQC
#include "dpdefs/progsdefs.qc"
#include "dpdefs/dpextensions.qc"
.float SendFlags;
.float gravity;
float MSG_ENTITY=5;
#endif
#ifdef CSQC
#define drawstring drawrawstring
#include "dpdefs/csprogsdefs.qc"
#undef drawstring
vector(vector position, string text, vector scale, vector rgb, float alpha, float flag) drawstring = #326;
//not actually defined for some fucked up reason, but work none the less. These are the ones that I use - there'll be others.
const float IE_KEYDOWN = 0;
const float IE_KEYUP = 1;
#endif
#ifdef MENU
#define cmd localcmd
#include "dpdefs/menudefs.qc"
const float GGDI_GAMEDIR = GETGAMEDIRINFO_NAME;
const float GGDI_DESCRIPTION = GETGAMEDIRINFO_DESCRIPTION;
//not actually defined for some fucked up reason, but work none the less. These are the ones that I use - there'll be others.
float(float s) asin = #471;
float(float c) acos = #472;
float(float t) atan = #473;
float(float c, float s) atan2 = #474;
float(float a) tan = #475;
string(string filename) whichpack = #503;
#endif
float(__variant) checkbuiltin = #0; //not really present in DP, but #0 is technically just an OP_DONE so returns 0 so its actually okay in the end. its important for this to not generate extra warnings.