#include "quakedef.h" #include "pr_common.h" #include "shader.h" #ifdef GLQUAKE #include "glquake.h" #endif #if defined(MENU_DAT) || defined(CSQC_DAT) #include "cl_master.h" //MP_MouseMove(mx, my, mouse->qdeviceid) static qbyte mpkeysdown[K_MAX/8]; extern qboolean csqc_dp_lastwas3d; void M_Init_Internal (void); void M_DeInit_Internal (void); extern unsigned int r2d_be_flags; static unsigned int PF_SelectDPDrawFlag(pubprogfuncs_t *prinst, int flag) { if (r_refdef.warndraw) { if (!*r_refdef.rt_destcolour[0].texname) { r_refdef.warndraw = false; //don't spam too much PR_RunWarning(prinst, "Detected attempt to draw to framebuffer where framebuffer is not valid\n"); } } #ifdef CSQC_DAT csqc_dp_lastwas3d = false; //for compat with dp's stupid beginpolygon #endif //flags: //0 = blend //1 = add //2 = modulate //3 = modulate*2 flag &= 3; if (flag == DRAWFLAG_ADD) return BEF_FORCEADDITIVE; else return 0; } //float drawfill(vector position, vector size, vector rgb, float alpha, float flag) = #457; void QCBUILTIN PF_CL_drawfill (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *pos = G_VECTOR(OFS_PARM0); float *size = G_VECTOR(OFS_PARM1); float *rgb = G_VECTOR(OFS_PARM2); float alpha = G_FLOAT(OFS_PARM3); int flag = prinst->callargc >= 5?G_FLOAT(OFS_PARM4):0; r2d_be_flags = PF_SelectDPDrawFlag(prinst, flag); R2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha); R2D_FillBlock(pos[0], pos[1], size[0], size[1]); r2d_be_flags = 0; G_FLOAT(OFS_RETURN) = 1; } //void drawsetcliparea(float x, float y, float width, float height) = #458; void QCBUILTIN PF_CL_drawsetcliparea (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { srect_t srect; if (R2D_Flush) R2D_Flush(); #ifdef CSQC_DAT csqc_dp_lastwas3d = false; #endif srect.x = G_FLOAT(OFS_PARM0) / (float)vid.fbvwidth; srect.y = G_FLOAT(OFS_PARM1) / (float)vid.fbvheight; srect.width = G_FLOAT(OFS_PARM2) / (float)vid.fbvwidth; srect.height = G_FLOAT(OFS_PARM3) / (float)vid.fbvheight; srect.dmin = -99999; srect.dmax = 99999; srect.y = (1-srect.y) - srect.height; BE_Scissor(&srect); G_FLOAT(OFS_RETURN) = 1; } //void drawresetcliparea(void) = #459; void QCBUILTIN PF_CL_drawresetcliparea (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { if (R2D_Flush) R2D_Flush(); #ifdef CSQC_DAT csqc_dp_lastwas3d = false; #endif BE_Scissor(NULL); G_FLOAT(OFS_RETURN) = 1; } #define FONT_SLOTS 32 #define FONT_SIZES 16 struct { unsigned int owner; //kdm_foo. whoever has an interest in this font. font is purged when this becomes 0. char slotname[16]; char facename[MAX_OSPATH]; float scale; //poop int outline; //argh int sizes; int size[FONT_SIZES]; struct font_s *font[FONT_SIZES]; } fontslot[FONT_SLOTS]; static struct font_s *PR_CL_ChooseFont(float *fontsel, int szx, int szy) { int fontidx = 0; //default by default... struct font_s *font = font_default; if (fontsel) { fontidx = *fontsel; } if (fontidx >= 0 && fontidx < FONT_SLOTS) { int i, j; int fontdiff = 10000; for (i = 0; i < fontslot[fontidx].sizes; i++) { j = abs(szy - fontslot[fontidx].size[i]); if (j < fontdiff && fontslot[fontidx].font[i]) { fontdiff = j; font = fontslot[fontidx].font[i]; } } } return font; } void PR_CL_BeginString(pubprogfuncs_t *prinst, float vx, float vy, float szx, float szy, float *px, float *py) { world_t *world = prinst->parms->user; struct font_s *font; if (world->g.drawfontscale && (world->g.drawfontscale[0] || world->g.drawfontscale[1])) { szx *= world->g.drawfontscale[0]; szy *= world->g.drawfontscale[1]; } font = PR_CL_ChooseFont(world->g.drawfont, szx, szy); Font_BeginScaledString(font, vx, vy, szx, szy, px, py); } int PR_findnamedfont(const char *name, qboolean isslotname) { int i; if (isslotname) { for (i = 0; i < FONT_SLOTS; i++) { if (!stricmp(fontslot[i].slotname, name)) return i; } } else { for (i = 0; i < FONT_SLOTS; i++) { if (!stricmp(fontslot[i].facename, name)) return i; } } return -1; } int PR_findunusedfont(void) { int i; //don't find slot 0. for (i = FONT_SLOTS; i-- > 1; ) { if (!*fontslot[i].slotname && !*fontslot[i].facename) return i; } return -1; } //purgeowner is the bitmask of owners that are getting freed. //if purgeowner is 0, fonts will get purged void PR_ReleaseFonts(unsigned int purgeowner) { int i, j; for (i = 0; i < FONT_SLOTS; i++) { if (fontslot[i].owner) continue; //already free fontslot[i].owner &= ~purgeowner; if (fontslot[i].owner) continue; //still owned by someone for (j = 0; j < fontslot[i].sizes; j++) { if (fontslot[i].font[j]) Font_Free(fontslot[i].font[j]); fontslot[i].font[j] = NULL; } fontslot[i].sizes = 0; fontslot[i].slotname[0] = '\0'; fontslot[i].facename[0] = '\0'; fontslot[i].scale = 1; fontslot[i].outline = 0; } } void PR_ReloadFonts(qboolean reload) { int i, j; if (qrenderer == QR_NONE) reload = false; for (i = 0; i < FONT_SLOTS; i++) { //already not loaded if (!fontslot[i].owner) continue; //flush it (if loaded) for (j = 0; j < fontslot[i].sizes; j++) { if (fontslot[i].font[j]) Font_Free(fontslot[i].font[j]); fontslot[i].font[j] = NULL; } //and reload if needed if (reload) { //otherwise load it. for (j = 0; j < fontslot[i].sizes; j++) { fontslot[i].font[j] = Font_LoadFont(fontslot[i].facename, fontslot[i].size[j], fontslot[i].scale, fontslot[i].outline); } } } } void QCBUILTIN PF_CL_findfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *slotname = PR_GetStringOfs(prinst, OFS_PARM0); G_FLOAT(OFS_RETURN) = PR_findnamedfont(slotname, true) + 1; //return default on failure. } void QCBUILTIN PF_CL_loadfont (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { extern cvar_t r_font_postprocess_outline; const char *slotname = PR_GetStringOfs(prinst, OFS_PARM0); const char *facename = PR_GetStringOfs(prinst, OFS_PARM1); const char *sizestr = PR_GetStringOfs(prinst, OFS_PARM2); int slotnum = (prinst->callargc>3)?G_FLOAT(OFS_PARM3):-1; //float fix_scale = (prinst->callargc>4)?G_FLOAT(OFS_PARM4):0; //float fix_voffset = (prinst->callargc>5)G_FLOAT(OFS_PARM5):0; int i, sz; world_t *world = prinst->parms->user; G_FLOAT(OFS_RETURN) = 0; //return default on failure. if (slotnum < 0 && *slotname) slotnum = PR_findnamedfont(slotname, true); else if (slotnum < 0) slotnum = PR_findnamedfont(facename, false); if (slotnum < 0) slotnum = PR_findunusedfont(); if (slotnum < 0) return; //eep. if ((unsigned)slotnum >= FONT_SLOTS) return; //if its changed, purge it. if (stricmp(fontslot[slotnum].slotname, slotname) || stricmp(fontslot[slotnum].facename, facename) || !fontslot[slotnum].sizes) { Q_strncpyz(fontslot[slotnum].slotname, slotname, sizeof(fontslot[slotnum].slotname)); Q_strncpyz(fontslot[slotnum].facename, facename, sizeof(fontslot[slotnum].facename)); for (i = 0; i < fontslot[slotnum].sizes; i++) { if (fontslot[slotnum].font[i]) Font_Free(fontslot[slotnum].font[i]); fontslot[slotnum].font[i] = NULL; } fontslot[slotnum].sizes = 0; fontslot[slotnum].owner = 0; fontslot[slotnum].scale = 1; fontslot[slotnum].outline = r_font_postprocess_outline.ival; } fontslot[slotnum].owner |= world->keydestmask; while(*sizestr) { sizestr = COM_Parse(sizestr); if (!strncmp(com_token, "scale=", 6)) { fontslot[slotnum].scale = atof(com_token+6); continue; } if (!strncmp(com_token, "outline=", 8)) { fontslot[slotnum].outline = atoi(com_token+8); continue; } if (!strncmp(com_token, "blur=", 5)) { //fontslot[slotnum].blur = atoi(com_token+5); continue; } if (!strncmp(com_token, "voffset=", 8)) { //com_token+8 unused. continue; } sz = atoi(com_token); if (!sz) continue; //o.O for (i = 0; i < fontslot[slotnum].sizes; i++) { if (fontslot[slotnum].size[i] == sz) break; } if (i == fontslot[slotnum].sizes) { if (i >= FONT_SIZES) break; fontslot[slotnum].size[i] = sz; fontslot[slotnum].font[i] = NULL; fontslot[slotnum].sizes++; } } if (qrenderer > QR_NONE) { for (i = 0; i < fontslot[slotnum].sizes; i++) fontslot[slotnum].font[i] = Font_LoadFont(facename, fontslot[slotnum].size[i], fontslot[slotnum].scale, fontslot[slotnum].outline); } G_FLOAT(OFS_RETURN) = slotnum; } #ifdef HAVE_LEGACY void CL_LoadFont_f(void) { extern cvar_t r_font_postprocess_outline; //console command for compat with dp/debug. if (Cmd_Argc() == 1) { int i, j; int th; for (i = 0; i < FONT_SLOTS; i++) { if (fontslot[i].sizes) { Con_Printf("%s[%i]: %s (", fontslot[i].slotname, i, fontslot[i].facename); for (j = 0; j < fontslot[i].sizes; j++) { if (j) Con_Printf(", "); Con_Printf("%i", fontslot[i].size[j]); if (fontslot[i].font[j]) { th = Font_GetTrueHeight(fontslot[i].font[j]); if (th != Font_CharPHeight(fontslot[i].font[j])) Con_Printf("[%g]", ((float)th*vid.height)/vid.pixelheight); } } Con_Printf(")\n"); } } } else { int i; int slotnum = 0; char *slotname = Cmd_Argv(1); char *facename = Cmd_Argv(2); int sizenum = 3; extern cvar_t dpcompat_console, gl_font, con_textfont; //loadfont slot face size1 size2... slotnum = PR_findnamedfont(slotname, true); if (slotnum < 0) { char *dpnames[] = {"default", "console", "sbar", "notify", "chat", "centerprint", "infobar", "menu", "user0", "user1", "user2", "user3", "user4", "user5", "user6", "user7", NULL}; for (i = 0; dpnames[i]; i++) { if (!strcmp(dpnames[i], slotname)) { //assign it to this slot only if this slot does not already have a face. avoids corrupting already-loaded fonts. if (!*fontslot[i].facename) slotnum = i; break; } } if (slotnum < 0) slotnum = PR_findnamedfont("", true); //whatever is still free } if (slotnum < 0) { Con_Printf("out of font slots\n"); return; } //if there's a new font in this slot, purge the old and change the name+face strings if (stricmp(fontslot[slotnum].slotname, slotname) || stricmp(fontslot[slotnum].facename, facename)) { Q_strncpyz(fontslot[slotnum].slotname, slotname, sizeof(fontslot[slotnum].slotname)); Q_strncpyz(fontslot[slotnum].facename, facename, sizeof(fontslot[slotnum].facename)); for (i = 0; i < fontslot[slotnum].sizes; i++) { if (fontslot[slotnum].font[i]) Font_Free(fontslot[slotnum].font[i]); fontslot[slotnum].font[i] = NULL; } fontslot[slotnum].owner = 0; fontslot[slotnum].scale = 1; fontslot[slotnum].sizes = 0; fontslot[slotnum].outline = r_font_postprocess_outline.ival; //locked in at definition, so different fonts can have different settings even with vid_reload going on. } if (!*facename) return; fontslot[slotnum].owner |= kdm_console; //fonts owned by the console are never forgotten. while(sizenum < Cmd_Argc()) { const char *a = Cmd_Argv(sizenum++); int sz; if (!strcmp(a, "scale")) { fontslot[slotnum].scale = atof(Cmd_Argv(sizenum++)); continue; } if (!strcmp(a, "outline")) { fontslot[slotnum].outline = atoi(Cmd_Argv(sizenum++)); continue; } if (!strcmp(a, "blur")) { //fontslot[slotnum].blur = atoi(Cmd_Argv(sizenum++)); sizenum++; continue; } if (!strcmp(a, "voffset")) { // fontslot[slotnum].voffset = atof(Cmd_Argv(sizenum++)); sizenum++; continue; } sz = atoi(a); if (sz <= 0) sz = 8; for (i = 0; i < fontslot[slotnum].sizes; i++) { if (fontslot[slotnum].size[i] == sz) break; } if (i == fontslot[slotnum].sizes) { if (i >= FONT_SIZES) break; fontslot[slotnum].size[i] = sz; fontslot[slotnum].font[i] = NULL; fontslot[slotnum].sizes++; } } if (qrenderer > QR_NONE) { for (i = 0; i < fontslot[slotnum].sizes; i++) fontslot[slotnum].font[i] = Font_LoadFont(facename, fontslot[slotnum].size[i], fontslot[slotnum].scale, fontslot[slotnum].outline); } //FIXME: slotnum0==default is problematic. if (dpcompat_console.ival && slotnum == 1) Cvar_Set(&con_textfont, facename); if (dpcompat_console.ival && slotnum == 0) Cvar_Set(&gl_font, facename); } } #endif //scrolling could be done with scissoring. //selection could be done with some substrings void QCBUILTIN PF_CL_DrawTextField (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *pos = G_VECTOR(OFS_PARM0); float *size = G_VECTOR(OFS_PARM1); unsigned int flags = G_FLOAT(OFS_PARM2); const char *text = PR_GetStringOfs(prinst, OFS_PARM3); world_t *world = prinst->parms->user; vec2_t scale = {8, 8}; struct font_s *font; if (world->g.drawfontscale && (world->g.drawfontscale[0] || world->g.drawfontscale[1])) { scale[0] *= world->g.drawfontscale[0]; scale[1] *= world->g.drawfontscale[1]; } font = PR_CL_ChooseFont(world->g.drawfont, scale[0], scale[1]); // Oversight ~eukara R2D_ImageColours(1.0f, 1.0f, 1.0f, 1.0f); G_FLOAT(OFS_RETURN) = R_DrawTextField(pos[0], pos[1], size[0], size[1], text, CON_WHITEMASK, flags, font, scale); } //float drawstring(vector position, string text, vector scale, float alpha, float flag) = #455; void QCBUILTIN PF_CL_drawcolouredstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *pos = G_VECTOR(OFS_PARM0); const char *text = PR_GetStringOfs(prinst, OFS_PARM1); float *size = G_VECTOR(OFS_PARM2); float alpha = 0; float flag = 0; float r, g, b; float px, py, ipx; unsigned int codeflags, codepoint; conchar_t buffer[2048], *str; if (prinst->callargc >= 6) { r = G_FLOAT(OFS_PARM3 + 0); g = G_FLOAT(OFS_PARM3 + 1); b = G_FLOAT(OFS_PARM3 + 2); alpha = G_FLOAT(OFS_PARM4); flag = G_FLOAT(OFS_PARM5); //flag is mandatory to distinguish it. } else { r = 1; g = 1; b = 1; alpha = G_FLOAT(OFS_PARM3); flag = prinst->callargc >= 5?G_FLOAT(OFS_PARM4):0; } if (!text) { G_FLOAT(OFS_RETURN) = -1; //was null.. return; } COM_ParseFunString(CON_WHITEMASK, text, buffer, sizeof(buffer), false); str = buffer; r2d_be_flags = PF_SelectDPDrawFlag(prinst, flag); PR_CL_BeginString(prinst, pos[0], pos[1], size[0], size[1], &px, &py); ipx = px; R2D_ImageColours(r, g, b, alpha); while(*str) { str = Font_Decode(str, &codeflags, &codepoint); if (codeflags & CON_HIDDEN) continue; if (codepoint == '\n') py += Font_CharHeight(); else if (codepoint == '\r') px = ipx; else px = Font_DrawScaleChar(px, py, codeflags, codepoint); } R2D_ImageColours(1,1,1,1); Font_EndString(NULL); r2d_be_flags = 0; } void QCBUILTIN PF_CL_stringwidth(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { conchar_t buffer[2048], *end; float px, py; const char *text = PR_GetStringOfs(prinst, OFS_PARM0); int usecolours = G_FLOAT(OFS_PARM1); float *size = (prinst->callargc > 2)?G_VECTOR(OFS_PARM2):NULL; if (!qrenderer) { G_FLOAT(OFS_RETURN) = 0; return; } end = COM_ParseFunString(CON_WHITEMASK, text, buffer, sizeof(buffer), !usecolours); PR_CL_BeginString(prinst, 0, 0, size?size[0]:8, size?size[1]:8, &px, &py); px = Font_LineScaleWidth(buffer, end); Font_EndString(NULL); if (!size) //for compat with dp, divide by 8 after... because weird. px /= 8; G_FLOAT(OFS_RETURN) = (px * vid.width) / vid.rotpixelwidth; } //float drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag) = #456; void QCBUILTIN PF_CL_drawpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *pos = G_VECTOR(OFS_PARM0); const char *picname = PR_GetStringOfs(prinst, OFS_PARM1); float *size = G_VECTOR(OFS_PARM2); float *rgb = G_VECTOR(OFS_PARM3); float alpha = G_FLOAT(OFS_PARM4); int flag = prinst->callargc >= 6?(int)G_FLOAT(OFS_PARM5):0; mpic_t *p; p = R2D_SafeCachePic(picname); if (!p || !R_GetShaderSizes(p, NULL, NULL, false)) p = R2D_SafePicFromWad(picname); if (!p) { if (!CL_IsDownloading(picname)) p = R2D_SafeCachePic("no_texture"); G_FLOAT(OFS_RETURN) = 0; } else G_FLOAT(OFS_RETURN) = 1; r2d_be_flags = PF_SelectDPDrawFlag(prinst, flag); R2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha); if ((size[0] < 0) ^ (size[1] < 0)) R2D_Image(pos[0]+size[0], pos[1]+size[1], -size[0], -size[1], 1, 1, 0, 0, p); else R2D_Image(pos[0], pos[1], size[0], size[1], 0, 0, 1, 1, p); r2d_be_flags = 0; } void QCBUILTIN PF_CL_drawrotpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *pivot = G_VECTOR(OFS_PARM0); float *mins = G_VECTOR(OFS_PARM1); float *maxs = G_VECTOR(OFS_PARM2); const char *picname = PR_GetStringOfs(prinst, OFS_PARM3); float *rgb = G_VECTOR(OFS_PARM4); float alpha = G_FLOAT(OFS_PARM5); float angle = (G_FLOAT(OFS_PARM6) * M_PI)/180; int flag = prinst->callargc >= 8?(int) G_FLOAT(OFS_PARM7):0; vec2_t points[4]; vec2_t tcoords[4]; vec2_t saxis; vec2_t taxis; mpic_t *p; p = R2D_SafeCachePic(picname); if (!p) p = R2D_SafePicFromWad(picname); saxis[0] = cos(angle); saxis[1] = sin(angle); taxis[0] = -sin(angle); taxis[1] = cos(angle); Vector2MA(pivot, mins[0], saxis, points[0]); Vector2MA(points[0], mins[1], taxis, points[0]); Vector2MA(pivot, maxs[0], saxis, points[1]); Vector2MA(points[1], mins[1], taxis, points[1]); Vector2MA(pivot, maxs[0], saxis, points[2]); Vector2MA(points[2], maxs[1], taxis, points[2]); Vector2MA(pivot, mins[0], saxis, points[3]); Vector2MA(points[3], maxs[1], taxis, points[3]); Vector2Set(tcoords[0], 0, 0); Vector2Set(tcoords[1], 1, 0); Vector2Set(tcoords[2], 1, 1); Vector2Set(tcoords[3], 0, 1); r2d_be_flags = PF_SelectDPDrawFlag(prinst, flag); R2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha); R2D_Image2dQuad((const vec2_t*)points, (const vec2_t*)tcoords, NULL, p); r2d_be_flags = 0; G_FLOAT(OFS_RETURN) = 1; } void QCBUILTIN PF_CL_drawsubpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *pos = G_VECTOR(OFS_PARM0); float *size = G_VECTOR(OFS_PARM1); const char *picname = PR_GetStringOfs(prinst, OFS_PARM2); float *srcPos = G_VECTOR(OFS_PARM3); float *srcSize = G_VECTOR(OFS_PARM4); float *rgb = G_VECTOR(OFS_PARM5); float alpha = G_FLOAT(OFS_PARM6); int flag = prinst->callargc >= 8?(int) G_FLOAT(OFS_PARM7):0; mpic_t *p; p = R2D_SafeCachePic(picname); if (!p || !R_GetShaderSizes(p, NULL, NULL, false)) p = R2D_SafePicFromWad(picname); r2d_be_flags = PF_SelectDPDrawFlag(prinst, flag); R2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha); if ((size[0] < 0) ^ (size[1] < 0)) R2D_Image(pos[0]+size[0], pos[1]+size[1], -size[0], -size[1], srcPos[0]+srcSize[0], srcPos[1]+srcSize[1], srcPos[0], srcPos[1], p); else R2D_Image(pos[0], pos[1], size[0], size[1], srcPos[0], srcPos[1], srcPos[0]+srcSize[0], srcPos[1]+srcSize[1], p); r2d_be_flags = 0; G_FLOAT(OFS_RETURN) = 1; } void QCBUILTIN PF_CL_drawrotsubpic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *pivot = G_VECTOR(OFS_PARM0); float *mins = G_VECTOR(OFS_PARM1); float *maxs = G_VECTOR(OFS_PARM2); const char *picname = PR_GetStringOfs(prinst, OFS_PARM3); float *srcPos = G_VECTOR(OFS_PARM4); float *srcSize = G_VECTOR(OFS_PARM5); float *rgb = G_VECTOR(OFS_PARM6); float alpha = G_FLOAT(OFS_PARM7+0); float angle = (G_FLOAT(OFS_PARM7+1) * M_PI) / 180; int flag = prinst->callargc >= 8?(int) G_FLOAT(OFS_PARM7+2):0; vec2_t points[4], tcoords[4]; vec2_t saxis; vec2_t taxis; mpic_t *p; saxis[0] = cos(angle); saxis[1] = sin(angle); taxis[0] = -sin(angle); taxis[1] = cos(angle); p = R2D_SafeCachePic(picname); if (!p) p = R2D_SafePicFromWad(picname); Vector2MA(pivot, mins[0], saxis, points[0]); Vector2MA(points[0], mins[1], taxis, points[0]); Vector2MA(pivot, maxs[0], saxis, points[1]); Vector2MA(points[1], mins[1], taxis, points[1]); Vector2MA(pivot, maxs[0], saxis, points[2]); Vector2MA(points[2], maxs[1], taxis, points[2]); Vector2MA(pivot, mins[0], saxis, points[3]); Vector2MA(points[3], maxs[1], taxis, points[3]); Vector2Set(tcoords[0], srcPos[0] , srcPos[1] ); Vector2Set(tcoords[1], srcPos[0]+srcSize[0] , srcPos[1] ); Vector2Set(tcoords[2], srcPos[0]+srcSize[0] , srcPos[1]+srcSize[1] ); Vector2Set(tcoords[3], srcPos[0] , srcPos[1]+srcSize[1] ); r2d_be_flags = PF_SelectDPDrawFlag(prinst, flag); R2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha); R2D_Image2dQuad((const vec2_t*)points, (const vec2_t*)tcoords, NULL, p); r2d_be_flags = 0; G_FLOAT(OFS_RETURN) = 1; } #ifdef HAVE_LEGACY /*fuck sake, why does no one give a shit about existing extension?!? seriously this stuff is pissing me off*/ void QCBUILTIN PF_CL_drawrotpic_dp (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *pivot = G_VECTOR(OFS_PARM0); const char *picname = PR_GetStringOfs(prinst, OFS_PARM1); float *size = G_VECTOR(OFS_PARM2); float *mins = G_VECTOR(OFS_PARM3); float angle = (G_FLOAT(OFS_PARM4) * M_PI)/180; float *rgb = G_VECTOR(OFS_PARM5); float alpha = G_FLOAT(OFS_PARM6); int flag = prinst->callargc >= 8?(int) G_FLOAT(OFS_PARM7):0; vec3_t maxs; vec2_t points[4]; vec2_t tcoords[4]; vec2_t saxis; vec2_t taxis; mpic_t *p; VectorSubtract(size, mins, maxs); p = R2D_SafeCachePic(picname); if (!p) p = R2D_SafePicFromWad(picname); saxis[0] = cos(angle); saxis[1] = sin(angle); taxis[0] = -sin(angle); taxis[1] = cos(angle); Vector2MA(pivot, mins[0], saxis, points[0]); Vector2MA(points[0], mins[1], taxis, points[0]); Vector2MA(pivot, maxs[0], saxis, points[1]); Vector2MA(points[1], mins[1], taxis, points[1]); Vector2MA(pivot, maxs[0], saxis, points[2]); Vector2MA(points[2], maxs[1], taxis, points[2]); Vector2MA(pivot, mins[0], saxis, points[3]); Vector2MA(points[3], maxs[1], taxis, points[3]); Vector2Set(tcoords[0], 0, 0); Vector2Set(tcoords[1], 1, 0); Vector2Set(tcoords[2], 1, 1); Vector2Set(tcoords[3], 0, 1); r2d_be_flags = PF_SelectDPDrawFlag(prinst, flag); R2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha); R2D_Image2dQuad((const vec2_t*)points, (const vec2_t*)tcoords, NULL, p); r2d_be_flags = 0; G_FLOAT(OFS_RETURN) = 1; } #endif void QCBUILTIN PF_CL_is_cached_pic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *str; str = PR_GetStringOfs(prinst, OFS_PARM0); G_FLOAT(OFS_RETURN) = !!R_RegisterCustom(str, SUF_2D, NULL, NULL); } void QCBUILTIN PF_CL_precache_pic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { extern cvar_t pr_precachepic_slow; const char *str; mpic_t *pic; unsigned int flags; #define PIC_FROMWAD 1 /*obsolete, probably better to just use gfx/foo.lmp instead*/ //#define PIC_TEMPORARY 2 /*DP, not meaningful here*/ //#define PIC_NOCLAMP 4 /*DP, not useful here - use a shader instead*/ //#define PIC_MIPMAP 8 /*DP, not useful here - use a shader instead*/ #define PRECACHE_PIC_DOWNLOAD 256 /*block until loaded, downloading if missing*/ #define PRECACHE_PIC_TEST 512 /*block until loaded*/ G_INT(OFS_RETURN) = G_INT(OFS_PARM0); str = PR_GetStringOfs(prinst, OFS_PARM0); if (prinst->callargc > 1) flags = G_FLOAT(OFS_PARM1); else flags = 0; if (pr_precachepic_slow.ival) flags |= PRECACHE_PIC_DOWNLOAD|PRECACHE_PIC_TEST; if (flags & PIC_FROMWAD) pic = R2D_SafePicFromWad(str); else { pic = R2D_SafeCachePic(str); if ((flags & PRECACHE_PIC_DOWNLOAD) && cls.state //if we're allowed to download it... && strchr(str, '.') //only try to download it if it looks as though it contains a path. #ifndef CLIENTONLY && !sv.active //not if we're already the server... #endif && (!pic || !R_GetShaderSizes(pic, NULL, NULL, true))) //and it wasn't loaded... CL_CheckOrEnqueDownloadFile(str, str, 0); } if (flags & PRECACHE_PIC_TEST) if (!pic || !R_GetShaderSizes(pic, NULL, NULL, true)) G_INT(OFS_RETURN) = 0; } #ifdef CSQC_DAT //warning: not threaded. void QCBUILTIN PF_CL_uploadimage (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *imagename = PR_GetStringOfs(prinst, OFS_PARM0); int width = G_INT(OFS_PARM1); int height = G_INT(OFS_PARM2); int src = G_INT(OFS_PARM3); //ptr int size = (prinst->callargc > 4)?G_INT(OFS_PARM4):(width * height * 4); uploadfmt_t format = (prinst->callargc > 5)?PR_TranslateTextureFormat(G_INT(OFS_PARM5)):TF_RGBA32; void *imgptr; texid_t tid; G_INT(OFS_RETURN) = 0; //assume the worst if (width < 0 || height < 0 || width > 16384 || height > 16384) { //this is actually kinda likely when everyone assumes everything is a float. PR_BIError(prinst, "PF_CL_uploadimage: dimensions are out of range\n"); return; } //FIXME: this should use a proper qclib function to validate more reliably / reusably if (src <= 0 || src+size >= prinst->stringtablesize) { PR_BIError(prinst, "PF_CL_uploadimage: invalid source\n"); return; } imgptr = prinst->stringtable + src; tid = Image_FindTexture(imagename, NULL, RT_IMAGEFLAGS); if (!TEXVALID(tid)) tid = Image_CreateTexture(imagename, NULL, RT_IMAGEFLAGS); if (!format) { void *data = BZ_Malloc(size); memcpy(data, imgptr, size); G_INT(OFS_RETURN) = Image_LoadTextureFromMemory(tid, tid->flags, tid->ident, imagename, data, size); } else { unsigned int blockbytes, blockwidth, blockheight, blockdepth; //get format info Image_BlockSizeForEncoding(format, &blockbytes, &blockwidth, &blockheight, &blockdepth); //round up as appropriate blockwidth = ((width+blockwidth-1)/blockwidth)*blockwidth; blockheight = ((height+blockheight-1)/blockheight)*blockheight; if (size != blockwidth*blockheight*blockbytes) G_INT(OFS_RETURN) = 0; //size isn't right. which means the pointer might be invalid too. else { Image_Upload(tid, format, imgptr, NULL, width, height, 1, RT_IMAGEFLAGS); tid->width = width; tid->height = height; G_INT(OFS_RETURN) = 1; } } } #endif //warning: not threadable. hopefully noone abuses it. void QCBUILTIN PF_CL_readimage (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { size_t filesize; const char *filename = PR_GetStringOfs(prinst, OFS_PARM0); int imagewidth, imageheight; uploadfmt_t format; void *filedata; G_INT(OFS_RETURN) = 0; //assume the worst G_INT(OFS_PARM1) = 0; //out width G_INT(OFS_PARM2) = 0; //out height filedata = FS_LoadMallocFile(filename, &filesize); if (filedata) { qbyte *imagedata = ReadRawImageFile(filedata, filesize, &imagewidth, &imageheight, &format, true, filename); Z_Free(filedata); if (imagedata) { void *ptr = prinst->AddressableAlloc(prinst, imagewidth*imageheight*4); if (ptr) { memcpy(ptr, imagedata, imagewidth*imageheight*4); G_INT(OFS_RETURN) = (char*)ptr - prinst->stringtable; G_INT(OFS_PARM1) = imagewidth; //out width G_INT(OFS_PARM2) = imageheight; //out height } BZ_Free(imagedata); } } } void QCBUILTIN PF_CL_free_pic (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //we don't support this, as the shader could be used elsewhere also, and we have pointers to things. /* char *str; str = PR_GetStringOfs(prinst, OFS_PARM0); R_UnloadShader(R_RegisterCustom(str, NULL, NULL)); */ } //float drawcharacter(vector position, float character, vector scale, vector rgb, float alpha, float flag) = #454; void QCBUILTIN PF_CL_drawcharacter (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *pos = G_VECTOR(OFS_PARM0); int chara = G_FLOAT(OFS_PARM1); float *size = G_VECTOR(OFS_PARM2); float *rgb = G_VECTOR(OFS_PARM3); float alpha = G_FLOAT(OFS_PARM4); int flag = prinst->callargc >= 6?G_FLOAT(OFS_PARM5):0; float x, y; if (!chara) { G_FLOAT(OFS_RETURN) = -1; //was null.. return; } //no control chars. use quake ones if so if (!(flag & 4) && !com_parseutf8.ival) { //ugly quake chars... if (chara >= 32 && chara < 128) ; //ascii-comptaible range else chara |= 0xe000; //use quake glyphs (including for red text, unfortunately) } r2d_be_flags = PF_SelectDPDrawFlag(prinst, flag); PR_CL_BeginString(prinst, pos[0], pos[1], size[0], size[1], &x, &y); R2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha); Font_DrawScaleChar(x, y, CON_WHITEMASK, chara); R2D_ImageColours(1,1,1,1); Font_EndString(NULL); r2d_be_flags = 0; G_FLOAT(OFS_RETURN) = 1; } //float drawrawstring(vector position, string text, vector scale, vector rgb, float alpha, float flag) = #455; void QCBUILTIN PF_CL_drawrawstring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *pos = G_VECTOR(OFS_PARM0); const char *text = PR_GetStringOfs(prinst, OFS_PARM1); float *size = G_VECTOR(OFS_PARM2); float *rgb = G_VECTOR(OFS_PARM3); float alpha = G_FLOAT(OFS_PARM4); int flag = prinst->callargc >= 6?G_FLOAT(OFS_PARM5):0; float x, y; unsigned int c; int error; if (!text) { G_FLOAT(OFS_RETURN) = -1; //was null.. return; } r2d_be_flags = PF_SelectDPDrawFlag(prinst, flag); PR_CL_BeginString(prinst, pos[0], pos[1], size[0], size[1], &x, &y); R2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha); while(*text) { if (1)//VMUTF8) c = unicode_decode(&error, text, &text, false); else { //FIXME: which charset is this meant to be using? //quakes? 8859-1? utf8? some weird hacky mixture? c = *text++&0xff; if ((c&0x7f) < 32) c |= 0xe000; //if its a control char, just use the quake range instead. else if (c & 0x80) c |= 0xe000; //if its a high char, just use the quake range instead. we could colour it, but why bother } x = Font_DrawScaleChar(x, y, CON_WHITEMASK, c); } R2D_ImageColours(1,1,1,1); Font_EndString(NULL); r2d_be_flags = 0; } //void (float width, vector pos1, vector pos2, vector rgb, float alpha, optional float flags) drawline; void QCBUILTIN PF_CL_drawline (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //float width = G_FLOAT(OFS_PARM0); float *point1 = G_VECTOR(OFS_PARM1); float *point2 = G_VECTOR(OFS_PARM2); float *rgb = G_VECTOR(OFS_PARM3); float alpha = G_FLOAT(OFS_PARM4); int flags = prinst->callargc >= 6?G_FLOAT(OFS_PARM5):0; shader_t *shader_draw_line; //this shader lookup might get pricy. shader_draw_line = R_RegisterShader("shader_draw_line", SUF_NONE, "{\n" "program defaultfill\n" "{\n" "map $whiteimage\n" "rgbgen exactvertex\n" "alphagen vertex\n" "blendfunc blend\n" "}\n" "}\n"); r2d_be_flags = PF_SelectDPDrawFlag(prinst, flags); R2D_ImageColours(rgb[0], rgb[1], rgb[2], alpha); R2D_Line(point1[0], point1[1], point2[0], point2[1], shader_draw_line); R2D_ImageColours(1,1,1,1); r2d_be_flags = 0; } //vector drawgetimagesize(string pic) = #460; void QCBUILTIN PF_CL_drawgetimagesize (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *picname = PR_GetStringOfs(prinst, OFS_PARM0); mpic_t *p = R2D_SafeCachePic(picname); float *ret = G_VECTOR(OFS_RETURN); int iw, ih; if (R_GetShaderSizes(p, &iw, &ih, true) > 0) { ret[0] = iw; ret[1] = ih; ret[2] = 0; } else { ret[0] = 0; ret[1] = 0; ret[2] = 0; } } //vector getmousepos(void) = #66; void QCBUILTIN PF_cl_getmousepos (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { float *ret = G_VECTOR(OFS_RETURN); world_t *world = prinst->parms->user; unsigned int target = world->keydestmask; if (key_dest_absolutemouse & target) { ret[0] = mousecursor_x; ret[1] = mousecursor_y; } else { ret[0] = mousemove_x; ret[1] = mousemove_y; } mousemove_x=0; mousemove_y=0; // extern int mousecursor_x, mousecursor_y; // ret[0] = mousecursor_x; // ret[1] = mousecursor_y; ret[2] = 0; } void QCBUILTIN PF_SubConGetSet (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *conname = PR_GetStringOfs(prinst, OFS_PARM0); const char *field = PR_GetStringOfs(prinst, OFS_PARM1); const char *value = (prinst->callargc>2)?PR_GetStringOfs(prinst, OFS_PARM2):NULL; console_t *con = Con_FindConsole(conname); G_INT(OFS_RETURN) = 0; if (!con) { //null if it doesn't exist return; } if (!strcmp(field, "title")) { RETURN_TSTRING(con->title); if (value) Q_strncpyz(con->title, value, sizeof(con->title)); } else if (!strcmp(field, "name")) { RETURN_TSTRING(con->name); if (value && *value && *con->name) Q_strncpyz(con->name, value, sizeof(con->name)); } else if (!strcmp(field, "next")) { con = con->next; if (con) RETURN_TSTRING(con->name); } else if (!strcmp(field, "unseen")) { RETURN_TSTRING(va("%i", con->unseentext)); if (value) con->unseentext = atoi(value); } else if (!strcmp(field, "markup")) { int cur; if (con->parseflags & PFS_NOMARKUP) cur = 0; else if (con->parseflags & PFS_KEEPMARKUP) cur = 2; else cur = 1; RETURN_TSTRING(va("%i", cur)); if (value) { cur = atoi(value); con->parseflags &= ~(PFS_NOMARKUP|PFS_KEEPMARKUP); if (cur == 0) con->parseflags |= PFS_NOMARKUP; else if (cur == 2) con->parseflags |= PFS_KEEPMARKUP; } } else if (!strcmp(field, "forceutf8")) { RETURN_TSTRING((con->parseflags&PFS_FORCEUTF8)?"1":"0"); if (value) { con->parseflags &= ~PFS_FORCEUTF8; if (atoi(value)) con->parseflags |= PFS_FORCEUTF8; } } else if (!strcmp(field, "close")) { RETURN_TSTRING("0"); //meant to return the old state... if (value && atoi(value)) { if (con->close && atoi(value) != 2 && !con->close(con, true)) return; Con_Destroy(con); } } else if (!strcmp(field, "clear")) { RETURN_TSTRING(con->linecount?"0":"1"); if (value && atoi(value)) Con_ClearCon(con); } else if (!strcmp(field, "hidden")) { RETURN_TSTRING((con->flags & CONF_HIDDEN)?"1":"0"); if (value) con->flags = (con->flags & ~CONF_HIDDEN) | (atoi(value)?CONF_HIDDEN:0); } else if (!strcmp(field, "linecount")) { RETURN_TSTRING(va("%i", con->linecount)); if (value) con->unseentext = atoi(value); } else if (!strcmp(field, "backimage")) { RETURN_TSTRING(con->backshader?con->backshader->name:con->backimage); if (value) { Q_strncpyz(con->backimage, value, sizeof(con->backimage)); if (con->backshader) R_UnloadShader(con->backshader); } } else if (!strcmp(field, "backvideomap")) { RETURN_TSTRING(con->backshader?con->backshader->name:con->backimage); if (value) { Q_strncpyz(con->backimage, "", sizeof(con->backimage)); if (con->backshader) R_UnloadShader(con->backshader); con->backshader = R_RegisterCustom(va("consolevid_%s", con->name), SUF_NONE, Shader_DefaultCinematic, value); } } } void QCBUILTIN PF_SubConPrintf (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { char outbuf[4096]; const char *conname = PR_GetStringOfs(prinst, OFS_PARM0); const char *fmt = PR_GetStringOfs(prinst, OFS_PARM1); console_t *con = Con_FindConsole(conname); if (!con) { con = Con_Create(conname, 0); if (!con) return; } PF_sprintf_internal(prinst, pr_globals, fmt, 2, outbuf, sizeof(outbuf)); Con_PrintCon(con, outbuf, con->parseflags); } void QCBUILTIN PF_SubConDraw (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *conname = PR_GetStringOfs(prinst, OFS_PARM0); float *pos = G_VECTOR(OFS_PARM1); float *size = G_VECTOR(OFS_PARM2); float fontsize = G_FLOAT(OFS_PARM3); console_t *con = Con_FindConsole(conname); world_t *world = prinst->parms->user; if (!con) return; if (world->g.drawfontscale) { // szx *= world->g.drawfontscale[0]; fontsize *= world->g.drawfontscale[1]; } Con_DrawOneConsole(con, con->flags & CONF_KEYFOCUSED, PR_CL_ChooseFont(world->g.drawfont, fontsize, fontsize), pos[0], pos[1], size[0], size[1], 0); } void QCBUILTIN PF_SubConInput (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *conname = PR_GetStringOfs(prinst, OFS_PARM0); int ie = G_FLOAT(OFS_PARM1); float pa = G_FLOAT(OFS_PARM2); float pb = G_FLOAT(OFS_PARM3); // float pc = G_FLOAT(OFS_PARM4); console_t *con = Con_FindConsole(conname); G_FLOAT(OFS_RETURN) = 0; if (!con) return; switch(ie) { case CSIE_KEYDOWN: //scan, char if ((pa && qcinput_scan != pa) || (pb && pb != qcinput_unicode)) G_FLOAT(OFS_RETURN) = 0; else G_FLOAT(OFS_RETURN) = Key_Console(con, MP_TranslateQCtoFTECodes(pa), pb); break; case CSIE_KEYUP: //scan, char Key_ConsoleRelease(con, MP_TranslateQCtoFTECodes(pa), pb); G_FLOAT(OFS_RETURN) = 0; //does not inhibit break; case CSIE_MOUSEABS: //x, y if (con == con_current && (key_dest_mask & kdm_console)) break; //no interfering with the main console! con->mousecursor[0] = pa; con->mousecursor[1] = pb; G_FLOAT(OFS_RETURN) = true; break; case CSIE_FOCUS: //mouse, key if (pb >= 0) { con->flags = (con->flags & ~CONF_KEYFOCUSED) | (pb?CONF_KEYFOCUSED:0); G_FLOAT(OFS_RETURN) = true; } break; } } #endif #ifdef MENU_DAT typedef struct menuedict_s { enum ereftype_e ereftype; float freetime; // sv.time when the object was freed int entnum; unsigned int fieldsize; pbool readonly; //world void *fields; } menuedict_t; static struct { evalc_t chain; evalc_t model; evalc_t mins; evalc_t maxs; evalc_t origin; evalc_t angles; evalc_t skin; evalc_t colormap; evalc_t frame1; evalc_t frame2; evalc_t lerpfrac; evalc_t frame1time; evalc_t frame2time; evalc_t renderflags; evalc_t skinobject; evalc_t colourmod; evalc_t alpha; } menuc_eval; static playerview_t menuview; static menu_t menuqc; //this is how the client forwards events etc. static int inmenuprogs; static progparms_t menuprogparms; static menuedict_t *menu_edicts; static int num_menu_edicts; world_t menu_world; static int menuentsize; double menutime; static struct { func_t init; func_t shutdown; func_t draw; qboolean fuckeddrawsizes; func_t drawloading; func_t keydown; func_t keyup; func_t inputevent; func_t toggle; func_t consolecommand; func_t gethostcachecategory; func_t rendererrestarted; } mpfuncs; jmp_buf mp_abort; // cvars #define MENUPROGSGROUP "Menu progs control" cvar_t forceqmenu = CVAR("forceqmenu", "0"); cvar_t pr_menu_coreonerror = CVAR("pr_menu_coreonerror", "1"); cvar_t pr_menu_memsize = CVAR("pr_menu_memsize", "64m"); //new generic functions. const char *RemapCvarNameFromDPToFTE(const char *name) { if (!stricmp(name, "vid_bitsperpixel")) return "vid_bpp"; if (!stricmp(name, "_cl_playermodel")) return "model"; if (!stricmp(name, "_cl_playerskin")) return "skin"; if (!stricmp(name, "_cl_color")) return "topcolor"; if (!stricmp(name, "_cl_name")) return "name"; if (!stricmp(name, "v_contrast")) return "v_contrast"; if (!stricmp(name, "v_hwgamma")) return "vid_hardwaregamma"; if (!stricmp(name, "showfps")) return "show_fps"; if (!stricmp(name, "sv_progs")) return "progs"; return name; } static void QCBUILTIN PF_menu_cvar (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { cvar_t *var; const char *str; str = PR_GetStringOfs(prinst, OFS_PARM0); if (!strcmp(str, "vid_conwidth")) G_FLOAT(OFS_RETURN) = vid.width; else if (!strcmp(str, "vid_conheight")) G_FLOAT(OFS_RETURN) = vid.height; else if (!strcmp(str, "vid_pixwidth")) G_FLOAT(OFS_RETURN) = vid.pixelwidth; else if (!strcmp(str, "vid_pixheight")) G_FLOAT(OFS_RETURN) = vid.pixelheight; else { str = RemapCvarNameFromDPToFTE(str); var = PF_Cvar_FindOrGet(str); if (var && !(var->flags & CVAR_NOUNSAFEEXPAND)) { //menuqc sees desired settings, not latched settings. if (var->latched_string) G_FLOAT(OFS_RETURN) = atof(var->latched_string); else G_FLOAT(OFS_RETURN) = var->value; } else G_FLOAT(OFS_RETURN) = 0; } } static void QCBUILTIN PF_menu_cvar_set (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *var_name, *val; cvar_t *var; var_name = PR_GetStringOfs(prinst, OFS_PARM0); var_name = RemapCvarNameFromDPToFTE(var_name); val = PR_GetStringOfs(prinst, OFS_PARM1); var = PF_Cvar_FindOrGet(var_name); if (var && var->flags & CVAR_NOTFROMSERVER) { //fixme: menuqc needs some way to display a prompt to allow it anyway. return; } Cvar_Set (var, val); } static void QCBUILTIN PF_menu_cvar_string (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *str = PR_GetStringOfs(prinst, OFS_PARM0); cvar_t *cv = PF_Cvar_FindOrGet(RemapCvarNameFromDPToFTE(str)); if (!cv) G_INT(OFS_RETURN) = 0; else if (cv->flags & CVAR_NOUNSAFEEXPAND) G_INT(OFS_RETURN) = 0; else if (cv->latched_string) G_INT(OFS_RETURN) = (int)PR_TempString(prinst, cv->latched_string); else G_INT(OFS_RETURN) = (int)PR_TempString(prinst, cv->string); } void QCBUILTIN PF_nonfatalobjerror (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *s; struct edict_s *ed; eval_t *selfp; s = PF_VarString(prinst, 0, pr_globals); PR_StackTrace(prinst, true); selfp = PR_FindGlobal(prinst, "self", PR_CURRENT, NULL); if (selfp && selfp->_int) { ed = PROG_TO_EDICT(prinst, selfp->_int); PR_PrintEdict(prinst, ed); if (developer.value) { //enable tracing. PR_RunWarning(prinst, "======OBJECT ERROR======\n%s\n", s); return; } else { ED_Free (prinst, ed); } } Con_Printf ("======OBJECT ERROR======\n%s\n", s); } //float isserver(void) = #60; void QCBUILTIN PF_isserver (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { #ifdef CLIENTONLY G_FLOAT(OFS_RETURN) = false; #else if (sv.state == ss_dead) G_FLOAT(OFS_RETURN) = false; else if (sv.allocated_client_slots == 1) G_FLOAT(OFS_RETURN) = 0.5; else G_FLOAT(OFS_RETURN) = true; #endif } void QCBUILTIN PF_isdemo (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { G_FLOAT(OFS_RETURN) = !!cls.demoplayback; } //float clientstate(void) = #62; void QCBUILTIN PF_clientstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //menuqc was originally implemented in DP, so these return values follow NQ norms. if (isDedicated) //unreachable G_FLOAT(OFS_RETURN) = 0/*nq ca_dedicated*/; else if ( cls.state >= ca_connected //we're on a server || CL_TryingToConnect() //or we're trying to connect (avoids bugs with certain menuqc mods) #ifndef CLIENTONLY || sv.state>=ss_loading #endif ) //or we're going to connect to ourselves once we get our act together G_FLOAT(OFS_RETURN) = 2/*nq ca_connected*/; else G_FLOAT(OFS_RETURN) = 1/*nq ca_disconnected*/; } //too specific to the prinst's builtins. static void QCBUILTIN PF_Fixme (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int binum; char fname[MAX_QPATH]; if (!prinst->GetBuiltinCallInfo(prinst, &binum, fname, sizeof(fname))) { binum = 0; strcpy(fname, "?unknown?"); } Con_Printf("\n"); prinst->RunError(prinst, "\nBuiltin %i:%s not implemented.\nMenu is not compatible.", binum, fname); PR_BIError (prinst, "bulitin not implemented"); } static void QCBUILTIN PF_checkbuiltin (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { func_t funcref = G_INT(OFS_PARM0); char *funcname = NULL; int args; int builtinno; if (prinst->GetFunctionInfo(prinst, funcref, &args, NULL, &builtinno, funcname, sizeof(funcname))) { //qc defines the function at least. nothing weird there... if (builtinno > 0 && builtinno < prinst->parms->numglobalbuiltins) { if (!prinst->parms->globalbuiltins[builtinno] || prinst->parms->globalbuiltins[builtinno] == PF_Fixme) G_FLOAT(OFS_RETURN) = false; //the builtin with that number isn't defined. else { G_FLOAT(OFS_RETURN) = true; //its defined, within the sane range, mapped, everything. all looks good. //we should probably go through the available builtins and validate that the qc's name matches what would be expected //this is really intended more for builtins defined as #0 though, in such cases, mismatched assumptions are impossible. } } else G_FLOAT(OFS_RETURN) = false; //not a valid builtin (#0 builtins get remapped according to the function name) } else { //not valid somehow. G_FLOAT(OFS_RETURN) = false; } } void QCBUILTIN PF_CL_precache_sound (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *str; str = PR_GetStringOfs(prinst, OFS_PARM0); if (S_PrecacheSound(str)) G_INT(OFS_RETURN) = G_INT(OFS_PARM0); else G_INT(OFS_RETURN) = 0; } //void setkeydest(float dest) = #601; void QCBUILTIN PF_cl_setkeydest (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //these arguments are stupid switch((int)G_FLOAT(OFS_PARM0)) { case 0: // key_game if (Key_Dest_Has(kdm_menu)) { Menu_Unlink(&menuqc); Key_Dest_Remove(kdm_menu); // Key_Dest_Remove(kdm_message); // if (cls.state == ca_disconnected) // Key_Dest_Add(kdm_console); } break; case 2: // key_menu Key_Dest_Remove(kdm_message); if (!Key_Dest_Has(kdm_menu)) Key_Dest_Remove(kdm_console); Menu_Push(&menuqc, false); break; case 1: // key_message //Key_Dest_Remove(kdm_menu); //Key_Dest_Add(kdm_message); // break; default: PR_BIError (prinst, "PF_setkeydest: wrong destination %i !\n",(int)G_FLOAT(OFS_PARM0)); } } //float getkeydest(void) = #602; void QCBUILTIN PF_cl_getkeydest (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { if (Key_Dest_Has(kdm_menu)) G_FLOAT(OFS_RETURN) = 2; // else if (Key_Dest_Has(kdm_message)) // G_FLOAT(OFS_RETURN) = 1; else G_FLOAT(OFS_RETURN) = 0; } static void QCBUILTIN PF_Remove_ (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { menuedict_t *ed; ed = (void*)G_EDICT(prinst, OFS_PARM0); if (ed->ereftype == ER_FREE) { Con_DPrintf("Tried removing free entity\n"); PR_StackTrace(prinst, false); return; } ED_Free (prinst, (void*)ed); } static void QCBUILTIN PF_RemoveInstant (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { menuedict_t *ed; ed = (void*)G_EDICT(prinst, OFS_PARM0); if (ed->ereftype == ER_FREE) { Con_DPrintf("Tried removing free entity\n"); PR_StackTrace(prinst, false); return; } prinst->EntFree(prinst, (void*)ed, true); } static void QCBUILTIN PF_CopyEntity (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { menuedict_t *in, *out; in = (menuedict_t*)G_EDICT(prinst, OFS_PARM0); out = (menuedict_t*)G_EDICT(prinst, OFS_PARM1); memcpy(out->fields, in->fields, menuentsize); } void QCBUILTIN PF_menu_checkextension (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *extname = PR_GetStringOfs(prinst, OFS_PARM0); int i; G_FLOAT(OFS_RETURN) = 0; for (i = 0; i < QSG_Extensions_count; i++) { if (!QSG_Extensions[i].name) continue; if (!stricmp(extname, QSG_Extensions[i].name)) { G_FLOAT(OFS_RETURN) = 1; break; } } } void QCBUILTIN PF_CL_precache_file (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { G_INT(OFS_RETURN) = G_INT(OFS_PARM0); } //entity findchainstring(.string _field, string match) = #26; void QCBUILTIN PF_menu_findchain (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int i, f; const char *s; string_t t; menuedict_t *ent, *chain; //note, all edicts share the common header, but don't use it's fields! eval_t *val; chain = (menuedict_t *) *prinst->parms->edicts; f = G_INT(OFS_PARM0)+prinst->fieldadjust; s = PR_GetStringOfs(prinst, OFS_PARM1); for (i = 1; i < *prinst->parms->num_edicts; i++) { ent = (menuedict_t *)EDICT_NUM_PB(prinst, i); if (ent->ereftype == ER_FREE) continue; t = *(string_t *)&((float*)ent->fields)[f]; if (!t) continue; if (strcmp(PR_GetString(prinst, t), s)) continue; val = prinst->GetEdictFieldValue(prinst, (void*)ent, "chain", ev_entity, &menuc_eval.chain); if (val) val->edict = EDICT_TO_PROG(prinst, (void*)chain); chain = ent; } RETURN_EDICT(prinst, (void*)chain); } //entity findchainfloat(.float _field, float match) = #27; void QCBUILTIN PF_menu_findchainfloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int i, f; float s; menuedict_t *ent, *chain; //note, all edicts share the common header, but don't use it's fields! eval_t *val; chain = (menuedict_t *) *prinst->parms->edicts; f = G_INT(OFS_PARM0)+prinst->fieldadjust; s = G_FLOAT(OFS_PARM1); for (i = 1; i < *prinst->parms->num_edicts; i++) { ent = (menuedict_t*)EDICT_NUM_PB(prinst, i); if (ent->ereftype == ER_FREE) continue; if (((float *)ent->fields)[f] != s) continue; val = prinst->GetEdictFieldValue(prinst, (void*)ent, "chain", ev_entity, &menuc_eval.chain); if (val) val->edict = EDICT_TO_PROG(prinst, (void*)chain); chain = ent; } RETURN_EDICT(prinst, (void*)chain); } //entity findchainflags(.float _field, float match); void QCBUILTIN PF_menu_findchainflags (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int i, f; int s; menuedict_t *ent, *chain; //note, all edicts share the common header, but don't use it's fields! eval_t *val; chain = (menuedict_t *) *prinst->parms->edicts; f = G_INT(OFS_PARM0)+prinst->fieldadjust; s = G_FLOAT(OFS_PARM1); for (i = 1; i < *prinst->parms->num_edicts; i++) { ent = (menuedict_t*)EDICT_NUM_PB(prinst, i); if (ent->ereftype == ER_FREE) continue; if ((int)((float *)ent->fields)[f] & s) continue; val = prinst->GetEdictFieldValue(prinst, (void*)ent, "chain", ev_entity, &menuc_eval.chain); if (val) val->edict = EDICT_TO_PROG(prinst, (void*)chain); chain = ent; } RETURN_EDICT(prinst, (void*)chain); } void QCBUILTIN PF_etof(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { G_FLOAT(OFS_RETURN) = G_EDICTNUM(prinst, OFS_PARM0); } void QCBUILTIN PF_ftoe(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int entnum = G_FLOAT(OFS_PARM0); RETURN_EDICT(prinst, EDICT_NUM_UB(prinst, entnum)); } void QCBUILTIN PF_IsNotNull(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int str = G_INT(OFS_PARM0); G_FLOAT(OFS_RETURN) = !!str; } //float altstr_count(string str) = #82; //returns number of single quoted strings in the string. void QCBUILTIN PF_altstr_count(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *s; int count = 0; s = PR_GetStringOfs(prinst, OFS_PARM0); for (;*s;s++) { if (*s == '\\') { if (!*++s) break; } else if (*s == '\'') count++; } G_FLOAT(OFS_RETURN) = count/2; } //string altstr_prepare(string str) = #83; void QCBUILTIN PF_altstr_prepare(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { char outstr[8192], *out; const char *instr, *in; int size; // VM_SAFEPARMCOUNT( 1, VM_altstr_prepare ); instr = PR_GetStringOfs(prinst, OFS_PARM0 ); //VM_CheckEmptyString( instr ); for( out = outstr, in = instr, size = sizeof(outstr) - 1 ; size && *in ; size--, in++, out++ ) { if( *in == '\'' ) { *out++ = '\\'; *out = '\''; size--; } else *out = *in; } *out = 0; G_INT( OFS_RETURN ) = (int)PR_TempString( prinst, outstr ); } //string altstr_get(string str, float num) = #84; void QCBUILTIN PF_altstr_get(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *altstr, *pos; char outstr[8192], *out; int count, size; // VM_SAFEPARMCOUNT( 2, VM_altstr_get ); altstr = PR_GetStringOfs(prinst, OFS_PARM0 ); //VM_CheckEmptyString( altstr ); count = G_FLOAT( OFS_PARM1 ); count = count * 2 + 1; for( pos = altstr ; *pos && count ; pos++ ) { if( *pos == '\\' && !*++pos ) break; else if( *pos == '\'' ) count--; } if( !*pos ) { G_INT( OFS_RETURN ) = (int)PR_SetString( prinst, "" ); return; } for( out = outstr, size = sizeof(outstr) - 1 ; size && *pos ; size--, pos++, out++ ) { if( *pos == '\\' ) { if( !*++pos ) break; *out = *pos; size--; } else if( *pos == '\'' ) break; else *out = *pos; } *out = 0; G_INT( OFS_RETURN ) = (int)PR_TempString( prinst, outstr ); } //string altstr_set(string str, float num, string set) = #85 void QCBUILTIN PF_altstr_set(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int num; const char *altstr, *str; const char *in; char outstr[8192], *out; // VM_SAFEPARMCOUNT( 3, VM_altstr_set ); altstr = PR_GetStringOfs(prinst, OFS_PARM0 ); //VM_CheckEmptyString( altstr ); num = G_FLOAT( OFS_PARM1 ); str = PR_GetStringOfs(prinst, OFS_PARM2 ); //VM_CheckEmptyString( str ); out = outstr; for( num = num * 2 + 1, in = altstr; *in && num; *out++ = *in++ ) { if( *in == '\\' && !*++in ) break; else if( *in == '\'' ) num--; } if( !in ) { G_INT( OFS_RETURN ) = (int)PR_SetString( prinst, "" ); return; } // copy set in for( ; *str; *out++ = *str++ ) ; // now jump over the old contents for( ; *in ; in++ ) { if( *in == '\'' || (*in == '\\' && !*++in) ) break; } if( !in ) { G_INT( OFS_RETURN ) = (int)PR_SetString( prinst, "" ); return; } strcpy( out, in ); G_INT( OFS_RETURN ) = (int)PR_TempString( prinst, outstr ); } //string(string serveraddress) crypto_getkeyfp void QCBUILTIN PF_crypto_getkeyfp(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //not supported. G_INT(OFS_RETURN) = 0; } //string(string serveraddress) crypto_getidfp void QCBUILTIN PF_crypto_getidfp(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //not supported. G_INT(OFS_RETURN) = 0; } //float(string serveraddress) crypto_getidstatus void QCBUILTIN PF_crypto_getidstatus(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //not supported. G_INT(OFS_RETURN) = 0; } //string(string serveraddress) crypto_getencryptlevel void QCBUILTIN PF_crypto_getencryptlevel(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //not supported. G_INT(OFS_RETURN) = 0; } //string(float i) crypto_getmykeyfp void QCBUILTIN PF_crypto_getmykeyfp(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //not supported. G_INT(OFS_RETURN) = 0; } //string(float i) crypto_getmyidfp void QCBUILTIN PF_crypto_getmyidfp(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //not supported. G_INT(OFS_RETURN) = 0; } //float(float i) PF_crypto_getmyidstatus void QCBUILTIN PF_crypto_getmyidstatus(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { //not supported. G_INT(OFS_RETURN) = 0; } static void QCBUILTIN PF_m_precache_model(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *modelname = PR_GetStringOfs(prinst, OFS_PARM0); Mod_ForName(modelname, MLV_WARN); } static void QCBUILTIN PF_m_setmodel(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { menuedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0); const char *modelname = PR_GetStringOfs(prinst, OFS_PARM1); eval_t *modelval = prinst->GetEdictFieldValue(prinst, (void*)ent, "model", ev_string, &menuc_eval.model); eval_t *minsval = prinst->GetEdictFieldValue(prinst, (void*)ent, "mins", ev_vector, &menuc_eval.mins); eval_t *maxsval = prinst->GetEdictFieldValue(prinst, (void*)ent, "maxs", ev_vector, &menuc_eval.maxs); model_t *mod = Mod_ForName(modelname, MLV_WARN); if (modelval) modelval->string = G_INT(OFS_PARM1); //lets hope garbage collection is enough. else Con_Printf("PF_m_setmodel: no model field!\n"); if (mod) while(mod->loadstate == MLS_LOADING) COM_WorkerPartialSync(mod, &mod->loadstate, MLS_LOADING); if (mod && minsval) VectorCopy(mod->mins, minsval->_vector); if (mod && maxsval) VectorCopy(mod->maxs, maxsval->_vector); } static void QCBUILTIN PF_m_setcustomskin(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { menuedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0); const char *fname = PR_GetStringOfs(prinst, OFS_PARM1); const char *skindata = PF_VarString(prinst, 2, pr_globals); eval_t *val = prinst->GetEdictFieldValue(prinst, (void*)ent, "skinobject", ev_string, &menuc_eval.skinobject); if (!val) { Con_Printf("PF_m_setcustomskin: no skinobject field!\n"); return; } if (val->_float > 0) { Mod_WipeSkin(val->_float, false); val->_float = 0; } if (*fname || *skindata) { if (*skindata) val->_float = Mod_ReadSkinFile(fname, skindata); else val->_float = -(int)Mod_RegisterSkinFile(fname); } } //trivially basic static void QCBUILTIN PF_m_setorigin(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { menuedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0); float *org = G_VECTOR(OFS_PARM1); eval_t *val = prinst->GetEdictFieldValue(prinst, (void*)ent, "origin", ev_vector, &menuc_eval.origin); if (val) VectorCopy(org, val->_vector); else Con_Printf("PF_m_setorigin: no origin field!\n"); } static void QCBUILTIN PF_m_clearscene(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { // CL_DecayLights (); #if defined(SKELETALOBJECTS) || defined(RAGDOLLS) world_t *world = prinst->parms->user; if (world) skel_dodelete(world); #endif CL_ClearEntityLists(); V_ClearRefdef(&menuview); r_refdef.drawsbar = false; r_refdef.drawcrosshair = false; V_CalcRefdef(&menuview); //set up the defaults r_refdef.flags |= RDF_NOWORLDMODEL; } static qboolean CopyMenuEdictToEntity(pubprogfuncs_t *prinst, menuedict_t *in, entity_t *out) { eval_t *modelval = prinst->GetEdictFieldValue(prinst, (void*)in, "model", ev_string, &menuc_eval.model); eval_t *originval = prinst->GetEdictFieldValue(prinst, (void*)in, "origin", ev_vector, &menuc_eval.origin); eval_t *anglesval = prinst->GetEdictFieldValue(prinst, (void*)in, "angles", ev_vector, &menuc_eval.angles); eval_t *skinval = prinst->GetEdictFieldValue(prinst, (void*)in, "skin", ev_float, &menuc_eval.skin); eval_t *frame1val = prinst->GetEdictFieldValue(prinst, (void*)in, "frame", ev_float, &menuc_eval.frame1); eval_t *frame2val = prinst->GetEdictFieldValue(prinst, (void*)in, "frame2", ev_float, &menuc_eval.frame2); eval_t *lerpfracval = prinst->GetEdictFieldValue(prinst, (void*)in, "lerpfrac", ev_float, &menuc_eval.lerpfrac); eval_t *frame1timeval = prinst->GetEdictFieldValue(prinst, (void*)in, "frame1time", ev_float, &menuc_eval.frame1time); eval_t *frame2timeval = prinst->GetEdictFieldValue(prinst, (void*)in, "frame2time", ev_float, &menuc_eval.frame2time); eval_t *colormapval = prinst->GetEdictFieldValue(prinst, (void*)in, "colormap", ev_float, &menuc_eval.colormap); eval_t *renderflagsval = prinst->GetEdictFieldValue(prinst, (void*)in, "renderflags", ev_float, &menuc_eval.renderflags); eval_t *skinobjectval = prinst->GetEdictFieldValue(prinst, (void*)in, "skinobject", ev_float, &menuc_eval.skinobject); eval_t *colourmodval = prinst->GetEdictFieldValue(prinst, (void*)in, "colormod", ev_vector, &menuc_eval.colourmod); eval_t *alphaval = prinst->GetEdictFieldValue(prinst, (void*)in, "alpha", ev_float, &menuc_eval.alpha); int ival; int rflags; rflags = renderflagsval?renderflagsval->_float:0; memset(out, 0, sizeof(*out)); if (modelval) out->model = Mod_ForName(prinst->StringToNative(prinst, modelval->_int), MLV_WARN); if (originval) VectorCopy(originval->_vector, out->origin); if (!anglesval)anglesval = (eval_t*)vec3_origin; AngleVectors(anglesval->_vector, out->axis[0], out->axis[1], out->axis[2]); VectorInverse(out->axis[1]); out->scale = 1; out->skinnum = skinval?skinval->_float:0; out->framestate.g[FS_REG].frame[0] = frame1val?frame1val->_float:0; out->framestate.g[FS_REG].frame[1] = frame2val?frame2val->_float:0; out->framestate.g[FS_REG].lerpweight[1] = lerpfracval?lerpfracval->_float:0; out->framestate.g[FS_REG].lerpweight[0] = 1-out->framestate.g[FS_REG].lerpweight[1]; out->framestate.g[FS_REG].frametime[0] = frame1timeval?frame1timeval->_float:0; out->framestate.g[FS_REG].frametime[1] = frame2timeval?frame2timeval->_float:0; out->customskin = skinobjectval?skinobjectval->_float:0; //FIXME: colourmap ival = colormapval?colormapval->_float:0; out->playerindex = -1; if (ival >= 1024) { //DP COLORMAP extension out->topcolour = (ival>>4) & 0x0f; out->bottomcolour = ival & 0xf; } /* else if (ival > 0 && ival <= MAX_CLIENTS) { //FIXME: tie to the current skin/topcolor/bottomcolor cvars somehow? out->playerindex = ival - 1; out->topcolour = cl.players[ival-1].ttopcolor; out->bottomcolour = cl.players[ival-1].tbottomcolor; }*/ else { out->topcolour = TOP_DEFAULT; out->bottomcolour = BOTTOM_DEFAULT; } VectorSet(out->glowmod, 1,1,1); if (!colourmodval || (!colourmodval->_vector[0] && !colourmodval->_vector[1] && !colourmodval->_vector[2])) VectorSet(out->shaderRGBAf, 1, 1, 1); else { out->flags |= RF_FORCECOLOURMOD; VectorCopy(colourmodval->_vector, out->shaderRGBAf); } if (!alphaval || !alphaval->_float || alphaval->_float == 1) out->shaderRGBAf[3] = 1.0f; else { out->flags |= RF_TRANSLUCENT; out->shaderRGBAf[3] = alphaval->_float; } if (rflags & CSQCRF_ADDITIVE) out->flags |= RF_ADDITIVE; if (rflags & CSQCRF_DEPTHHACK) out->flags |= RF_DEPTHHACK; if (out->model) return true; return false; } static void QCBUILTIN PF_m_addentity(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { menuedict_t *in = (void*)G_EDICT(prinst, OFS_PARM0); entity_t ent; if (in->ereftype == ER_FREE || in->entnum == 0) { Con_Printf("Tried drawing a free/removed/world entity\n"); return; } if (CopyMenuEdictToEntity(prinst, in, &ent)) V_AddAxisEntity(&ent); } static void QCBUILTIN PF_m_renderscene(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { V_ApplyRefdef(); R_RenderView(); } void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_R_GetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); static void QCBUILTIN PF_menu_cprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *str = PF_VarString(prinst, 0, pr_globals); SCR_CenterPrint(0, str, true); } static void QCBUILTIN PF_cl_changelevel (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { #ifndef CLIENTONLY const char *nextmap = PR_GetStringOfs(prinst, OFS_PARM0); if (sv.active || !cls.state) { char buf[1024]; Cbuf_AddText(va("changelevel %s\n", COM_QuotedString(nextmap, buf, sizeof(buf), false)), RESTRICT_INSECURE); } #endif } static void QCBUILTIN PF_crash (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { int binum; char fname[MAX_QPATH]; //allow people to rename it or whatever if (!prinst->GetBuiltinCallInfo(prinst, &binum, fname, sizeof(fname))) { binum = 0; strcpy(fname, "?unknown?"); } prinst->RunError(prinst, "\n%s called", fname); } static void QCBUILTIN PF_stackdump (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { prinst->StackTrace(prinst, true); } #define PF_cl_clientcommand PF_Fixme #define PF_altstr_ins PF_Fixme //insert after, apparently static void MP_ConsoleCommand_f(void) { char cmd[2048]; Q_snprintfz(cmd, sizeof(cmd), "%s %s", Cmd_Argv(0), Cmd_Args()); MP_ConsoleCommand(cmd); } static void QCBUILTIN PF_menu_registercommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *str = PF_VarString(prinst, 0, pr_globals); if (!Cmd_Exists(str)) Cmd_AddCommand(str, MP_ConsoleCommand_f); } static void PF_m_clipboard_got(void *ctx, const char *utf8) { void *pr_globals; unsigned int unicode; int error; while (*utf8) { unicode = utf8_decode(&error, utf8, &utf8); if (error) unicode = 0xfffdu; if (!menu_world.progs || !mpfuncs.inputevent) return; #ifdef TEXTEDITOR if (editormodal) return; #endif pr_globals = PR_globals(menu_world.progs, PR_CURRENT); G_FLOAT(OFS_PARM0) = CSIE_PASTE; G_FLOAT(OFS_PARM1) = 0; G_FLOAT(OFS_PARM2) = unicode; G_FLOAT(OFS_PARM3) = 0; qcinput_scan = G_FLOAT(OFS_PARM1); qcinput_unicode = G_FLOAT(OFS_PARM2); PR_ExecuteProgram (menu_world.progs, mpfuncs.inputevent); qcinput_scan = 0; //and stop replay attacks qcinput_unicode = 0; } } static void QCBUILTIN PF_m_clipboard_get(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { clipboardtype_t cliptype = G_FLOAT(OFS_PARM0); Sys_Clipboard_PasteText(cliptype, PF_m_clipboard_got, prinst); } static struct { char *name; builtin_t bifunc; int ebfsnum; } BuiltinList[] = { {"checkextension", PF_menu_checkextension, 1}, {"checkbuiltin", PF_checkbuiltin, 0}, {"error", PF_error, 2}, {"objerror", PF_nonfatalobjerror, 3}, {"print", PF_print, 4}, {"bprint", PF_cl_bprint, 5}, {"msprint", PF_cl_sprint, 6}, {"cprint", PF_menu_cprint, 7}, {"normalize", PF_normalize, 8}, {"vlen", PF_vlen, 9}, {"vectoyaw", PF_vectoyaw, 10}, {"vectoangles", PF_vectoangles, 11}, {"crossproduct", PF_crossproduct, 0}, {"random", PF_random, 12}, {"localcmd", PF_localcmd, 13}, {"cvar", PF_menu_cvar, 14}, {"cvar_set", PF_menu_cvar_set, 15}, {"dprint", PF_dprint, 16}, {"ftos", PF_ftos, 17}, {"fabs", PF_fabs, 18}, {"vtos", PF_vtos, 19}, {"etos", PF_etos, 20}, {"stof", PF_stof, 21}, {"stoi", PF_stoi, 0}, {"itos", PF_itos, 0}, {"stoh", PF_stoh, 0}, {"htos", PF_htos, 0}, {"ftoi", PF_ftoi, 0}, {"itof", PF_itof, 0}, {"spawn", PF_Spawn, 22}, {"remove", PF_Remove_, 23}, {"removeinstant", PF_RemoveInstant, 0}, {"find", PF_FindString, 24}, {"findfloat", PF_FindFloat, 25}, {"findentity", PF_FindFloat, 25}, {"findchain", PF_menu_findchain, 26}, {"findchainfloat", PF_menu_findchainfloat, 27}, #ifdef QCGC {"find_list", PF_FindList, 0}, #endif {"precache_file", PF_CL_precache_file, 28}, {"precache_sound", PF_CL_precache_sound, 29}, {"coredump", PF_coredump, 30}, {"traceon", PF_traceon, 31}, {"traceoff", PF_traceoff, 32}, {"eprint", PF_eprint, 33}, {"rint", PF_rint, 34}, {"floor", PF_floor, 35}, {"ceil", PF_ceil, 36}, {"nextent", PF_nextent, 37}, {"sin", PF_Sin, 38}, {"cos", PF_Cos, 39}, {"sqrt", PF_Sqrt, 40}, {"randomvector", PF_randomvector, 41}, {"registercvar", PF_registercvar, 42}, {"min", PF_min, 43}, {"max", PF_max, 44}, {"bound", PF_bound, 45}, {"pow", PF_pow, 46}, {"logarithm", PF_Logarithm, 0}, {"entityprotection", PF_entityprotection, 0}, {"copyentity", PF_CopyEntity, 47}, {"fopen", PF_fopen, 48}, {"fclose", PF_fclose, 49}, {"fgets", PF_fgets, 50}, {"fputs", PF_fputs, 51}, {"fread", PF_fread, 0}, {"fwrite", PF_fwrite, 0}, {"fseek", PF_fseek, 0}, {"fsize", PF_fsize, 0}, {"strlen", PF_strlen, 52}, {"strcat", PF_strcat, 53}, {"substring", PF_substring, 54}, {"stov", PF_stov, 55}, {"strzone", PF_strzone, 56}, {"strunzone", PF_strunzone, 57}, #ifdef QCGC {"createbuffer", PF_createbuffer, 0}, #endif {"tokenize", PF_Tokenize, 58}, {"argv", PF_ArgV, 59}, {"isserver", PF_isserver, 60}, {"clientcount", PF_cl_clientcount, 61}, //float clientcount(void) = #61; {"clientstate", PF_clientstate, 62}, {"clientcommand", PF_cl_clientcommand, 63}, //void clientcommand(float client, string s) = #63; {"changelevel", PF_cl_changelevel, 64}, //void changelevel(string map) = #64; {"localsound", PF_cl_localsound, 65}, {"getmousepos", PF_cl_getmousepos, 66}, {"gettime", PF_gettime, 67}, {"loadfromdata", PF_loadfromdata, 68}, {"loadfromfile", PF_loadfromfile, 69}, {"mod", PF_mod, 70}, {"cvar_string", PF_menu_cvar_string, 71}, {"crash", PF_crash, 72}, //void crash(void) = #72; {"stackdump", PF_stackdump, 73}, //void stackdump(void) = #73; {"search_begin", PF_search_begin, 74}, {"search_end", PF_search_end, 75}, {"search_getsize", PF_search_getsize, 76}, {"search_getfilename", PF_search_getfilename, 77}, {"search_getfilesize", PF_search_getfilesize, 0}, {"search_getfilemtime", PF_search_getfilemtime, 0}, {"search_getpackagename", PF_search_getpackagename, 0}, {"search_fopen", PF_search_fopen, 0}, {"chr2str", PF_chr2str, 78}, {"etof", PF_etof, 79}, {"ftoe", PF_ftoe, 80}, {"validstring", PF_IsNotNull, 81}, {"altstr_count", PF_altstr_count, 82}, {"altstr_prepare", PF_altstr_prepare, 83}, {"altstr_get", PF_altstr_get, 84}, {"altstr_set", PF_altstr_set, 85}, {"altstr_ins", PF_altstr_ins, 86}, {"findflags", PF_FindFlags, 87}, {"findchainflags", PF_menu_findchainflags, 88}, {"cvar_defstring", PF_cvar_defstring, 89}, {"setmodel", PF_m_setmodel, 90}, {"precache_model", PF_m_precache_model, 91}, {"setorigin", PF_m_setorigin, 92}, //gap {"abort", PF_Abort, 211}, //gap {"strstrofs", PF_strstrofs, 221}, {"str2chr", PF_str2chr, 222}, {"chr2str", PF_chr2str, 223}, {"strconv", PF_strconv, 224}, {"strpad", PF_strpad, 225}, {"infoadd", PF_infoadd, 226}, {"infoget", PF_infoget, 227}, {"strcmp", PF_strncmp, 228}, {"strncmp", PF_strncmp, 228}, {"strcasecmp", PF_strncasecmp, 229}, {"strncasecmp", PF_strncasecmp, 230}, {"strtrim", PF_strtrim, 0}, //gap {"shaderforname", PF_shaderforname, 238}, {"sendpacket", PF_cl_SendPacket, 242}, //gap {"hash_createtab", PF_hash_createtab, 287}, {"hash_destroytab", PF_hash_destroytab, 288}, {"hash_add", PF_hash_add, 289}, {"hash_get", PF_hash_get, 290}, {"hash_delete", PF_hash_delete, 291}, {"hash_getkey", PF_hash_getkey, 292}, {"hash_getcb", PF_hash_getcb, 293}, {"checkcommand", PF_checkcommand, 294}, {"argescape", PF_argescape, 295}, //gap {"clearscene", PF_m_clearscene, 300}, // {"addentities", PF_Fixme, 301}, {"addentity", PF_m_addentity, 302},//FIXME: needs setmodel, origin, angles, colormap(eep), frame etc, skin, #ifdef CSQC_DAT {"setproperty", PF_R_SetViewFlag, 303},//should be okay to share #endif {"renderscene", PF_m_renderscene, 304},//too module-specific // {"dynamiclight_add", PF_R_DynamicLight_Add, 305},//should be okay to share {"R_BeginPolygon", PF_R_PolygonBegin, 306},//useful for 2d stuff {"R_PolygonVertex", PF_R_PolygonVertex, 307}, {"R_EndPolygon", PF_R_PolygonEnd, 308}, #ifdef CSQC_DAT {"getproperty", PF_R_GetViewFlag, 309},//should be okay to share #endif //unproject 310 //project 311 #ifdef CSQC_DAT {"r_uploadimage", PF_CL_uploadimage, 0}, #endif {"r_readimage", PF_CL_readimage, 0}, {"print_csqc", PF_print, 339}, {"keynumtostring_csqc", PF_cl_keynumtostring, 340}, {"stringtokeynum_csqc", PF_cl_stringtokeynum, 341}, {"getkeybind", PF_cl_getkeybind, 342}, {"setcursormode", PF_cl_setcursormode, 343}, {"getcursormode", PF_cl_getcursormode, 0}, {"setmousepos", PF_cl_setmousepos, 0}, {"clipboard_get", PF_m_clipboard_get, 0}, {"clipboard_set", PF_cl_clipboard_set, 0}, // {NULL, PF_Fixme, 344}, // {NULL, PF_Fixme, 345}, // {NULL, PF_Fixme, 346}, // {NULL, PF_Fixme, 347}, // {NULL, PF_Fixme, 348}, {"isdemo", PF_isdemo, 349}, // {NULL, PF_Fixme, 350}, // {NULL, PF_Fixme, 351}, {"registercommand", PF_menu_registercommand, 352}, {"wasfreed", PF_WasFreed, 353}, // {NULL, PF_Fixme, 354}, #ifdef HAVE_MEDIA_DECODER {"videoplaying", PF_cs_media_getstate, 355}, #endif {"findfont", PF_CL_findfont, 356}, {"loadfont", PF_CL_loadfont, 357}, //gap // {"dynamiclight_get", PF_R_DynamicLight_Get, 372}, // {"dynamiclight_set", PF_R_DynamicLight_Set, 373}, // {NULL, PF_Fixme, 374}, // {NULL, PF_Fixme, 375}, {"setcustomskin", PF_m_setcustomskin, 376}, //gap {"memalloc", PF_memalloc, 384}, {"memfree", PF_memfree, 385}, {"memcpy", PF_memcpy, 386}, {"memfill8", PF_memfill8, 387}, {"memgetval", PF_memgetval, 388}, {"memsetval", PF_memsetval, 389}, {"memptradd", PF_memptradd, 390}, {"memstrsize", PF_memstrsize, 0}, {"con_getset", PF_SubConGetSet, 391}, {"con_printf", PF_SubConPrintf, 392}, {"con_draw", PF_SubConDraw, 393}, {"con_input", PF_SubConInput, 394}, {"setwindowcaption", PF_cl_setwindowcaption, 0}, {"cvars_haveunsaved", PF_cvars_haveunsaved, 0}, //gap // {"writebyte, PF_Fixme, 401}, // {"writechar, PF_Fixme, 402}, // {"writeshort, PF_Fixme, 403}, // {"writelong, PF_Fixme, 404}, // {"writeangle, PF_Fixme, 405}, // {"writecoord, PF_Fixme, 406}, // {"writestring, PF_Fixme, 407}, // {"writeentity, PF_Fixme, 408}, //gap {"buf_create", PF_buf_create, 440}, {"buf_del", PF_buf_del, 441}, {"buf_getsize", PF_buf_getsize, 442}, {"buf_copy", PF_buf_copy, 443}, {"buf_sort", PF_buf_sort, 444}, {"buf_implode", PF_buf_implode, 445}, {"bufstr_get", PF_bufstr_get, 446}, {"bufstr_set", PF_bufstr_set, 447}, {"bufstr_add", PF_bufstr_add, 448}, {"bufstr_free", PF_bufstr_free, 449}, // {NULL, PF_Fixme, 450}, {"iscachedpic", PF_CL_is_cached_pic, 451}, {"precache_pic", PF_CL_precache_pic, 452}, {"free_pic", PF_CL_free_pic, 453}, {"drawcharacter", PF_CL_drawcharacter, 454}, {"drawrawstring", PF_CL_drawrawstring, 455}, {"drawpic", PF_CL_drawpic, 456}, {"drawrotpic", PF_CL_drawrotpic, 0}, {"drawfill", PF_CL_drawfill, 457}, {"drawsetcliparea", PF_CL_drawsetcliparea, 458}, {"drawresetcliparea", PF_CL_drawresetcliparea, 459}, {"drawgetimagesize", PF_CL_drawgetimagesize, 460}, #ifdef HAVE_MEDIA_DECODER {"cin_open", PF_cs_media_create, 461}, {"cin_close", PF_cs_media_destroy, 462}, {"cin_setstate", PF_cs_media_setstate, 463}, {"cin_getstate", PF_cs_media_getstate, 464}, {"cin_restart", PF_cs_media_restart, 465}, #endif {"drawline", PF_CL_drawline, 466}, {"drawstring", PF_CL_drawcolouredstring, 467}, {"stringwidth", PF_CL_stringwidth, 468}, {"drawsubpic", PF_CL_drawsubpic, 469}, {"drawrotsubpic", PF_CL_drawrotsubpic, 0}, {"drawtextfield", PF_CL_DrawTextField, 0}, #ifdef HAVE_LEGACY {"drawrotpic_dp", PF_CL_drawrotpic_dp, 470}, #endif //MERGES WITH CLIENT+SERVER BUILTIN MAPPINGS BELOW {"asin", PF_asin, 471}, {"acos", PF_acos, 472}, {"atan", PF_atan, 473}, {"atan2", PF_atan2, 474}, {"tan", PF_tan, 475}, {"strlennocol", PF_strlennocol, 476}, {"strdecolorize", PF_strdecolorize, 477}, {"strftime", PF_strftime, 478}, {"tokenizebyseparator", PF_tokenizebyseparator, 479}, {"strtolower", PF_strtolower, 480}, {"strtoupper", PF_strtoupper, 481}, {"csqc_cvar_defstring", PF_cvar_defstring, 482}, // {NULL, PF_Fixme, 483}, {"strreplace", PF_strreplace, 484}, {"strireplace", PF_strireplace, 485}, // {NULL, PF_Fixme, 486}, #ifdef HAVE_MEDIA_DECODER {"gecko_create", PF_cs_media_create, 487}, {"gecko_destroy", PF_cs_media_destroy, 488}, {"gecko_navigate", PF_cs_media_command, 489}, {"gecko_keyevent", PF_cs_media_keyevent, 490}, {"gecko_mousemove", PF_cs_media_mousemove, 491}, {"gecko_resize", PF_cs_media_resize, 492}, {"gecko_get_texture_extent",PF_cs_media_get_texture_extent,493}, {"gecko_getproperty", PF_cs_media_getproperty, 0}, #endif {"crc16", PF_crc16, 494}, {"cvar_type", PF_cvar_type, 495}, {"numentityfields", PF_numentityfields, 496}, {"findentityfield", PF_findentityfield, 0}, {"entityfieldref", PF_entityfieldref, 0}, {"entityfieldname", PF_entityfieldname, 497}, {"entityfieldtype", PF_entityfieldtype, 498}, {"getentityfieldstring", PF_getentityfieldstring, 499}, {"putentityfieldstring", PF_putentityfieldstring, 500}, // {NULL, PF_Fixme, 501}, // {NULL, PF_Fixme, 502}, {"whichpack", PF_whichpack, 503}, //gap {"uri_escape", PF_uri_escape, 510}, {"uri_unescape", PF_uri_unescape, 511}, {"num_for_edict", PF_etof, 512}, {"uri_get", PF_uri_get, 513}, {"uri_post", PF_uri_get, 513}, {"tokenize_console", PF_tokenize_console, 514}, {"argv_start_index", PF_argv_start_index, 515}, {"argv_end_index", PF_argv_end_index, 516}, {"buf_cvarlist", PF_buf_cvarlist, 517}, {"cvar_description", PF_cvar_description, 518}, //gap {"log", PF_Logarithm, 532}, // {"getsoundtime", PF_Fixme, 533}, {"soundlength", PF_soundlength, 534}, {"buf_loadfile", PF_buf_loadfile, 535}, {"buf_writefile", PF_buf_writefile, 536}, {"bufstr_find", PF_bufstr_find, 537}, // {"matchpattern", PF_Fixme, 538}, //gap {"setkeydest", PF_cl_setkeydest, 601}, {"getkeydest", PF_cl_getkeydest, 602}, {"setmousetarget", PF_cl_setmousetarget, 603}, {"getmousetarget", PF_cl_getmousetarget, 604}, {"callfunction", PF_callfunction, 605}, {"writetofile", PF_writetofile, 606}, {"isfunction", PF_isfunction, 607}, {"getresolution", PF_cl_getresolution, 608}, {"keynumtostring", PF_cl_keynumtostring, 609}, {"findkeysforcommand", PF_cl_findkeysforcommand, 610}, {"gethostcachevalue", PF_cl_gethostcachevalue, 611}, {"gethostcachestring", PF_cl_gethostcachestring, 612}, {"parseentitydata", PF_parseentitydata, 613}, {"generateentitydata", PF_generateentitydata, 0}, {"stringtokeynum", PF_cl_stringtokeynum, 614}, {"resethostcachemasks", PF_cl_resethostcachemasks, 615}, {"sethostcachemaskstring", PF_cl_sethostcachemaskstring,616}, {"sethostcachemasknumber", PF_cl_sethostcachemasknumber,617}, {"resorthostcache", PF_cl_resorthostcache, 618}, {"sethostcachesort", PF_cl_sethostcachesort, 619}, {"refreshhostcache", PF_cl_refreshhostcache, 620}, {"gethostcachenumber", PF_cl_gethostcachenumber, 621}, {"gethostcacheindexforkey", PF_cl_gethostcacheindexforkey,622}, {"addwantedhostcachekey", PF_cl_addwantedhostcachekey,623}, #ifdef CL_MASTER {"getextresponse", PF_cl_getextresponse, 624}, #endif {"netaddress_resolve", PF_netaddress_resolve, 625}, {"getgamedirinfo", PF_cl_getgamedirinfo, 626}, #ifdef PACKAGEMANAGER {"getpackagemanagerinfo", PF_cl_getpackagemanagerinfo,0}, #endif {"sprintf", PF_sprintf, 627}, // {NULL, PF_Fixme, 628}, // {NULL, PF_Fixme, 629}, {"setkeybind", PF_cl_setkeybind, 630}, {"getbindmaps", PF_cl_GetBindMap, 631}, {"setbindmaps", PF_cl_SetBindMap, 632}, {"crypto_getkeyfp", PF_crypto_getkeyfp, 633}, {"crypto_getidfp", PF_crypto_getidfp, 634}, {"crypto_getencryptlevel", PF_crypto_getencryptlevel, 635}, {"crypto_getmykeyfp", PF_crypto_getmykeyfp, 636}, {"crypto_getmyidfp", PF_crypto_getmyidfp, 637}, // {NULL, PF_Fixme, 638}, {"digest_hex", PF_digest_hex, 639}, {"digest_ptr", PF_digest_ptr, 0}, // {NULL, PF_Fixme, 640}, {"crypto_getmyidstatus", PF_crypto_getmyidstatus, 641}, // {"coverage", PF_Fixme, 642}, {"crypto_getidstatus", PF_crypto_getidstatus, 643}, // {NULL, PF_Fixme, 644}, // {NULL, PF_Fixme, 645}, // {NULL, PF_Fixme, 646}, // {NULL, PF_Fixme, 647}, // {NULL, PF_Fixme, 648}, // {NULL, PF_Fixme, 649}, {"fcopy", PF_fcopy, 650}, {"frename", PF_frename, 651}, {"fremove", PF_fremove, 652}, {"fexists", PF_fexists, 653}, {"rmtree", PF_rmtree, 654}, {"setlocaluserinfo", PF_cl_setlocaluserinfo, 0}, {"getlocaluserinfo", PF_cl_getlocaluserinfostring, 0}, {"setlocaluserinfoblob", PF_cl_setlocaluserinfo, 0}, {"getlocaluserinfoblob", PF_cl_getlocaluserinfoblob, 0}, {NULL} }; static builtin_t menu_builtins[1024]; int MP_BuiltinValid(const char *name, int num) { int i; for (i = 0; BuiltinList[i].name; i++) { if (BuiltinList[i].ebfsnum == num) { if (!strcmp(BuiltinList[i].name, name)) { if (/*BuiltinList[i].bifunc == PF_NoMenu ||*/ BuiltinList[i].bifunc == PF_Fixme) return false; else return true; } } } return false; } static void MP_SetupBuiltins(void) { int i; for (i = 0; i < sizeof(menu_builtins)/sizeof(menu_builtins[0]); i++) menu_builtins[i] = PF_Fixme; for (i = 0; BuiltinList[i].bifunc; i++) { if (BuiltinList[i].ebfsnum) menu_builtins[BuiltinList[i].ebfsnum] = BuiltinList[i].bifunc; } } static int PDECL PR_Menu_MapNamedBuiltin(pubprogfuncs_t *progfuncs, int headercrc, const char *builtinname) { int i, binum; for (i = 0;BuiltinList[i].name;i++) { if (!strcmp(BuiltinList[i].name, builtinname) && BuiltinList[i].bifunc != PF_Fixme) { for (binum = sizeof(menu_builtins)/sizeof(menu_builtins[0]); --binum; ) { if (menu_builtins[binum] && menu_builtins[binum] != PF_Fixme && BuiltinList[i].bifunc) continue; menu_builtins[binum] = BuiltinList[i].bifunc; return binum; } Con_Printf("No more builtin slots to allocate for %s\n", builtinname); break; } } Con_DPrintf("Unknown menu builtin: %s\n", builtinname); return 0; } static qboolean MP_MouseMove(menu_t *menu, qboolean isabs, unsigned int devid, float xdelta, float ydelta) { void *pr_globals; if (!menu_world.progs || !mpfuncs.inputevent) return false; if (setjmp(mp_abort)) return false; inmenuprogs++; pr_globals = PR_globals(menu_world.progs, PR_CURRENT); G_FLOAT(OFS_PARM0) = isabs?CSIE_MOUSEABS:CSIE_MOUSEDELTA; G_FLOAT(OFS_PARM1) = (xdelta * vid.width) / vid.pixelwidth; G_FLOAT(OFS_PARM2) = (ydelta * vid.height) / vid.pixelheight; G_FLOAT(OFS_PARM3) = devid; PR_ExecuteProgram (menu_world.progs, mpfuncs.inputevent); if (R2D_Flush) R2D_Flush(); inmenuprogs--; return G_FLOAT(OFS_RETURN); } static qboolean MP_JoystickAxis(menu_t *menu, unsigned int devid, int axis, float value) { void *pr_globals; if (!menu_world.progs || !mpfuncs.inputevent) return false; if (setjmp(mp_abort)) return false; inmenuprogs++; pr_globals = PR_globals(menu_world.progs, PR_CURRENT); G_FLOAT(OFS_PARM0) = CSIE_JOYAXIS; G_FLOAT(OFS_PARM1) = axis; G_FLOAT(OFS_PARM2) = value; G_FLOAT(OFS_PARM3) = devid; PR_ExecuteProgram (menu_world.progs, mpfuncs.inputevent); if (R2D_Flush) R2D_Flush(); inmenuprogs--; return G_FLOAT(OFS_RETURN); } static qboolean MP_KeyEvent(menu_t *menu, qboolean isdown, unsigned int devid, int key, int unicode) { qboolean result; #ifdef TEXTEDITOR if (editormodal) return false; #endif if (setjmp(mp_abort)) return true; if (isdown) { #ifndef NOBUILTINMENUS #ifdef _DEBUG if (key == 'c') { extern qboolean keydown[K_MAX]; if ((keydown[K_LCTRL] || keydown[K_RCTRL]) && (keydown[K_LSHIFT] || keydown[K_RSHIFT])) { MP_Shutdown(); M_Init_Internal(); return true; } } #endif #endif mpkeysdown[key>>3] |= (1<<(key&7)); } else { //don't fire up events if it was not actually pressed. if (key && !(mpkeysdown[key>>3] & (1<<(key&7)))) return false; mpkeysdown[key>>3] &= ~(1<<(key&7)); } menutime = Sys_DoubleTime(); if (menu_world.g.time) *menu_world.g.time = menutime; inmenuprogs++; if (mpfuncs.inputevent) { void *pr_globals = PR_globals(menu_world.progs, PR_CURRENT); G_FLOAT(OFS_PARM0) = isdown?CSIE_KEYDOWN:CSIE_KEYUP; G_FLOAT(OFS_PARM1) = MP_TranslateFTEtoQCCodes(key); G_FLOAT(OFS_PARM2) = unicode; G_FLOAT(OFS_PARM3) = devid; if (isdown) { qcinput_scan = G_FLOAT(OFS_PARM1); qcinput_unicode = G_FLOAT(OFS_PARM2); } PR_ExecuteProgram(menu_world.progs, mpfuncs.inputevent); result = G_FLOAT(OFS_RETURN); qcinput_scan = 0; qcinput_unicode = 0; } else if (isdown && mpfuncs.keydown) { void *pr_globals = PR_globals(menu_world.progs, PR_CURRENT); G_FLOAT(OFS_PARM0) = MP_TranslateFTEtoQCCodes(key); G_FLOAT(OFS_PARM1) = unicode; PR_ExecuteProgram(menu_world.progs, mpfuncs.keydown); result = true; //doesn't have a return value, so if the menu is set up for key events, all events are considered eaten. } else if (!isdown && mpfuncs.keyup) { void *pr_globals = PR_globals(menu_world.progs, PR_CURRENT); G_FLOAT(OFS_PARM0) = MP_TranslateFTEtoQCCodes(key); G_FLOAT(OFS_PARM1) = unicode; PR_ExecuteProgram(menu_world.progs, mpfuncs.keyup); result = false; // doesn't have a return value, so don't block it } else result = false; inmenuprogs--; if (R2D_Flush) //shouldn't be needed, but in case the mod is buggy. R2D_Flush(); return result; } static void MP_TryRelease(menu_t *m) { if (inmenuprogs) return; //if the qc asked for it, the qc probably already knows about it. don't recurse. MP_Toggle(0); } void MP_Shutdown (void) { func_t temp; if (!menu_world.progs) return; menuqc.release = NULL; //don't notify Menu_Unlink(&menuqc); /* { char *buffer; int size = 1024*1024*8; buffer = Z_Malloc(size); menuprogs->save_ents(menuprogs, buffer, &size, 1); COM_WriteFile("menucore.txt", buffer, size); Z_Free(buffer); } */ temp = mpfuncs.shutdown; mpfuncs.shutdown = 0; if (temp && !inmenuprogs) PR_ExecuteProgram(menu_world.progs, temp); PR_Common_Shutdown(menu_world.progs, false); menu_world.progs->Shutdown(menu_world.progs); memset(&menu_world, 0, sizeof(menu_world)); PR_ReleaseFonts(kdm_menu); #ifdef CL_MASTER Master_ClearMasks(); #endif Cmd_RemoveCommands(MP_ConsoleCommand_f); Key_Dest_Remove(kdm_menu); key_dest_absolutemouse &= ~kdm_menu; } void *VARGS PR_CB_Malloc(int size); //these functions should be tracked by the library reliably, so there should be no need to track them ourselves. void VARGS PR_CB_Free(void *mem); //Any menu builtin error or anything like that will come here. void VARGS Menu_Abort (char *format, ...) { va_list argptr; char string[1024]; va_start (argptr, format); vsnprintf (string,sizeof(string)-1, format,argptr); va_end (argptr); Con_Printf("Menu_Abort: %s\nShutting down menu.dat\n", string); if (pr_menu_coreonerror.value) { char *buffer; size_t size = 1024*1024*8; buffer = Z_Malloc(size); menu_world.progs->save_ents(menu_world.progs, buffer, &size, size, 3); COM_WriteFile("menucore.txt", FS_GAMEONLY, buffer, size); Z_Free(buffer); } MP_Shutdown(); M_Init_Internal(); if (inmenuprogs) //something in the menu caused the problem, so... { inmenuprogs = 0; longjmp(mp_abort, 1); } } void MP_CvarChanged(cvar_t *var) { if (menu_world.progs) { PR_AutoCvar(menu_world.progs, var); } } pbool PDECL Menu_CheckHeaderCrc(pubprogfuncs_t *inst, progsnum_t idx, int crc, const char *filename) { if (crc == 10020) return true; //its okay Con_Printf("progs crc is invalid for %s\n", filename); if (crc == 12776) { //whoever wrote wrath fucked up. Con_Printf("(please correct .src include orders)\n"); return true; } return false; } static void *PDECL MP_PRReadFile (const char *path, qbyte *(PDECL *buf_get)(void *buf_ctx, size_t size), void *buf_ctx, size_t *size, pbool issource) { flocation_t loc; if (FS_FLocateFile(path, FSLF_IFFOUND|FSLF_SECUREONLY, &loc)) { qbyte *buffer = NULL; vfsfile_t *file = FS_OpenReadLocation(path, &loc); if (file) { *size = loc.len; buffer = buf_get(buf_ctx, *size); if (buffer) VFS_READ(file, buffer, *size); VFS_CLOSE(file); } return buffer; } else { if (FS_FLocateFile(path, FSLF_IFFOUND, &loc)) Con_Printf("Not loading %s because it comes from an untrusted source\n", path); return NULL; } } static int PDECL MP_PRFileSize (const char *path) { flocation_t loc; if (FS_FLocateFile(path, FSLF_IFFOUND|FSLF_SECUREONLY, &loc)) return loc.len; else return -1; } qboolean MP_Init (void) { struct key_cursor_s *m = &key_customcursor[kc_menuqc]; if (qrenderer == QR_NONE) { return false; } if (forceqmenu.value) { Con_DPrintf("menu.dat disabled\n"); return false; } MP_SetupBuiltins(); memset(&menuc_eval, 0, sizeof(menuc_eval)); menuprogparms.progsversion = PROGSTRUCT_VERSION; menuprogparms.ReadFile = MP_PRReadFile;//char *(*ReadFile) (char *fname, void *buffer, int *len); menuprogparms.FileSize = MP_PRFileSize;//int (*FileSize) (char *fname); //-1 if file does not exist menuprogparms.WriteFile = QC_WriteFile;//bool (*WriteFile) (char *name, void *data, int len); menuprogparms.Printf = PR_Printf;//Con_Printf;//void (*printf) (char *, ...); menuprogparms.DPrintf = PR_DPrintf;//Con_DPrintf;//void (*dprintf) (char *, ...); menuprogparms.Sys_Error = Sys_Error; menuprogparms.Abort = Menu_Abort; menuprogparms.CheckHeaderCrc = Menu_CheckHeaderCrc; menuprogparms.edictsize = sizeof(menuedict_t); menuprogparms.entspawn = NULL;//void (*entspawn) (struct edict_s *ent); //ent has been spawned, but may not have all the extra variables (that may need to be set) set menuprogparms.entcanfree = NULL;//bool (*entcanfree) (struct edict_s *ent); //return true to stop ent from being freed menuprogparms.stateop = NULL;//StateOp;//void (*stateop) (float var, func_t func); menuprogparms.cstateop = NULL;//CStateOp; menuprogparms.cwstateop = NULL;//CWStateOp; menuprogparms.thinktimeop = NULL;//ThinkTimeOp; menuprogparms.MapNamedBuiltin = PR_Menu_MapNamedBuiltin; menuprogparms.loadcompleate = NULL;//void (*loadcompleate) (int edictsize); //notification to reset any pointers. menuprogparms.memalloc = PR_CB_Malloc;//void *(*memalloc) (int size); //small string allocation malloced and freed randomly menuprogparms.memfree = PR_CB_Free;//void (*memfree) (void * mem); menuprogparms.globalbuiltins = menu_builtins;//builtin_t *globalbuiltins; //these are available to all progs menuprogparms.numglobalbuiltins = sizeof(menu_builtins) / sizeof(menu_builtins[0]); menuprogparms.autocompile = PR_COMPILEIGNORE;//PR_COMPILEEXISTANDCHANGED;//enum {PR_NOCOMPILE, PR_COMPILENEXIST, PR_COMPILECHANGED, PR_COMPILEALWAYS} autocompile; menuprogparms.gametime = &menutime; #ifdef MULTITHREAD menuprogparms.usethreadedgc = pr_gc_threaded.ival; #endif menuprogparms.edicts = (struct edict_s **)&menu_edicts; menuprogparms.num_edicts = &num_menu_edicts; menuprogparms.useeditor = QCEditor;//void (*useeditor) (char *filename, int line, int nump, char **parms); menuprogparms.user = &menu_world; menu_world.keydestmask = kdm_menu; //default to free mouse+hidden cursor, to match dp's default setting, and because its generally the right thing for a menu. Q_strncpyz(m->name, "none", sizeof(m->name)); m->hotspot[0] = 0; m->hotspot[1] = 0; m->scale = 1; m->dirty = true; menuqc.cursor = &key_customcursor[kc_menuqc]; menuqc.drawmenu = NULL; //menuqc sucks! menuqc.mousemove = MP_MouseMove; menuqc.keyevent = MP_KeyEvent; menuqc.joyaxis = MP_JoystickAxis; menuqc.release = MP_TryRelease; menutime = Sys_DoubleTime(); if (!menu_world.progs) { int mprogs; Con_DPrintf("Initializing menu.dat\n"); menu_world.progs = InitProgs(&menuprogparms); PR_Configure(menu_world.progs, PR_ReadBytesString(pr_menu_memsize.string), 1, pr_enable_profiling.ival); mprogs = PR_LoadProgs(menu_world.progs, "menu.dat"); if (mprogs < 0) //no per-progs builtins. { //failed to load or something // CloseProgs(menu_world.progs); // menuprogs = NULL; return false; } if (setjmp(mp_abort)) { Con_DPrintf("Failed to initialize menu.dat\n"); inmenuprogs = false; return false; } inmenuprogs++; M_DeInit_Internal(); PF_InitTempStrings(menu_world.progs); menu_world.g.self = (int*)PR_FindGlobal(menu_world.progs, "self", 0, NULL); menu_world.g.time = (float*)PR_FindGlobal(menu_world.progs, "time", 0, NULL); if (menu_world.g.time) *menu_world.g.time = Sys_DoubleTime(); menu_world.g.frametime = (float*)PR_FindGlobal(menu_world.progs, "frametime", 0, NULL); menu_world.g.drawfont = (float*)PR_FindGlobal(menu_world.progs, "drawfont", 0, NULL); menu_world.g.drawfontscale = (float*)PR_FindGlobal(menu_world.progs, "drawfontscale", 0, NULL); PR_ProgsAdded(menu_world.progs, mprogs, "menu.dat"); //ensure that there's space for these fields in. //other fields will always be referenced/defined by the qc, or 0. PR_RegisterFieldVar(menu_world.progs, ev_string, "model", -1, -1); PR_RegisterFieldVar(menu_world.progs, ev_vector, "origin", -1, -1); PR_RegisterFieldVar(menu_world.progs, ev_float, "skinobject", -1, -1); menuentsize = PR_InitEnts(menu_world.progs, 8192); //'world' edict // EDICT_NUM_PB(menu_world.progs, 0)->readonly = true; EDICT_NUM_PB(menu_world.progs, 0)->ereftype = ER_ENTITY; mpfuncs.init = PR_FindFunction(menu_world.progs, "m_init", PR_ANY); mpfuncs.shutdown = PR_FindFunction(menu_world.progs, "m_shutdown", PR_ANY); { int args = 0; qbyte *argsizes = NULL; mpfuncs.draw = PR_FindFunction(menu_world.progs, "m_draw", PR_ANY); menu_world.progs->GetFunctionInfo(menu_world.progs, mpfuncs.draw, &args, &argsizes, NULL, NULL, 0); mpfuncs.fuckeddrawsizes = (args == 2 && argsizes[0] == 1 && argsizes[1] == 1); } mpfuncs.drawloading = PR_FindFunction(menu_world.progs, "m_drawloading", PR_ANY); mpfuncs.inputevent = PR_FindFunction(menu_world.progs, "Menu_InputEvent", PR_ANY); mpfuncs.keydown = PR_FindFunction(menu_world.progs, "m_keydown", PR_ANY); mpfuncs.keyup = PR_FindFunction(menu_world.progs, "m_keyup", PR_ANY); mpfuncs.toggle = PR_FindFunction(menu_world.progs, "m_toggle", PR_ANY); mpfuncs.consolecommand = PR_FindFunction(menu_world.progs, "m_consolecommand", PR_ANY); mpfuncs.gethostcachecategory = PR_FindFunction(menu_world.progs, "m_gethostcachecategory", PR_ANY); mpfuncs.rendererrestarted = PR_FindFunction(menu_world.progs, "Menu_RendererRestarted", PR_ANY); if (mpfuncs.init) PR_ExecuteProgram(menu_world.progs, mpfuncs.init); inmenuprogs--; EDICT_NUM_PB(menu_world.progs, 0)->readonly = true; Con_DPrintf("Initialized menu.dat\n"); return true; } return false; } static void MP_GameCommand_f(void) { void *pr_globals; func_t gamecommand; if (!menu_world.progs) return; gamecommand = PR_FindFunction(menu_world.progs, "GameCommand", PR_ANY); if (!gamecommand) return; if (setjmp(mp_abort)) return; inmenuprogs++; pr_globals = PR_globals(menu_world.progs, PR_CURRENT); (((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(menu_world.progs, Cmd_Args())); PR_ExecuteProgram (menu_world.progs, gamecommand); inmenuprogs--; } qboolean MP_ConsoleCommand(const char *cmdtext) { void *pr_globals; if (!menu_world.progs) return false; if (!mpfuncs.consolecommand) return false; if (setjmp(mp_abort)) return true; inmenuprogs++; pr_globals = PR_globals(menu_world.progs, PR_CURRENT); (((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(menu_world.progs, cmdtext)); PR_ExecuteProgram (menu_world.progs, mpfuncs.consolecommand); inmenuprogs--; return G_FLOAT(OFS_RETURN); } void MP_CoreDump_f(void) { if (!menu_world.progs) { Con_Printf("Can't core dump, you need to be running the CSQC progs first."); return; } { size_t size = 1024*1024*8; char *buffer = BZ_Malloc(size); menu_world.progs->save_ents(menu_world.progs, buffer, &size, size, 3); COM_WriteFile("menucore.txt", FS_GAMEONLY, buffer, size); BZ_Free(buffer); } } static void MP_Poke_f(void) { /*if (!SV_MayCheat()) Con_TPrintf ("Please set sv_cheats 1 and restart the map first.\n"); else */if (menu_world.progs && menu_world.progs->EvaluateDebugString) Con_TPrintf("Result: %s\n", menu_world.progs->EvaluateDebugString(menu_world.progs, Cmd_Args())); else Con_TPrintf ("not supported.\n"); } void MP_Breakpoint_f(void) { int wasset; int isset; char *filename = Cmd_Argv(1); int line = atoi(Cmd_Argv(2)); if (!menu_world.progs) { Con_Printf("Menu not running\n"); return; } wasset = menu_world.progs->ToggleBreak(menu_world.progs, filename, line, 3); isset = menu_world.progs->ToggleBreak(menu_world.progs, filename, line, 2); if (wasset == isset) Con_Printf("Breakpoint was not valid\n"); else if (isset) Con_Printf("Breakpoint has been set\n"); else Con_Printf("Breakpoint has been cleared\n"); Cvar_Set(Cvar_FindVar("pr_debugger"), "1"); } void MP_RegisterCvarsAndCmds(void) { Cmd_AddCommand("coredump_menuqc", MP_CoreDump_f); Cmd_AddCommand("menu_cmd", MP_GameCommand_f); Cmd_AddCommand("breakpoint_menu", MP_Breakpoint_f); #ifdef HAVE_LEGACY Cmd_AddCommand("loadfont", CL_LoadFont_f); #endif Cmd_AddCommand("poke_menuqc", MP_Poke_f); Cvar_Register(&forceqmenu, MENUPROGSGROUP); Cvar_Register(&pr_menu_coreonerror, MENUPROGSGROUP); Cvar_Register(&pr_menu_memsize, MENUPROGSGROUP); } qboolean MP_UsingGamecodeLoadingScreen(void) { return menu_world.progs && mpfuncs.drawloading; } int MP_GetServerCategory(int index) { int category = 0; if (menu_world.progs && mpfuncs.gethostcachecategory) { void *pr_globals = PR_globals(menu_world.progs, PR_CURRENT); if (!setjmp(mp_abort)) { inmenuprogs++; G_FLOAT(OFS_PARM0) = index; PR_ExecuteProgram(menu_world.progs, mpfuncs.gethostcachecategory); category = G_FLOAT(OFS_RETURN); inmenuprogs--; } } return category; } void MP_RendererRestarted(void) { int i; if (!menu_world.progs) return; menu_world.worldmodel = cl.worldmodel; for (i = 0; i < MAX_CSMODELS; i++) { cl.model_csqcprecache[i] = NULL; } //FIXME: registered shaders //let the csqc know that its rendertargets got purged if (mpfuncs.rendererrestarted) { void *pr_globals = PR_globals(menu_world.progs, PR_CURRENT); (((string_t *)pr_globals)[OFS_PARM0] = PR_TempString(menu_world.progs, rf->description)); PR_ExecuteProgram(menu_world.progs, mpfuncs.rendererrestarted); } //in case it drew to any render targets. if (R2D_Flush) R2D_Flush(); if (*r_refdef.rt_destcolour[0].texname) { Q_strncpyz(r_refdef.rt_destcolour[0].texname, "", sizeof(r_refdef.rt_destcolour[0].texname)); BE_RenderToTextureUpdate2d(true); } } void MP_Draw(void) { extern qboolean scr_drawloading; globalvars_t *pr_globals; if (!menu_world.progs) return; if (setjmp(mp_abort)) return; menutime = Sys_DoubleTime(); if (menu_world.g.time) *menu_world.g.time = menutime; if (menu_world.g.frametime) *menu_world.g.frametime = host_frametime; inmenuprogs++; pr_globals = PR_globals(menu_world.progs, PR_CURRENT); if (scr_drawloading||scr_disabled_for_loading) { //don't draw the menu if we're meant to be drawing a loading screen //the menu should provide a special function if it wants to draw custom loading screens. this is for compat with old/dp/lazy/crappy menus. if (mpfuncs.drawloading) { ((float *)pr_globals)[OFS_PARM0+0] = vid.width; ((float *)pr_globals)[OFS_PARM0+1] = vid.height; ((float *)pr_globals)[OFS_PARM0+2] = 0; ((float *)pr_globals)[OFS_PARM1] = scr_disabled_for_loading; PR_ExecuteProgram(menu_world.progs, mpfuncs.drawloading); } } else if (mpfuncs.draw) { if (mpfuncs.fuckeddrawsizes) { //pass useless sizes in two args if its a dp menu ((float *)pr_globals)[OFS_PARM0] = vid.pixelwidth; ((float *)pr_globals)[OFS_PARM0+1] = 0; //make sure its set, just in case... ((float *)pr_globals)[OFS_PARM0+2] = 0; ((float *)pr_globals)[OFS_PARM1] = vid.pixelheight; ((float *)pr_globals)[OFS_PARM1+1] = 0; ((float *)pr_globals)[OFS_PARM1+2] = 0; } else { //pass useful sizes in a 1-arg vector if its an fte menu. ((float *)pr_globals)[OFS_PARM0+0] = vid.width; ((float *)pr_globals)[OFS_PARM0+1] = vid.height; ((float *)pr_globals)[OFS_PARM0+2] = 0; //make physical pixel counts available too, because we can. ((float *)pr_globals)[OFS_PARM1+0] = vid.pixelwidth; ((float *)pr_globals)[OFS_PARM1+1] = vid.pixelheight; ((float *)pr_globals)[OFS_PARM1+2] = 0; } PR_ExecuteProgram(menu_world.progs, mpfuncs.draw); } inmenuprogs--; } qboolean MP_Toggle(int mode) { if (!menu_world.progs) return false; #ifdef TEXTEDITOR if (editormodal) return false; #endif if (!mode && !Key_Dest_Has(kdm_menu)) return false; if (setjmp(mp_abort)) return false; menutime = Sys_DoubleTime(); if (menu_world.g.time) *menu_world.g.time = menutime; inmenuprogs++; if (mpfuncs.toggle) { void *pr_globals = PR_globals(menu_world.progs, PR_CURRENT); G_FLOAT(OFS_PARM0) = mode; PR_ExecuteProgram(menu_world.progs, mpfuncs.toggle); } if (R2D_Flush) R2D_Flush(); inmenuprogs--; return true; } #endif