--- /dev/null
+//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;
+}