static void trace_print_global(qc_program_t *prog, unsigned int glob, int vtype) { static char spaces[28+1] = " "; prog_section_def_t *def; qcany_t *value; int len; if (!glob) { if ((len = printf("<null>,")) == -1) len = 0; goto done; } def = prog_getdef(prog, glob); value = (qcany_t*)(&prog->globals[glob]); len = printf("[@%u] ", glob); if (def) { const char *name = prog_getstring(prog, def->name); if (name[0] == '#') len += printf("$"); else len += printf("%s ", name); vtype = def->type & DEF_TYPEMASK; } switch (vtype) { case TYPE_VOID: case TYPE_ENTITY: case TYPE_FIELD: case TYPE_FUNCTION: case TYPE_POINTER: len += printf("(%i),", value->_int); break; case TYPE_VECTOR: len += printf("'%g %g %g',", value->vector[0], value->vector[1], value->vector[2]); break; case TYPE_STRING: if (value->string) len += print_escaped_string(prog_getstring(prog, value->string), sizeof(spaces)-len-5); else len += printf("(null)"); len += printf(","); /* len += printf("\"%s\",", prog_getstring(prog, value->string)); */ break; case TYPE_FLOAT: default: len += printf("%g,", value->_float); break; } done: if (len < (int)sizeof(spaces)-1) { spaces[sizeof(spaces)-1-len] = 0; fputs(spaces, stdout); spaces[sizeof(spaces)-1-len] = ' '; } }
static void prog_disasm_function(qc_program_t *prog, size_t id) { prog_section_function_t *fdef = &prog->functions[0] + id; prog_section_statement_t *st; if (fdef->entry < 0) { printf("FUNCTION \"%s\" = builtin #%i\n", prog_getstring(prog, fdef->name), (int)-fdef->entry); return; } else printf("FUNCTION \"%s\"\n", prog_getstring(prog, fdef->name)); st = &prog->code[0] + fdef->entry; while (st->opcode != INSTR_DONE) { prog_print_statement(prog, st); ++st; } }
static int qc_stof(qc_program_t *prog) { qcany_t *str; qcany_t num; CheckArgs(1); str = GetArg(0); num._float = (float)strtod(prog_getstring(prog, str->string), nullptr); Return(num); return 0; }
static int qc_stof(qc_program *prog) { qcany *str; qcany num; CheckArgs(1); str = GetArg(0); num._float = strtof(prog_getstring(prog, str->string), NULL); Return(num); return 0; }
static int qc_strcmp(qc_program_t *prog) { qcany_t *str1, *str2; qcany_t out; const char *cstr1; const char *cstr2; if (prog->argc != 2 && prog->argc != 3) { fprintf(stderr, "ERROR: invalid number of arguments for strcmp/strncmp: %i, expected 2 or 3\n", prog->argc); return -1; } str1 = GetArg(0); str2 = GetArg(1); cstr1 = prog_getstring(prog, str1->string); cstr2 = prog_getstring(prog, str2->string); if (prog->argc == 3) out._float = strncmp(cstr1, cstr2, GetArg(2)->_float); else out._float = strcmp(cstr1, cstr2); Return(out); return 0; }
static int qc_strcat(qc_program_t *prog) { char *buffer; size_t len1, len2; qcany_t *str1, *str2; qcany_t out; const char *cstr1; const char *cstr2; CheckArgs(2); str1 = GetArg(0); str2 = GetArg(1); cstr1 = prog_getstring(prog, str1->string); cstr2 = prog_getstring(prog, str2->string); len1 = strlen(cstr1); len2 = strlen(cstr2); buffer = (char*)mem_a(len1 + len2 + 1); memcpy(buffer, cstr1, len1); memcpy(buffer+len1, cstr2, len2+1); out.string = prog_tempstring(prog, buffer); mem_d(buffer); Return(out); return 0; }
static int qc_print(qc_program_t *prog) { size_t i; const char *laststr = nullptr; for (i = 0; i < (size_t)prog->argc; ++i) { qcany_t *str = (qcany_t*)(&prog->globals[0] + OFS_PARM0 + 3*i); laststr = prog_getstring(prog, str->string); printf("%s", laststr); } if (laststr && (prog->xflags & VMXF_TRACE)) { size_t len = strlen(laststr); if (!len || laststr[len-1] != '\n') printf("\n"); } return 0; }
static qcint_t prog_enterfunction(qc_program_t *prog, prog_section_function_t *func) { qc_exec_stack_t st; size_t parampos; int32_t p; /* back up locals */ st.localsp = vec_size(prog->localstack); st.stmt = prog->statement; st.function = func; if (prog->xflags & VMXF_TRACE) { const char *str = prog_getstring(prog, func->name); vec_push(prog->function_stack, str); } #ifdef QCVM_BACKUP_STRATEGY_CALLER_VARS if (vec_size(prog->stack)) { prog_section_function_t *cur; cur = prog->stack[vec_size(prog->stack)-1].function; if (cur) { qcint_t *globals = &prog->globals[0] + cur->firstlocal; vec_append(prog->localstack, cur->locals, globals); } } #else { qcint_t *globals = &prog->globals[0] + func->firstlocal; vec_append(prog->localstack, func->locals, globals); } #endif /* copy parameters */ parampos = func->firstlocal; for (p = 0; p < func->nargs; ++p) { size_t s; for (s = 0; s < func->argsize[p]; ++s) { prog->globals[parampos] = prog->globals[OFS_PARM0 + 3*p + s]; ++parampos; } } vec_push(prog->stack, st); return func->entry; }
int main(int argc, char **argv) { size_t i; qcint_t fnmain = -1; qc_program_t *prog; size_t xflags = VMXF_DEFAULT; bool opts_printfields = false; bool opts_printdefs = false; bool opts_printfuns = false; bool opts_disasm = false; bool opts_info = false; bool noexec = false; const char *progsfile = nullptr; const char **dis_list = nullptr; int opts_v = 0; arg0 = argv[0]; if (argc < 2) { usage(); exit(EXIT_FAILURE); } while (argc > 1) { if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help")) { usage(); exit(EXIT_SUCCESS); } else if (!strcmp(argv[1], "-v")) { ++opts_v; --argc; ++argv; } else if (!strncmp(argv[1], "-vv", 3)) { const char *av = argv[1]+1; for (; *av; ++av) { if (*av == 'v') ++opts_v; else { usage(); exit(EXIT_FAILURE); } } --argc; ++argv; } else if (!strcmp(argv[1], "-version") || !strcmp(argv[1], "--version")) { version(); exit(EXIT_SUCCESS); } else if (!strcmp(argv[1], "-trace")) { --argc; ++argv; xflags |= VMXF_TRACE; } else if (!strcmp(argv[1], "-profile")) { --argc; ++argv; xflags |= VMXF_PROFILE; } else if (!strcmp(argv[1], "-info")) { --argc; ++argv; opts_info = true; noexec = true; } else if (!strcmp(argv[1], "-disasm")) { --argc; ++argv; opts_disasm = true; noexec = true; } else if (!strcmp(argv[1], "-disasm-func")) { --argc; ++argv; if (argc <= 1) { usage(); exit(EXIT_FAILURE); } vec_push(dis_list, argv[1]); --argc; ++argv; noexec = true; } else if (!strcmp(argv[1], "-printdefs")) { --argc; ++argv; opts_printdefs = true; noexec = true; } else if (!strcmp(argv[1], "-printfuns")) { --argc; ++argv; opts_printfuns = true; noexec = true; } else if (!strcmp(argv[1], "-printfields")) { --argc; ++argv; opts_printfields = true; noexec = true; } else if (!strcmp(argv[1], "-vector") || !strcmp(argv[1], "-string") || !strcmp(argv[1], "-float") ) { qcvm_parameter p; if (argv[1][1] == 'f') p.vtype = TYPE_FLOAT; else if (argv[1][1] == 's') p.vtype = TYPE_STRING; else if (argv[1][1] == 'v') p.vtype = TYPE_VECTOR; else p.vtype = TYPE_VOID; --argc; ++argv; if (argc < 2) { usage(); exit(EXIT_FAILURE); } p.value = argv[1]; vec_push(main_params, p); --argc; ++argv; } else if (!strcmp(argv[1], "--")) { --argc; ++argv; break; } else if (argv[1][0] != '-') { if (progsfile) { fprintf(stderr, "only 1 program file may be specified\n"); usage(); exit(EXIT_FAILURE); } progsfile = argv[1]; --argc; ++argv; } else { fprintf(stderr, "unknown parameter: %s\n", argv[1]); usage(); exit(EXIT_FAILURE); } } if (argc == 2 && !progsfile) { progsfile = argv[1]; --argc; ++argv; } if (!progsfile) { fprintf(stderr, "must specify a program to execute\n"); usage(); exit(EXIT_FAILURE); } prog = prog_load(progsfile, noexec); if (!prog) { fprintf(stderr, "failed to load program '%s'\n", progsfile); exit(EXIT_FAILURE); } prog->builtins = qc_builtins; prog->builtins_count = GMQCC_ARRAY_COUNT(qc_builtins); if (opts_info) { printf("Program's system-checksum = 0x%04x\n", (unsigned int)prog->crc16); printf("Entity field space: %u\n", (unsigned int)prog->entityfields); printf("Globals: %zu\n", prog->globals.size()); printf("Counts:\n" " code: %zu\n" " defs: %zu\n" " fields: %zu\n" " functions: %zu\n" " strings: %zu\n", prog->code.size(), prog->defs.size(), prog->fields.size(), prog->functions.size(), prog->strings.size()); } if (opts_info) { prog_delete(prog); return 0; } for (i = 0; i < vec_size(dis_list); ++i) { size_t k; printf("Looking for `%s`\n", dis_list[i]); for (k = 1; k < prog->functions.size(); ++k) { const char *name = prog_getstring(prog, prog->functions[k].name); if (!strcmp(name, dis_list[i])) { prog_disasm_function(prog, k); break; } } } if (opts_disasm) { for (i = 1; i < prog->functions.size(); ++i) prog_disasm_function(prog, i); return 0; } if (opts_printdefs) { const char *getstring = nullptr; for (auto &it : prog->defs) { printf("Global: %8s %-16s at %u%s", type_name[it.type & DEF_TYPEMASK], prog_getstring(prog, it.name), (unsigned int)it.offset, ((it.type & DEF_SAVEGLOBAL) ? " [SAVE]" : "")); if (opts_v) { switch (it.type & DEF_TYPEMASK) { case TYPE_FLOAT: printf(" [init: %g]", ((qcany_t*)(&prog->globals[0] + it.offset))->_float); break; case TYPE_INTEGER: printf(" [init: %i]", (int)( ((qcany_t*)(&prog->globals[0] + it.offset))->_int )); break; case TYPE_ENTITY: case TYPE_FUNCTION: case TYPE_FIELD: case TYPE_POINTER: printf(" [init: %u]", (unsigned)( ((qcany_t*)(&prog->globals[0] + it.offset))->_int )); break; case TYPE_STRING: getstring = prog_getstring(prog, ((qcany_t*)(&prog->globals[0] + it.offset))->string); printf(" [init: `"); print_escaped_string(getstring, strlen(getstring)); printf("`]\n"); break; default: break; } } printf("\n"); } } if (opts_printfields) { for (auto &it : prog->fields) { printf("Field: %8s %-16s at %d%s\n", type_name[it.type], prog_getstring(prog, it.name), it.offset, ((it.type & DEF_SAVEGLOBAL) ? " [SAVE]" : "")); } } if (opts_printfuns) { for (auto &it : prog->functions) { int32_t a; printf("Function: %-16s taking %u parameters:(", prog_getstring(prog, it.name), (unsigned int)it.nargs); for (a = 0; a < it.nargs; ++a) { printf(" %i", it.argsize[a]); } if (opts_v > 1) { int32_t start = it.entry; if (start < 0) printf(") builtin %i\n", (int)-start); else { size_t funsize = 0; prog_section_statement_t *st = &prog->code[0] + start; for (;st->opcode != INSTR_DONE; ++st) ++funsize; printf(") - %zu instructions", funsize); if (opts_v > 2) { printf(" - locals: %i + %i\n", it.firstlocal, it.locals); } else printf("\n"); } } else if (opts_v) { printf(") locals: %i + %i\n", it.firstlocal, it.locals); } else printf(")\n"); } } if (!noexec) { for (i = 1; i < prog->functions.size(); ++i) { const char *name = prog_getstring(prog, prog->functions[i].name); if (!strcmp(name, "main")) fnmain = (qcint_t)i; } if (fnmain > 0) { prog_main_setparams(prog); prog_exec(prog, &prog->functions[fnmain], xflags, VM_JUMPS_DEFAULT); } else fprintf(stderr, "No main function found\n"); } prog_delete(prog); return 0; }
qc_program_t* prog_load(const char *filename, bool skipversion) { prog_header_t header; qc_program_t *prog; FILE *file = fopen(filename, "rb"); /* we need all those in order to support INSTR_STATE: */ bool has_self = false, has_time = false, has_think = false, has_nextthink = false, has_frame = false; if (!file) return nullptr; if (fread(&header, sizeof(header), 1, file) != 1) { loaderror("failed to read header from '%s'", filename); fclose(file); return nullptr; } util_swap_header(header); if (!skipversion && header.version != 6) { loaderror("header says this is a version %i progs, we need version 6\n", header.version); fclose(file); return nullptr; } prog = (qc_program_t*)mem_a(sizeof(qc_program_t)); if (!prog) { fclose(file); fprintf(stderr, "failed to allocate program data\n"); return nullptr; } memset(prog, 0, sizeof(*prog)); prog->entityfields = header.entfield; prog->crc16 = header.crc16; prog->filename = util_strdup(filename); if (!prog->filename) { loaderror("failed to store program name"); goto error; } #define read_data(hdrvar, progvar, reserved) \ if (fseek(file, header.hdrvar.offset, SEEK_SET) != 0) { \ loaderror("seek failed"); \ goto error; \ } \ prog->progvar.resize(header.hdrvar.length + reserved); \ if (fread( \ &prog->progvar[0], \ sizeof(prog->progvar[0]), \ header.hdrvar.length, \ file \ )!= header.hdrvar.length \ ) { \ loaderror("read failed"); \ goto error; \ } #define read_data1(x) read_data(x, x, 0) #define read_data2(x, y) read_data(x, x, y) read_data (statements, code, 0); read_data1(defs); read_data1(fields); read_data1(functions); read_data1(strings); read_data2(globals, 2); /* reserve more in case a RETURN using with the global at "the end" exists */ util_swap_statements(prog->code); util_swap_defs_fields(prog->defs); util_swap_defs_fields(prog->fields); util_swap_functions(prog->functions); util_swap_globals(prog->globals); fclose(file); /* profile counters */ memset(vec_add(prog->profile, prog->code.size()), 0, sizeof(prog->profile[0]) * prog->code.size()); /* Add tempstring area */ prog->tempstring_start = prog->strings.size(); prog->tempstring_at = prog->strings.size(); prog->strings.resize(prog->strings.size() + 16*1024, '\0'); /* spawn the world entity */ vec_push(prog->entitypool, true); memset(vec_add(prog->entitydata, prog->entityfields), 0, prog->entityfields * sizeof(prog->entitydata[0])); prog->entities = 1; /* cache some globals and fields from names */ for (auto &it : prog->defs) { const char *name = prog_getstring(prog, it.name); if (!strcmp(name, "self")) { prog->cached_globals.self = it.offset; has_self = true; } else if (!strcmp(name, "time")) { prog->cached_globals.time = it.offset; has_time = true; } } for (auto &it : prog->fields) { const char *name = prog_getstring(prog, it.name); if (!strcmp(name, "think")) { prog->cached_fields.think = it.offset; has_think = true; } else if (!strcmp(name, "nextthink")) { prog->cached_fields.nextthink = it.offset; has_nextthink = true; } else if (!strcmp(name, "frame")) { prog->cached_fields.frame = it.offset; has_frame = true; } } if (has_self && has_time && has_think && has_nextthink && has_frame) prog->supports_state = true; return prog; error: if (prog->filename) mem_d(prog->filename); vec_free(prog->entitydata); vec_free(prog->entitypool); mem_d(prog); fclose(file); return nullptr; }
int main(int argc, char **argv) { size_t i; qcint fnmain = -1; qc_program *prog; size_t xflags = VMXF_DEFAULT; bool opts_printfields = false; bool opts_printdefs = false; bool opts_printfuns = false; bool opts_disasm = false; bool opts_info = false; bool noexec = false; const char *progsfile = NULL; arg0 = argv[0]; if (argc < 2) { usage(); exit(1); } while (argc > 1) { if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "-help") || !strcmp(argv[1], "--help")) { usage(); exit(0); } else if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "-version") || !strcmp(argv[1], "--version")) { version(); exit(0); } else if (!strcmp(argv[1], "-trace")) { --argc; ++argv; xflags |= VMXF_TRACE; } else if (!strcmp(argv[1], "-profile")) { --argc; ++argv; xflags |= VMXF_PROFILE; } else if (!strcmp(argv[1], "-info")) { --argc; ++argv; opts_info = true; noexec = true; } else if (!strcmp(argv[1], "-disasm")) { --argc; ++argv; opts_disasm = true; noexec = true; } else if (!strcmp(argv[1], "-printdefs")) { --argc; ++argv; opts_printdefs = true; noexec = true; } else if (!strcmp(argv[1], "-printfuns")) { --argc; ++argv; opts_printfuns = true; noexec = true; } else if (!strcmp(argv[1], "-printfields")) { --argc; ++argv; opts_printfields = true; noexec = true; } else if (!strcmp(argv[1], "-vector") || !strcmp(argv[1], "-string") || !strcmp(argv[1], "-float") ) { qcvm_parameter p; if (argv[1][1] == 'f') p.vtype = TYPE_FLOAT; else if (argv[1][1] == 's') p.vtype = TYPE_STRING; else if (argv[1][1] == 'v') p.vtype = TYPE_VECTOR; --argc; ++argv; if (argc < 3) { usage(); exit(1); } p.value = argv[1]; vec_push(main_params, p); --argc; ++argv; } else if (!strcmp(argv[1], "--")) { --argc; ++argv; break; } else if (argv[1][0] != '-') { if (progsfile) { printf("only 1 program file may be specified\n"); usage(); exit(1); } progsfile = argv[1]; --argc; ++argv; } else { usage(); exit(1); } } if (argc > 2) { usage(); exit(1); } if (argc > 1) { if (progsfile) { printf("only 1 program file may be specified\n"); usage(); exit(1); } progsfile = argv[1]; --argc; ++argv; } if (!progsfile) { usage(); exit(1); } prog = prog_load(progsfile); if (!prog) { printf("failed to load program '%s'\n", progsfile); exit(1); } prog->builtins = qc_builtins; prog->builtins_count = qc_builtins_count; if (opts_info) { printf("Program's system-checksum = 0x%04x\n", (unsigned int)prog->crc16); printf("Entity field space: %u\n", (unsigned int)prog->entityfields); printf("Globals: %u\n", (unsigned int)vec_size(prog->globals)); } if (opts_info) { prog_delete(prog); return 0; } if (opts_disasm) { for (i = 1; i < vec_size(prog->functions); ++i) prog_disasm_function(prog, i); return 0; } if (opts_printdefs) { for (i = 0; i < vec_size(prog->defs); ++i) { printf("Global: %8s %-16s at %u%s\n", type_name[prog->defs[i].type & DEF_TYPEMASK], prog_getstring(prog, prog->defs[i].name), (unsigned int)prog->defs[i].offset, ((prog->defs[i].type & DEF_SAVEGLOBAL) ? " [SAVE]" : "")); } } if (opts_printfields) { for (i = 0; i < vec_size(prog->fields); ++i) { printf("Field: %8s %-16s at %u%s\n", type_name[prog->fields[i].type], prog_getstring(prog, prog->fields[i].name), (unsigned int)prog->fields[i].offset, ((prog->fields[i].type & DEF_SAVEGLOBAL) ? " [SAVE]" : "")); } } if (opts_printfuns) { for (i = 0; i < vec_size(prog->functions); ++i) { int32_t a; printf("Function: %-16s taking %i parameters:(", prog_getstring(prog, prog->functions[i].name), (unsigned int)prog->functions[i].nargs); for (a = 0; a < prog->functions[i].nargs; ++a) { printf(" %i", prog->functions[i].argsize[a]); } printf(") locals: %i + %i\n", prog->functions[i].firstlocal, prog->functions[i].locals); } } if (!noexec) { for (i = 1; i < vec_size(prog->functions); ++i) { const char *name = prog_getstring(prog, prog->functions[i].name); if (!strcmp(name, "main")) fnmain = (qcint)i; } if (fnmain > 0) { prog_main_setparams(prog); prog_exec(prog, &prog->functions[fnmain], xflags, VM_JUMPS_DEFAULT); } else printf("No main function found\n"); } prog_delete(prog); return 0; }