fteqw/engine/qclib/test.c

222 lines
5.3 KiB
C

//This is basically a sample program.
//It deomnstrates the code required to get qclib up and running.
//This code does not demonstrate entities, however.
//It does demonstrate the built in qc compiler, and does demonstrate a globals-only progs interface.
//It also demonstrates basic builtin(s).
#include "progtype.h"
#include "progslib.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
//builtins and builtin management.
void PF_prints (progfuncs_t *prinst, struct globalvars_s *gvars)
{
char *s;
s = prinst->VarString(prinst, 0);
printf("%s", s);
}
void PF_printv (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
printf("%f %f %f\n", G_FLOAT(OFS_PARM0+0), G_FLOAT(OFS_PARM0+1), G_FLOAT(OFS_PARM0+2));
}
void PF_printf (progfuncs_t *prinst, struct globalvars_s *pr_globals)
{
printf("%f\n", G_FLOAT(OFS_PARM0));
}
void PF_bad (progfuncs_t *prinst, struct globalvars_s *gvars)
{
printf("bad builtin\n");
}
builtin_t builtins[] = {
PF_bad,
PF_prints,
PF_printv,
PF_printf
};
//Called when the qc library has some sort of serious error.
void Sys_Abort(char *s, ...)
{ //quake handles this with a longjmp.
va_list ap;
va_start(ap, s);
vprintf(s, ap);
va_end(ap);
exit(1);
}
//Called when the library has something to say.
//Kinda required for the compiler...
//Not really that useful for the normal vm.
int Sys_Printf(char *s, ...)
{ //look up quake's va function to find out how to deal with variable arguments properly.
return printf("%s", s);
}
#include <stdio.h>
//copy file into buffer. note that the buffer will have been sized to fit the file (obtained via FileSize)
unsigned char *Sys_ReadFile (char *fname, void *buffer, int buflen)
{
int len;
FILE *f;
if (!strncmp(fname, "src/", 4))
fname+=4; //skip the src part
f = fopen(fname, "rb");
if (!f)
return NULL;
fseek(f, 0, SEEK_END);
len = ftell(f);
if (buflen < len)
return NULL;
fseek(f, 0, SEEK_SET);
fread(buffer, 1, len, f);
fclose(f);
return buffer;
}
//Finds the size of a file.
int Sys_FileSize (char *fname)
{
int len;
FILE *f;
if (!strncmp(fname, "src/", 4))
fname+=4; //skip the src part
f = fopen(fname, "rb");
if (!f)
return -1;
fseek(f, 0, SEEK_END);
len = ftell(f);
fclose(f);
return len;
}
//Writes a file.
pbool Sys_WriteFile (char *fname, void *data, int len)
{
FILE *f;
f = fopen(fname, "wb");
if (!f)
return 0;
fwrite(data, 1, len, f);
fclose(f);
return 1;
}
void runtest(char *progsname)
{
progfuncs_t *pf;
func_t func;
progsnum_t pn;
progparms_t ext;
memset(&ext, 0, sizeof(ext));
ext.progsversion = PROGSTRUCT_VERSION;
ext.ReadFile = Sys_ReadFile;
ext.FileSize= Sys_FileSize;
ext.Abort = Sys_Abort;
ext.printf = printf;
ext.numglobalbuiltins = sizeof(builtins)/sizeof(builtins[0]);
ext.globalbuiltins = builtins;
pf = InitProgs(&ext);
pf->Configure(pf, 1024*1024, 1); //memory quantity of 1mb. Maximum progs loadable into the instance of 1
//If you support multiple progs types, you should tell the VM the offsets here, via RegisterFieldVar
pn = pf->LoadProgs(pf, progsname, 0, NULL, 0); //load the progs, don't care about the crc, and use those builtins.
if (pn < 0)
printf("test: Failed to load progs \"%s\"\n", progsname);
else
{
//allocate qc-acessable strings here for 64bit cpus. (allocate via AddString, tempstringbase is a holding area not used by the actual vm)
//you can call functions before InitEnts if you want. it's not really advised for anything except naming additional progs. This sample only allows one max.
pf->InitEnts(pf, 10); //Now we know how many fields required, we can say how many maximum ents we want to allow. 10 in this case. This can be huge without too many problems.
//now it's safe to ED_Alloc.
func = pf->FindFunction(pf, "main", PR_ANY); //find the function 'main' in the first progs that has it.
if (!func)
printf("Couldn't find function\n");
else
pf->ExecuteProgram(pf, func); //call the function
}
CloseProgs(pf);
}
//Run a compiler and nothing else.
//Note that this could be done with an autocompile of PR_COMPILEALWAYS.
void compile(int argc, char **argv)
{
progfuncs_t *pf;
progparms_t ext;
if (0)
{
char *testsrcfile = //newstyle progs.src must start with a #.
//it's newstyle to avoid using multiple source files.
"#pragma PROGS_DAT \"testprogs.dat\"\r\n"
"//INTERMEDIATE FILE - EDIT TEST.C INSTEAD\r\n"
"\r\n"
"void(...) print = #1;\r\n"
"void() main =\r\n"
"{\r\n"
" print(\"hello world\\n\");\r\n"
"};\r\n";
//so that the file exists. We could insert it via the callbacks instead
Sys_WriteFile("progs.src", testsrcfile, strlen(testsrcfile));
}
memset(&ext, 0, sizeof(ext));
ext.progsversion = PROGSTRUCT_VERSION;
ext.ReadFile = Sys_ReadFile;
ext.FileSize= Sys_FileSize;
ext.WriteFile= Sys_WriteFile;
ext.Abort = Sys_Abort;
ext.printf = printf;
pf = InitProgs(&ext);
if (pf->StartCompile)
{
if (pf->StartCompile(pf, argc, argv))
{
while(pf->ContinueCompile(pf) == 1)
;
}
}
else
printf("no compiler in this qcvm build\n");
CloseProgs(pf);
}
int main(int argc, char **argv)
{
if (argc < 2)
{
printf("Invalid arguments!\nPlease run as, for example:\n%s testprogs.dat -srcfile progs.src\nThe first argument is the name of the progs.dat to run, the remaining arguments are the qcc args to use", argv[0]);
return 0;
}
compile(argc-1, argv+1);
runtest(argv[1]);
return 0;
}