fteqw/engine/client/m_native.c
Spoike d1d0d86fea Rewrote infostrings. Now using infobuffers, which allows for the use of arbitrary blobs, except not using the protocol extension yet in case it needs to be fixed.
Fix sound source issues in Q3.
Fix q2 air acceleration/prediction omission.
Don't change console completion while typing (while that option is still possible).
Shift+tab now cycles completion backwards (now ctrl+shift for cycle subconsoles).
Allow a few things to ignore sv_pure - including csprogs files (which is useful for all the mods that come with the csprogs.dat distributed separately).
clamp pitch values to the range documented by openal, to hopefully avoid error spam.
add some colour coding to the text editor when shader files are being edited/viewed.
Changed how overbrights are clamped on q3bsp.
Added portalfboscale for explicit texture scales on portal/refract/reflect fbos.
qc decompiler can now at least attempt to decompile qtest's qc.
fteqccgui can now be pointed at a .pak file, and decompile the progs.dat inside.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5269 fc73d0e0-1445-4013-8a0c-d673dee63da5
2018-07-05 16:21:44 +00:00

436 lines
No EOL
12 KiB
C

#include "quakedef.h"
#ifdef MENU_NATIVECODE
static dllhandle_t *libmenu;
menu_export_t *mn_entry;
extern unsigned int r2d_be_flags;
#include "pr_common.h"
#include "shader.h"
#include "cl_master.h"
static int MN_CheckExtension(const char *extname)
{
unsigned int i;
for (i = 0; i < QSG_Extensions_count; i++)
{
if (!strcmp(QSG_Extensions[i].name, extname))
return true;
}
return false;
}
static void MN_LocalCmd(const char *text)
{
Cbuf_AddText(text, RESTRICT_LOCAL); //menus are implicitly trusted. latching and other stuff would be a nightmare otherwise.
}
static const char *MN_Cvar_String(const char *cvarname, qboolean effective)
{
cvar_t *cv = Cvar_FindVar(cvarname);
if (cv)
{ //some cvars don't change instantly, giving them (temporary) effective values that are different from their intended values.
if (cv->latched_string && !effective)
return cv->latched_string;
return cv->string;
}
else
return NULL;
}
static const char *MN_Cvar_GetDefault(const char *cvarname)
{
cvar_t *cv = Cvar_FindVar(cvarname);
if (cv)
return cv->defaultstr?cv->defaultstr:"";
else
return NULL;
}
static void MN_RegisterCvar(const char *cvarname, const char *defaulttext, unsigned int flags, const char *description)
{
Cvar_Get2(cvarname, defaulttext, flags, description, NULL);
}
void Cmd_DeleteAlias(const char *name);
static void MN_RegisterCommand(const char *commandname, const char *description)
{
Cmd_DeleteAlias(commandname); //menuqc has no real way to register commands, so it has a nasty habit of creating loads of weird awkward aliases.
Cmd_AddCommandD(commandname, NULL, description);
}
static int MN_GetServerState(void)
{
if (!sv.active)
return 0;
if (svs.allocated_client_slots <= 1)
return 1;
return 2;
}
static int MN_GetClientState(void)
{
if (cls.state >= ca_active)
return 2;
if (cls.state != ca_disconnected)
return 1;
return 0;
}
static void MN_fclose(vfsfile_t *f)
{
VFS_CLOSE(f);
}
static shader_t *MN_CachePic(const char *picname)
{
return R2D_SafeCachePic(picname);
}
static qboolean MN_DrawGetImageSize(struct shader_s *pic, int *w, int *h)
{
return R_GetShaderSizes(pic, w, h, true)>0;
}
static void MN_DrawQuad(const vec2_t position[4], const vec2_t texcoords[4], shader_t *pic, const vec4_t rgba, unsigned int be_flags)
{
extern shader_t *shader_draw_fill, *shader_draw_fill_trans;
r2d_be_flags = be_flags;
if (!pic)
pic = rgba[3]==1?shader_draw_fill:shader_draw_fill_trans;
R2D_ImageColours(rgba[0], rgba[1], rgba[2], rgba[3]);
R2D_Image2dQuad(position, texcoords, pic);
r2d_be_flags = 0;
}
static float MN_DrawString(const vec2_t position, const char *text, struct font_s *font, float height, const vec4_t rgba, unsigned int be_flags)
{
float px, py, ix;
unsigned int codeflags, codepoint;
conchar_t buffer[2048], *str = buffer;
if (!font)
font = font_default;
COM_ParseFunString(CON_WHITEMASK, text, buffer, sizeof(buffer), false);
R2D_ImageColours(rgba[0], rgba[1], rgba[2], rgba[3]);
Font_BeginScaledString(font, position[0], position[1], height, height, &px, &py);
ix=px;
while(*str)
{
str = Font_Decode(str, &codeflags, &codepoint);
px = Font_DrawScaleChar(px, py, codeflags, codepoint);
}
Font_EndString(font);
return ((px-ix)*(float)vid.width)/(float)vid.rotpixelwidth;
}
static float MN_StringWidth(const char *text, struct font_s *font, float height)
{
float px, py;
conchar_t buffer[2048], *end;
if (!font)
font = font_default;
end = COM_ParseFunString(CON_WHITEMASK, text, buffer, sizeof(buffer), false);
Font_BeginScaledString(font, 0, 0, height, height, &px, &py);
px = Font_LineScaleWidth(buffer, end);
Font_EndString(font);
return (px * (float)vid.width) / (float)vid.rotpixelwidth;
}
static void MN_DrawSetClipArea(float x, float y, float width, float height)
{
srect_t srect;
if (R2D_Flush)
R2D_Flush();
srect.x = x / (float)vid.fbvwidth;
srect.y = y / (float)vid.fbvheight;
srect.width = width / (float)vid.fbvwidth;
srect.height = height / (float)vid.fbvheight;
srect.dmin = -99999;
srect.dmax = 99999;
srect.y = (1-srect.y) - srect.height;
BE_Scissor(&srect);
}
static void MN_DrawResetClipArea(void)
{
if (R2D_Flush)
R2D_Flush();
BE_Scissor(NULL);
}
static qboolean MN_SetKeyDest(qboolean focused)
{
qboolean ret = Key_Dest_Has(kdm_nmenu);
if (ret == focused)
return false;
if (focused)
{
if (key_dest_absolutemouse & kdm_nmenu)
{ //we're activating the mouse cursor now... make sure the position is actually current.
//FIXME: we should probably get the input code to do this for us when switching cursor modes.
struct menu_inputevent_args_s ev = {MIE_MOUSEABS, -1};
ev.mouse.screen[0] = (mousecursor_x * vid.width) / vid.pixelwidth;
ev.mouse.screen[1] = (mousecursor_y * vid.height) / vid.pixelheight;
mn_entry->InputEvent(ev);
}
Key_Dest_Add(kdm_nmenu);
}
else
Key_Dest_Remove(kdm_nmenu);
return true;
}
static int MN_GetKeyDest(void)
{
if (Key_Dest_Has(kdm_nmenu))
{
if (Key_Dest_Has_Higher(kdm_nmenu))
return -1;
return 1;
}
return 0;
}
static int MN_SetMouseTarget(const char *cursorname, float hot_x, float hot_y, float scale)
{
if (cursorname)
{
struct key_cursor_s *m = &key_customcursor[kc_nmenu];
if (scale <= 0)
scale = 1;
if (!strcmp(m->name, cursorname) || m->hotspot[0] != hot_x || m->hotspot[1] != hot_y || m->scale != scale)
{
Q_strncpyz(m->name, cursorname, sizeof(m->name));
m->hotspot[0] = hot_x;
m->hotspot[1] = hot_y;
m->scale = scale;
m->dirty = true;
}
key_dest_absolutemouse |= kdm_nmenu;
}
else
key_dest_absolutemouse &= ~kdm_nmenu;
return true;
}
static model_t *MN_CacheModel(const char *name)
{
return Mod_ForName(name, MLV_SILENT);
}
static qboolean MN_GetModelSize(model_t *model, vec3_t out_mins, vec3_t out_maxs)
{
if (model)
{
while(model->loadstate == MLS_LOADING)
COM_WorkerPartialSync(model, &model->loadstate, MLS_LOADING);
VectorCopy(model->mins, out_mins);
VectorCopy(model->maxs, out_maxs);
return model->loadstate == MLS_LOADED;
}
VectorClear(out_mins);
VectorClear(out_maxs);
return false;
}
static void MN_RenderScene(menuscene_t *scene)
{
int i;
entity_t ent;
menuentity_t *e;
if (R2D_Flush)
R2D_Flush();
CL_ClearEntityLists();
memset(&ent, 0, sizeof(ent));
for (i = 0; i < scene->numentities; i++)
{
e = &scene->entlist[i];
ent.keynum = i;
ent.model = scene->entlist[i].model;
VectorCopy(e->matrix[0], ent.axis[0]); ent.origin[0] = e->matrix[0][3];
VectorCopy(e->matrix[1], ent.axis[1]); ent.origin[1] = e->matrix[1][3];
VectorCopy(e->matrix[2], ent.axis[2]); ent.origin[2] = e->matrix[2][3];
ent.scale = 1;
ent.framestate.g[FS_REG].frame[0] = e->frame[0];
ent.framestate.g[FS_REG].frame[1] = e->frame[1];
ent.framestate.g[FS_REG].lerpweight[1] = e->frameweight[0];
ent.framestate.g[FS_REG].lerpweight[0] = e->frameweight[1];
ent.framestate.g[FS_REG].frametime[0] = e->frametime[0];
ent.framestate.g[FS_REG].frametime[1] = e->frametime[1];
ent.playerindex = -1;
ent.topcolour = TOP_DEFAULT;
ent.bottomcolour = BOTTOM_DEFAULT;
Vector4Set(ent.shaderRGBAf, 1, 1, 1, 1);
VectorSet(ent.glowmod, 1, 1, 1);
#ifdef HEXEN2
ent.drawflags = SCALE_ORIGIN_ORIGIN;
ent.abslight = 0;
#endif
ent.skinnum = 0;
ent.fatness = 0;
ent.forcedshader = NULL;
ent.customskin = 0;
V_AddAxisEntity(&ent);
}
VectorCopy(scene->viewmatrix[0], r_refdef.viewaxis[0]); r_refdef.vieworg[0] = scene->viewmatrix[0][3];
VectorCopy(scene->viewmatrix[1], r_refdef.viewaxis[1]); r_refdef.vieworg[1] = scene->viewmatrix[1][3];
VectorCopy(scene->viewmatrix[2], r_refdef.viewaxis[2]); r_refdef.vieworg[2] = scene->viewmatrix[2][3];
r_refdef.viewangles[0] = -(atan2(r_refdef.viewaxis[0][2], sqrt(r_refdef.viewaxis[0][1]*r_refdef.viewaxis[0][1]+r_refdef.viewaxis[0][0]*r_refdef.viewaxis[0][0])) * 180 / M_PI);
r_refdef.viewangles[1] = (atan2(r_refdef.viewaxis[0][1], r_refdef.viewaxis[0][0]) * 180 / M_PI);
r_refdef.viewangles[2] = 0;
r_refdef.flags = 0;
if (scene->worldmodel && scene->worldmodel == cl.worldmodel)
r_refdef.flags &= ~RDF_NOWORLDMODEL;
else
r_refdef.flags |= RDF_NOWORLDMODEL;
r_refdef.fovv_x = r_refdef.fov_x = scene->fov[0];
r_refdef.fovv_y = r_refdef.fov_y = scene->fov[1];
r_refdef.vrect.x = scene->pos[0];
r_refdef.vrect.y = scene->pos[1];
r_refdef.vrect.width = scene->size[0];
r_refdef.vrect.height = scene->size[1];
r_refdef.time = scene->time;
r_refdef.useperspective = true;
r_refdef.mindist = bound(0.1, gl_mindist.value, 4);
r_refdef.maxdist = gl_maxdist.value;
r_refdef.playerview = &cl.playerview[0];
memset(&r_refdef.globalfog, 0, sizeof(r_refdef.globalfog));
r_refdef.areabitsknown = false;
R_RenderView();
r_refdef.playerview = NULL;
r_refdef.time = 0;
}
void MN_Shutdown(void)
{
Key_Dest_Remove(kdm_nmenu);
if (mn_entry)
{
mn_entry->Shutdown(MI_INIT);
mn_entry = NULL;
}
if (libmenu)
{
Sys_CloseLibrary(libmenu);
libmenu = NULL;
}
}
qboolean MN_Init(void)
{
menu_export_t *(QDECL *pGetMenuAPI) ( menu_import_t *import );
static menu_import_t imports =
{
NATIVEMENU_API_VERSION_MAX,
NULL,
MN_CheckExtension,
Host_Error,
Con_Printf,
Con_DPrintf,
MN_LocalCmd,
Cvar_VariableValue,
MN_Cvar_String,
MN_Cvar_GetDefault,
Cvar_SetNamed,
MN_RegisterCvar,
MN_RegisterCommand,
COM_ParseType,
MN_GetServerState,
MN_GetClientState,
S_LocalSound2,
// file input / search crap
FS_OpenVFS,
MN_fclose,
VFS_GETS,
VFS_PRINTF,
COM_EnumerateFiles,
// Drawing stuff
MN_DrawSetClipArea,
MN_DrawResetClipArea,
//pics
MN_CachePic,
MN_DrawGetImageSize,
MN_DrawQuad,
//strings
MN_DrawString,
MN_StringWidth,
Font_LoadFont,
Font_Free,
//3d stuff
MN_CacheModel,
MN_GetModelSize,
MN_RenderScene,
// Menu specific stuff
MN_SetKeyDest,
MN_GetKeyDest,
MN_SetMouseTarget,
Key_KeynumToString,
Key_StringToKeynum,
M_FindKeysForBind,
// Server browser stuff
Master_KeyForName,
Master_SortedServer,
Master_ReadKeyString,
Master_ReadKeyFloat,
Master_ClearMasks,
Master_SetMaskString,
Master_SetMaskInteger,
Master_SetSortField,
Master_SortServers,
MasterInfo_Refresh,
CL_QueryServers,
};
dllfunction_t funcs[] =
{
{(void*)&pGetMenuAPI, "GetMenuAPI"},
{NULL}
};
void *iterator = NULL;
char syspath[MAX_OSPATH];
char gamepath[MAX_QPATH];
while(COM_IteratePaths(&iterator, syspath, sizeof(syspath), gamepath, sizeof(gamepath)))
{
if (!com_nogamedirnativecode.ival)
libmenu = Sys_LoadLibrary(va("%smenu_"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, syspath), funcs);
if (libmenu)
break;
if (host_parms.binarydir && !strchr(gamepath, '/') && !strchr(gamepath, '\\'))
libmenu = Sys_LoadLibrary(va("%smenu_%s_"ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, host_parms.binarydir, gamepath), funcs);
if (libmenu)
break;
//some build systems don't really know the cpu type.
if (host_parms.binarydir && !strchr(gamepath, '/') && !strchr(gamepath, '\\'))
libmenu = Sys_LoadLibrary(va("%smenu_%s" ARCH_DL_POSTFIX, host_parms.binarydir, gamepath), funcs);
if (libmenu)
break;
}
if (libmenu)
{
imports.engine_version = version_string();
key_dest_absolutemouse |= kdm_nmenu;
mn_entry = pGetMenuAPI (&imports);
if (mn_entry && mn_entry->api_version >= NATIVEMENU_API_VERSION_MIN && mn_entry->api_version <= NATIVEMENU_API_VERSION_MAX)
{
mn_entry->Init(0, vid.width, vid.height, vid.pixelwidth, vid.pixelheight);
return true;
}
else
mn_entry = NULL;
MN_Shutdown();
Sys_CloseLibrary(libmenu);
libmenu = NULL;
}
return false;
}
#endif