1
0
Fork 0
forked from fte/fteqw

Update prediction code to propagate some values properly with certain protocol combinations that I'd overlooked.

Try to fix problems caused by (auto)save's screenshots
Added code to allow falling back on stbi for when libpng/libjpeg are not compiled it, at eukara's request.
Handle .exr image files as suggested by eukara, when the appropriate library is available.
Fix mipmaps etc for half-float files.
Enable support for stbi's special gif loader, loading gifs as an 2darray texture.
Add code for threading the qcvm's tempstring recycling, disabled due to paranoia but does otherwise help xonotic perf (at the cost of extra ram).



git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5548 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2019-09-25 20:23:24 +00:00
parent 2498024a77
commit cfd20f4f06
49 changed files with 1656 additions and 606 deletions

View file

@ -452,7 +452,7 @@ void CL_PredictUsercmd (int pnum, int entnum, player_state_t *from, player_state
void CL_CatagorizePosition (playerview_t *pv, float *org)
{
//fixme: in nq, we are told by the server and should skip this, which avoids needing to know the player's size.
if (pv->spectator)
if (pv->spectator && !CAM_ISLOCKED(pv))
{
pv->onground = false; // in air
return;
@ -787,7 +787,7 @@ static void CL_EntStateToPlayerState(player_state_t *plstate, entity_state_t *st
memset(plstate, 0, sizeof(*plstate));
plstate->jump_held = jumpheld;
switch(state->u.q1.pmovetype & 0x7f)
switch(state->u.q1.pmovetype & 0x3f)
{
case MOVETYPE_NOCLIP:
if (cls.z_ext & Z_EXT_PM_TYPE_NEW)
@ -828,6 +828,7 @@ static void CL_EntStateToPlayerState(player_state_t *plstate, entity_state_t *st
{
VectorScale(state->u.q1.velocity, 1/8.0, plstate->velocity);
plstate->onground = !!(state->u.q1.pmovetype&128);
plstate->jump_held = !!(state->u.q1.pmovetype&64);
}
plstate->pm_type = pmtype;
@ -917,15 +918,18 @@ void CL_PredictMovePNum (int seat)
//interpolation state should be updated to match prediction state, so entities move correctly in mirrors/portals.
//this entire function is pure convolouted bollocks.
struct {
int frame;
double time;
player_state_t *state;
usercmd_t *cmd;
} from, to;
playerview_t *pv = &cl.playerview[seat];
int i;
float f;
int fromframe, toframe;
outframe_t *backdate;
player_state_t *fromstate, *tostate, framebuf[2]; //need two framebufs so we can interpolate between two states.
player_state_t framebuf[2]; //need two framebufs so we can interpolate between two states.
static player_state_t nullstate;
usercmd_t *cmdfrom = NULL, *cmdto = NULL;
double fromtime, totime;
int oldphysent;
double simtime; //this is server time if nopred is set (lerp-only), and local time if we're predicting
extern cvar_t cl_netfps;
@ -934,6 +938,7 @@ void CL_PredictMovePNum (int seat)
qboolean lerpangles = false;
int trackent;
qboolean cam_nowlocked = false;
usercmd_t indcmd;
//these are to make svc_viewentity work better
float netfps = cl_netfps.value;
@ -1076,17 +1081,18 @@ void CL_PredictMovePNum (int seat)
if (nopred)
{
//match interpolation info
fromframe = ((char*)cl.previouspackentities - (char*)&cl.inframes[0].packet_entities) / sizeof(inframe_t);
fromtime = cl.inframes[fromframe & UPDATE_MASK].packet_entities.servertime;
toframe = ((char*)cl.currentpackentities - (char*)&cl.inframes[0].packet_entities) / sizeof(inframe_t);
totime = cl.inframes[toframe & UPDATE_MASK].packet_entities.servertime;
from.frame = ((char*)cl.previouspackentities - (char*)&cl.inframes[0].packet_entities) / sizeof(inframe_t);
from.time = cl.inframes[from.frame & UPDATE_MASK].packet_entities.servertime;
to.frame = ((char*)cl.currentpackentities - (char*)&cl.inframes[0].packet_entities) / sizeof(inframe_t);
to.time = cl.inframes[to.frame & UPDATE_MASK].packet_entities.servertime;
simtime = cl.currentpacktime;
to.cmd = from.cmd = NULL;
}
else
{
fromframe = 0;
toframe = 0;
totime = fromtime = 0;
to.frame = from.frame = 0;
to.time = from.time = 0;
to.cmd = from.cmd = NULL;
//try to find the inbound frame that sandwiches the realtime that we're trying to simulate.
//if we're predicting, this will be some time in the future, and thus we'll be forced to pick the most recent frame.
@ -1116,18 +1122,20 @@ void CL_PredictMovePNum (int seat)
//okay, looks valid
//if this is the first one we found, make sure both from+to are set properly
if (!fromframe)
if (!from.frame)
{
fromframe = i;
fromtime = backdate->senttime;
from.frame = i;
from.time = backdate->senttime;
}
toframe = fromframe;
totime = fromtime;
cmdto = cmdfrom;
fromframe = i;
fromtime = backdate->senttime;
cmdfrom = &backdate->cmd[seat];
if (fromtime < simtime && fromframe != toframe)
to = from;
to.state = NULL;
from.frame = i;
from.time = backdate->senttime;
from.cmd = &backdate->cmd[seat];
if (to.frame > pv->prop.sequence)
continue; //if we didn't predict to this frame yet, then the waterjump etc state will be invalid, so try to go for an older frame so that it actually propagates properly.
if (from.time < simtime && from.frame != to.frame)
break; //okay, we found the first frame that is older, no need to continue looking
}
}
@ -1136,61 +1144,61 @@ void CL_PredictMovePNum (int seat)
if ((pv->cam_state == CAM_WALLCAM || pv->cam_state == CAM_EYECAM) && trackent && trackent <= cl.allocated_client_slots)
{
fromstate = &cl.inframes[fromframe & UPDATE_MASK].playerstate[trackent-1];
tostate = &cl.inframes[toframe & UPDATE_MASK].playerstate[trackent-1];
from.state = &cl.inframes[from.frame & UPDATE_MASK].playerstate[trackent-1];
to.state = &cl.inframes[to.frame & UPDATE_MASK].playerstate[trackent-1];
}
else
{
if (cls.demoplayback==DPB_MVD || cls.demoplayback == DPB_EZTV)
{
pv->nolocalplayer = false;
fromstate = &cl.inframes[cl.ackedmovesequence & UPDATE_MASK].playerstate[pv->playernum];
tostate = &cl.inframes[cl.movesequence & UPDATE_MASK].playerstate[pv->playernum];
from.state = &cl.inframes[cl.ackedmovesequence & UPDATE_MASK].playerstate[pv->playernum];
to.state = &cl.inframes[cl.movesequence & UPDATE_MASK].playerstate[pv->playernum];
}
else
{
fromstate = &cl.inframes[fromframe & UPDATE_MASK].playerstate[pv->playernum];
tostate = &cl.inframes[toframe & UPDATE_MASK].playerstate[pv->playernum];
from.state = &cl.inframes[from.frame & UPDATE_MASK].playerstate[pv->playernum];
to.state = &cl.inframes[to.frame & UPDATE_MASK].playerstate[pv->playernum];
}
}
pv->pmovetype = tostate->pm_type;
pv->pmovetype = to.state->pm_type;
le = &cl.lerpplayers[pv->playernum];
if (!cmdfrom)
cmdfrom = &cl.outframes[fromframe & UPDATE_MASK].cmd[pv->playernum];
if (!cmdto)
cmdto = &cl.outframes[toframe & UPDATE_MASK].cmd[pv->playernum];
if (!from.cmd)
from.cmd = &cl.outframes[from.frame & UPDATE_MASK].cmd[pv->playernum];
if (!to.cmd)
to.cmd = &cl.outframes[to.frame & UPDATE_MASK].cmd[pv->playernum];
//if our network protocol doesn't have a concept of separate players, make sure our player states are updated from those entities
//fixme: use entity states instead of player states to avoid the extra work here
if (pv->nolocalplayer)
{
packet_entities_t *pe;
pe = &cl.inframes[fromframe & UPDATE_MASK].packet_entities;
pe = &cl.inframes[from.frame & UPDATE_MASK].packet_entities;
for (i = 0; i < pe->num_entities; i++)
{
if (pe->entities[i].number == trackent)
{
CL_EntStateToPlayerState(fromstate, &pe->entities[i]);
CL_EntStateToPlayerState(from.state, &pe->entities[i]);
if (nopred)
fromtime -= (pe->entities[i].u.q1.msec / 1000.0f); //correct the time to match stale players
from.time -= (pe->entities[i].u.q1.msec / 1000.0f); //correct the time to match stale players
break;
}
}
if (i == pe->num_entities && pv->nolocalplayer)
{
fromstate = &nullstate;
from.state = &nullstate;
nopred = true;
}
pe = &cl.inframes[toframe & UPDATE_MASK].packet_entities;
pe = &cl.inframes[to.frame & UPDATE_MASK].packet_entities;
for (i = 0; i < pe->num_entities; i++)
{
if (pe->entities[i].number == trackent)
{
CL_EntStateToPlayerState(tostate, &pe->entities[i]);
CL_EntStateToPlayerState(to.state, &pe->entities[i]);
if (nopred)
totime -= (pe->entities[i].u.q1.msec / 1000.0f); //correct the time to match stale players. FIXME: this can push the simtime into the 'future' resulting in stuttering
to.time -= (pe->entities[i].u.q1.msec / 1000.0f); //correct the time to match stale players. FIXME: this can push the simtime into the 'future' resulting in stuttering
if (cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS)
{
#ifdef QUAKESTATS
@ -1201,14 +1209,14 @@ void CL_PredictMovePNum (int seat)
pv->statsf[STAT_WEAPONFRAME] = cl.players[pv->playernum].statsf[STAT_WEAPONFRAME] = pe->entities[i].u.q1.weaponframe;
}
#endif
pv->pmovetype = tostate->pm_type;
pv->pmovetype = to.state->pm_type;
}
break;
}
}
if (i == pe->num_entities && pv->nolocalplayer)
{
tostate = &nullstate;
to.state = &nullstate;
nopred = true;
}
if (pv->nolocalplayer && trackent < cl.maxlerpents)
@ -1225,82 +1233,77 @@ void CL_PredictMovePNum (int seat)
pmove.skipent = trackent;
//just in case we don't run any prediction
VectorCopy(tostate->gravitydir, pmove.gravitydir);
VectorCopy(to.state->gravitydir, pmove.gravitydir);
//if all else fails...
pmove.pm_type = tostate->pm_type;
pmove.onground = tostate->onground;
VectorCopy(tostate->szmins, pmove.player_mins);
VectorCopy(tostate->szmaxs, pmove.player_maxs);
pmove.pm_type = to.state->pm_type;
pmove.onground = to.state->onground;
VectorCopy(to.state->szmins, pmove.player_mins);
VectorCopy(to.state->szmaxs, pmove.player_maxs);
if (!nopred)
{
for (i=1 ; i<UPDATE_BACKUP-1 && cl.ackedmovesequence+i < cl.movesequence; i++)
{
int stopframe;
//Con_Printf("Pred %i to %i\n", to.frame+1, min(from.frame+UPDATE_BACKUP, cl.movesequence));
for (i=to.frame+1, stopframe=min(from.frame+UPDATE_BACKUP, cl.movesequence) ; i < stopframe; i++)
{
outframe_t *of = &cl.outframes[(cl.ackedmovesequence+i) & UPDATE_MASK];
if (totime >= simtime)
{
if (i == 1)
{
//we must always predict a frame, just to ensure that the playerstate's jump status etc is valid for the next frame, even if we're not going to use it for interpolation.
//this assumes that we always have at least one video frame to each network frame, of course.
//note that q2 updates its values via networking rather than propagation.
player_state_t tmp, *next;
// Con_DPrintf(" propagate %i: %f-%f\n", cl.ackedmovesequence+i, fromtime, totime);
CL_PredictUsercmd (seat, pv->viewentity, tostate, &tmp, &of->cmd[seat]);
next = &cl.inframes[(toframe+i) & UPDATE_MASK].playerstate[pv->playernum];
next->onground = tmp.onground;
next->jump_held = tmp.jump_held;
next->jump_msec = tmp.jump_msec;
VectorCopy(tmp.gravitydir, next->gravitydir);
}
outframe_t *of = &cl.outframes[i & UPDATE_MASK];
if (to.time >= simtime)
break;
}
if (of->cmd_sequence != cl.ackedmovesequence+i)
if (of->cmd_sequence != i)
{
// Con_DPrintf("trying to predict a frame which is no longer valid\n");
break;
}
fromtime = totime;
fromstate = tostate;
fromframe = toframe; //qw debug
cmdfrom = cmdto;
//okay, move it forward a frame.
from = to;
cmdto = &of->cmd[seat];
totime = of->senttime;
toframe = cl.ackedmovesequence+i;//qw debug
to.cmd = &of->cmd[seat];
to.time = of->senttime;
to.frame = i;//qw debug
to.state = &framebuf[to.frame&1];
if (i == 1)//I've no idea how else to propogate event state from one frame to the next
tostate = &cl.inframes[(fromframe+i) & UPDATE_MASK].playerstate[pv->playernum];
else
tostate = &framebuf[i&1];
// Con_DPrintf(" pred %i: %f-%f\n", cl.ackedmovesequence+i, fromtime, totime);
CL_PredictUsercmd (seat, trackent, fromstate, tostate, cmdto);
if (from.frame == pv->prop.sequence && pv->prop.sequence)
{
if (!(cls.z_ext & Z_EXT_PF_ONGROUND))
from.state->onground = pv->prop.onground;
if (!(cls.z_ext & Z_EXT_PM_TYPE))
from.state->jump_held = pv->prop.jump_held;
from.state->jump_msec = pv->prop.jump_msec;
from.state->waterjumptime = pv->prop.waterjumptime;
if (!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))
VectorCopy(pv->prop.gravitydir, from.state->gravitydir);
}
CL_PredictUsercmd (seat, trackent, from.state, to.state, to.cmd);
if (i <= cl.validsequence && simtime >= to.time)
{ //this frame is final keep track of our propagated values.
pv->prop.onground = pmove.onground;
pv->prop.jump_held = pmove.jump_held;
pv->prop.jump_msec = pmove.jump_msec;
pv->prop.waterjumptime = pmove.waterjumptime;
VectorCopy(pmove.gravitydir, pv->prop.gravitydir);
pv->prop.sequence = i;
}
}
if (simtime > totime)
if (simtime > to.time)
{
//extrapolate X extra seconds
float msec;
usercmd_t indcmd;
msec = ((simtime - totime) * 1000);
msec = ((simtime - to.time) * 1000);
if (msec >= 1)
{
fromstate = tostate;
fromtime = totime;
fromframe = toframe;
cmdfrom = cmdto;
from = to;
tostate = &framebuf[i++&1];
if (cl_pendingcmd[seat].msec && !cls.demoplayback)
indcmd = cl_pendingcmd[seat];
else
indcmd = *cmdto;
cmdto = &indcmd;
totime = simtime;
toframe+=1;
indcmd = *to.cmd;
to.cmd = &indcmd;
to.time = simtime;
to.frame+=1;
to.state = &framebuf[to.frame&1];
if (cls.demoplayback)
{
@ -1308,66 +1311,77 @@ void CL_PredictMovePNum (int seat)
msec *= cl_demospeed.value;
}
cmdto->msec = bound(0, msec, 250);
to.cmd->msec = bound(0, msec, 250);
if (from.frame == pv->prop.sequence && pv->prop.sequence)
{ //overwrite non-networked state, to propagate it as required.
if (!(cls.z_ext & Z_EXT_PF_ONGROUND))
from.state->onground = pv->prop.onground;
if (!(cls.z_ext & Z_EXT_PM_TYPE))
from.state->jump_held = pv->prop.jump_held;
from.state->jump_msec = pv->prop.jump_msec;
from.state->waterjumptime = pv->prop.waterjumptime;
if (!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))
VectorCopy(pv->prop.gravitydir, from.state->gravitydir);
}
// Con_DPrintf(" extrap %i: %f-%f (%g)\n", toframe, fromtime, simtime, simtime-fromtime);
CL_PredictUsercmd (seat, trackent, fromstate, tostate, cmdto);
CL_PredictUsercmd (seat, trackent, from.state, to.state, to.cmd);
}
}
pv->onground = pmove.onground;
pv->pmovetype = tostate->pm_type;
pv->pmovetype = to.state->pm_type;
}
pmove.numphysent = oldphysent;
if (totime == fromtime)
if (to.time == from.time)
{
VectorCopy (tostate->velocity, pv->simvel);
VectorCopy (tostate->origin, pv->simorg);
VectorCopy (to.state->velocity, pv->simvel);
VectorCopy (to.state->origin, pv->simorg);
if (trackent && trackent != pv->playernum+1 && pv->cam_state == CAM_EYECAM)
VectorCopy(tostate->viewangles, pv->simangles);
VectorCopy(to.state->viewangles, pv->simangles);
//Con_DPrintf("%f %f %f\n", fromtime, simtime, totime);
}
else
{
vec3_t move;
// now interpolate some fraction of the final frame
f = (simtime - fromtime) / (totime - fromtime);
f = (simtime - from.time) / (to.time - from.time);
if (f < 0)
f = 0;
if (f > 1)
f = 1;
//Con_DPrintf("%i:%f %f %i:%f (%f)\n", fromframe, fromtime, simtime, toframe, totime, f);
VectorSubtract(tostate->origin, fromstate->origin, move);
VectorSubtract(to.state->origin, from.state->origin, move);
if (DotProduct(move, move) > 128*128)
{
// teleported, so don't lerp
VectorCopy (tostate->velocity, pv->simvel);
VectorCopy (tostate->origin, pv->simorg);
VectorCopy (to.state->velocity, pv->simvel);
VectorCopy (to.state->origin, pv->simorg);
}
else
{
for (i=0 ; i<3 ; i++)
{
pv->simorg[i] = (1-f)*fromstate->origin[i] + f*tostate->origin[i];
pv->simvel[i] = (1-f)*fromstate->velocity[i] + f*tostate->velocity[i];
pv->simorg[i] = (1-f)*from.state->origin[i] + f*to.state->origin[i];
pv->simvel[i] = (1-f)*from.state->velocity[i] + f*to.state->velocity[i];
if (trackent && trackent != pv->playernum+1 && pv->cam_state == CAM_EYECAM)
{
pv->simangles[i] = LerpAngles360(fromstate->viewangles[i], tostate->viewangles[i], f);// * (360.0/65535);
pv->simangles[i] = LerpAngles360(from.state->viewangles[i], to.state->viewangles[i], f);// * (360.0/65535);
// pv->viewangles[i] = LerpAngles16(fromstate->command.angles[i], tostate->command.angles[i], f) * (360.0/65535);
}
else if (lerpangles)
pv->simangles[i] = LerpAngles16(cmdfrom->angles[i], cmdto->angles[i], f) * (360.0/65535);
pv->simangles[i] = LerpAngles16(from.cmd->angles[i], to.cmd->angles[i], f) * (360.0/65535);
}
}
}
if (cls.protocol == CP_NETQUAKE && nopred)
pv->onground = tostate->onground;
pv->onground = to.state->onground;
else
CL_CatagorizePosition(pv, tostate->origin);
CL_CatagorizePosition(pv, to.state->origin);
CL_CalcCrouch (pv);
pv->waterlevel = pmove.waterlevel;

View file

@ -2498,10 +2498,25 @@ void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, int *stride, enum upload
int width, height;
void *buf;
qboolean okay = false;
qboolean usefbo;
qboolean oldwarndraw = r_refdef.warndraw;
Q_strncpyz(r_refdef.rt_destcolour[0].texname, "megascreeny", sizeof(r_refdef.rt_destcolour[0].texname));
R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, fbwidth, fbheight, 1, RT_IMAGEFLAGS);
BE_RenderToTextureUpdate2d(true);
if (fbwidth == vid.fbpwidth && fbheight == vid.fbpheight && qrenderer != QR_VULKAN)
usefbo = false;
else if (qrenderer == QR_OPENGL && gl_config.ext_framebuffer_objects)
usefbo = true;
else
return NULL;
if (usefbo)
{
r_refdef.warndraw = true;
Q_strncpyz(r_refdef.rt_destcolour[0].texname, "megascreeny", sizeof(r_refdef.rt_destcolour[0].texname));
/*vid.framebuffer =*/R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, fbwidth, fbheight, 1, RT_IMAGEFLAGS);
BE_RenderToTextureUpdate2d(true);
}
else
r_refdef.warndraw = false;
R2D_FillBlock(0, 0, vid.fbvwidth, vid.fbvheight);
@ -2512,7 +2527,7 @@ void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, int *stride, enum upload
#ifdef CSQC_DAT
if (!okay && CSQC_DrawView())
okay = true;
// if (!*r_refdef.rt_destcolour[0].texname)
if (usefbo)// && !*r_refdef.rt_destcolour[0].texname)
{ //csqc protects its own. lazily.
Q_strncpyz(r_refdef.rt_destcolour[0].texname, "megascreeny", sizeof(r_refdef.rt_destcolour[0].texname));
BE_RenderToTextureUpdate2d(true);
@ -2523,6 +2538,9 @@ void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, int *stride, enum upload
V_RenderView (no2d);
okay = true;
}
if (R2D_Flush)
R2D_Flush();
//fixme: add a way to get+save the depth values too
if (!okay)
{
@ -2533,9 +2551,14 @@ void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, int *stride, enum upload
else
buf = VID_GetRGBInfo(stride, &width, &height, fmt);
R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, 0, 0, 0, RT_IMAGEFLAGS);
Q_strncpyz(r_refdef.rt_destcolour[0].texname, "", sizeof(r_refdef.rt_destcolour[0].texname));
BE_RenderToTextureUpdate2d(true);
if (usefbo)
{
R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, 0, 0, 0, RT_IMAGEFLAGS);
Q_strncpyz(r_refdef.rt_destcolour[0].texname, "", sizeof(r_refdef.rt_destcolour[0].texname));
BE_RenderToTextureUpdate2d(true);
}
r_refdef.warndraw = oldwarndraw;
if (!buf || width != fbwidth || height != fbheight)
{
@ -3431,8 +3454,6 @@ void SCR_Init (void)
Cmd_AddCommandD ("screenshot_cubemap",SCR_ScreenShot_Cubemap_f, "screenshot_cubemap <name> [size]\nTakes 6 screenshots forming a single cubemap.");
Cmd_AddCommandD ("envmap",SCR_ScreenShot_Cubemap_f, "Legacy name for the screenshot_cubemap command."); //legacy
Cmd_AddCommand ("screenshot",SCR_ScreenShot_f);
Cmd_AddCommand ("sizeup",SCR_SizeUp_f);
Cmd_AddCommand ("sizedown",SCR_SizeDown_f);
scr_net = R2D_SafePicFromWad ("net");
scr_turtle = R2D_SafePicFromWad ("turtle");
@ -3473,7 +3494,5 @@ void SCR_DeInit (void)
Cmd_RemoveCommand ("screenshot_360");
Cmd_RemoveCommand ("screenshot_cubemap");
Cmd_RemoveCommand ("envmap");
Cmd_RemoveCommand ("sizeup");
Cmd_RemoveCommand ("sizedown");
}
}

View file

@ -50,10 +50,11 @@ typedef struct
double state_time; // not the same as the packet time,
// because player commands come asyncronously
usercmd_t command; // last command for prediction
vec3_t origin;
vec3_t predorigin; // pre-predicted pos
vec3_t predorigin; // pre-predicted pos for other players (allowing us to just lerp when active)
vec3_t viewangles; // only for demos, not from server
vec3_t velocity;
int weaponframe;
@ -75,15 +76,15 @@ typedef struct
int flags; // dead, gib, etc
int pm_type;
float waterjumptime;
qboolean onground;
qboolean jump_held;
int jump_msec; // hack for fixing bunny-hop flickering on non-ZQuake servers
vec3_t szmins, szmaxs;
vec3_t gravitydir;
float lerpstarttime;
int oldframe;
vec3_t szmins, szmaxs;
//maybe-propagated... use the networked value if available.
float waterjumptime; //never networked...
qboolean onground; //networked with Z_EXT_PF_ONGROUND||replacementdeltas
qboolean jump_held; //networked with Z_EXT_PM_TYPE
int jump_msec; // hack for fixing bunny-hop flickering on non-ZQuake servers
vec3_t gravitydir; //networked with replacementdeltas
} player_state_t;
@ -686,6 +687,20 @@ struct playerview_s
float viewheight;
int waterlevel; //for smartjump
//for values that are propagated from one frame to the next
//the next frame will always predict from the one we're tracking, where possible.
//these values should all regenerate naturally from networked state (misses should be small/rare and fade when the physics catches up).
struct playerpredprop_s
{
float waterjumptime;
qboolean onground;
vec3_t gravitydir;
qboolean jump_held;
int jump_msec; // hack for fixing bunny-hop flickering on non-ZQuake servers
int sequence;
} prop;
#ifdef Q2CLIENT
vec3_t predicted_origin;
vec3_t predicted_angles;

View file

@ -10,8 +10,12 @@
#define LittleShort(s) s
#define LittleLong(s) s
#else
#ifdef IMAGEFMT_TGA
cvar_t r_dodgytgafiles = CVARD("r_dodgytgafiles", "0", "Many old glquake engines had a buggy tga loader that ignored bottom-up flags. Naturally people worked around this and the world was plagued with buggy images. Most engines have now fixed the bug, but you can reenable it if you have bugged tga files.");
#endif
#ifdef IMAGEFMT_PCX
cvar_t r_dodgypcxfiles = CVARD("r_dodgypcxfiles", "0", "When enabled, this will ignore the palette stored within pcx files, for compatibility with quake2.");
#endif
cvar_t r_dodgymiptex = CVARD("r_dodgymiptex", "1", "When enabled, this will force regeneration of mipmaps, discarding mips1-4 like glquake did. This may eg solve fullbright issues with some maps, but may reduce distant detail levels.");
char *r_defaultimageextensions =
@ -21,15 +25,17 @@ char *r_defaultimageextensions =
#ifdef IMAGEFMT_KTX
"ktx " //compressed or something. not to be confused with the qw mod by the same name. GL requires that etc2 compression is supported by modern drivers, but not necessarily the hardware. as such, dds with its s3tc bias should always come first (as the patents mean that drivers are much less likely to advertise it when they don't support it properly).
#endif
#ifdef IMAGEFMT_TGA
"tga" //fairly fast to load
#if defined(AVAIL_PNGLIB) || defined(FTE_TARGET_WEB)
#endif
#if defined(IMAGEFMT_PNG) || defined(FTE_TARGET_WEB)
" png" //pngs, fairly common, but slow
#endif
#ifdef IMAGEFMT_BMP
//" bmp" //wtf? at least not lossy
//" ico" //noone wants this...
#endif
#if defined(AVAIL_JPEGLIB) || defined(FTE_TARGET_WEB)
#if defined(IMAGEFMT_JPG) || defined(FTE_TARGET_WEB)
" jpg" //q3 uses some jpegs, for some reason
//" jpeg" //thankfuly the quake community stuck to .jpg instead
#endif
@ -49,10 +55,102 @@ char *r_defaultimageextensions =
#ifdef IMAGEFMT_PKM
//" pkm" //compressed format, but lacks mipmaps which makes it terrible to use.
#endif
#ifdef IMAGEFMT_ASTC
//" astc" //compressed format, but lacks mipmaps which makes it terrible to use.
#endif
#ifdef IMAGEFMT_GIF
//" gif"
#endif
#ifdef IMAGEFMT_PIC
//" pic"
#endif
#ifdef IMAGEFMT_PCX
" pcx" //pcxes are the original gamedata of q2. So we don't want them to override pngs.
#endif
#ifdef IMAGEFMT_LMP
//" lmp" //lame outdated junk. any code that expects a lmp will use that extension, so don't bother swapping out extensions for it.
#endif
;
#ifdef AVAIL_STBI
#if defined(IMAGEFMT_PNG) && !defined(AVAIL_PNGLIB) && !defined(FTE_TARGET_WEB)
#define STBI_ONLY_PNG
#define STBIW_ONLY_PNG
#undef IMAGEFMT_PNG
#endif
#if defined(IMAGEFMT_JPG) && !defined(AVAIL_JPEGLIB) && !defined(FTE_TARGET_WEB)
#define STBI_ONLY_JPEG
#define STBIW_ONLY_JPEG
#undef IMAGEFMT_JPG
#endif
#if defined(IMAGEFMT_BMP) && 0 //use our own implementation, giving .ico too
#define STBI_ONLY_BMP
#define STBIW_ONLY_BMP
#undef IMAGEFMT_BMP
#endif
#if defined(IMAGEFMT_PSD) && 0 //use our own implementation
#define STBI_ONLY_PSD
#undef IMAGEFMT_PSD
#endif
#if defined(IMAGEFMT_TGA) && 0 //use our own implementation, giving htga and some twiddles.
#define STBI_ONLY_TGA
#define STBIW_ONLY_TGA
#undef IMAGEFMT_TGA
#endif
#if defined(IMAGEFMT_GIF) //&& 0
#define STBI_ONLY_GIF
#undef IMAGEFMT_GIF
#endif
#if defined(IMAGEFMT_HDR) && 0 //use our own implementation, we're not using the stbi_loadf stuff anyway
#define STBI_ONLY_HDR
#define STBIW_ONLY_HDR
#undef IMAGEFMT_HDR
#endif
#if defined(IMAGEFMT_PIC) //&& 0
#define STBI_ONLY_PIC
#undef IMAGEFMT_PIC
#endif
#if defined(IMAGEFMT_PBM) && 0 //use our own implementation, giving pfm.pbm.pam too
#define STBI_ONLY_PNM
#undef IMAGEFMT_PBM
#endif
//now we know whether we need stbi or not, pull in the right stuff.
#if defined(STBI_ONLY_PNG) || defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_BMP) || defined(STBI_ONLY_PSD) || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM)
#define STBI_NO_STDIO
#define STBI_MALLOC BZ_Malloc
#define STBI_REALLOC BZ_Realloc
#define STBI_FREE BZ_Free
//#define STBI_NO_FAILURE_STRINGS //not thread safe, so don't bother showing messages.
#define STB_IMAGE_IMPLEMENTATION
#include "../libs/stb_image.h" //from https://raw.githubusercontent.com/nothings/stb/master/stb_image.h
#endif
#if defined(STBIW_ONLY_PNG) || defined(STBIW_ONLY_JPEG) || defined(STBIW_ONLY_BMP) || defined(STBIW_ONLY_TGA) || defined(STBIW_ONLY_HDR)
#define STBI_WRITE_NO_STDIO
#define STB_IMAGE_WRITE_STATIC
#define STBIWDEF fte_inlinestatic
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "../libs/stb_image_write.h" //from https://raw.githubusercontent.com/nothings/stb/master/stb_image_write.h
#endif
#endif
#ifdef IMAGEFMT_GIF
#pragma message("IMAGEFMT_GIF requires AVAIL_STBI")
#undef IMAGEFMT_GIF
#endif
#if defined(IMAGEFMT_PNG) && !defined(AVAIL_PNGLIB)
#pragma message("IMAGEFMT_PNG requires AVAIL_PNGLIB or AVAIL_STBI")
#undef IMAGEFMT_PNG
#elif defined(AVAIL_PNGLIB)
#undef AVAIL_PNGLIB
#endif
#if defined(IMAGEFMT_JPG) && !defined(AVAIL_JPEGLIB)
#pragma message("IMAGEFMT_JPG requires AVAIL_JPEGLIB or AVAIL_STBI")
#undef IMAGEFMT_JPG
#elif defined(AVAIL_JPEGLIB)
#undef AVAIL_JPEGLIB
#endif
static void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int flags, uploadfmt_t origfmt, const char *imagename);
static void QDECL R_ImageExtensions_Callback(struct cvar_s *var, char *oldvalue);
cvar_t r_imageextensions = CVARCD("r_imageextensions", NULL, R_ImageExtensions_Callback, "The list of image file extensions which might exist on disk (note that this does not list all supported formats, only the extensions that should be searched for).");
@ -119,6 +217,7 @@ static void GenerateXMPData(char *blob, size_t blobsize, int width, int height,
#endif
#endif
#ifdef IMAGEFMT_TGA
#ifndef _WIN32
#include <unistd.h>
#endif
@ -1082,6 +1181,7 @@ qboolean WriteTGA(char *filename, enum fs_relative fsroot, const qbyte *fte_rest
}
return success;
}
#endif
#ifdef AVAIL_PNGLIB
#ifndef AVAIL_ZLIB
@ -3368,6 +3468,117 @@ static void *ReadPSDFile(qbyte *buf, size_t len, const char *fname, int *outwidt
}
#endif
#ifdef IMAGEFMT_EXR
#if 0
#include "OpenEXR/ImfCRgbaFile.h"
#else
typedef void ImfInputFile;
typedef void ImfHeader;
typedef void ImfRgba;
#endif
static struct
{
void *handle;
ImfInputFile *(*OpenInputFile) (const char name[]);
int (*CloseInputFile) (ImfInputFile *in);
int (*InputSetFrameBuffer) (ImfInputFile *in, ImfRgba *base, size_t xStride, size_t yStride);
int (*InputReadPixels) (ImfInputFile *in, int scanLine1, int scanLine2);
const ImfHeader*(*InputHeader) (const ImfInputFile *in);
void (*HeaderDataWindow) (const ImfHeader *hdr, int *xMin, int *yMin, int *xMax, int *yMax);
} exr;
static void InitLibrary_OpenEXR(void)
{
#ifdef IMF_MAGIC
exr.OpenInputFile = ImfOpenInputFile;
exr.CloseInputFile = ImfCloseInputFile;
exr.InputSetFrameBuffer = ImfInputSetFrameBuffer;
exr.InputReadPixels = ImfInputReadPixels;
exr.InputHeader = ImfInputHeader;
exr.HeaderDataWindow = ImfHeaderDataWindow;
#else
dllfunction_t funcs[] =
{
{(void**)&exr.OpenInputFile, "ImfOpenInputFile"},
{(void**)&exr.CloseInputFile, "ImfCloseInputFile"},
{(void**)&exr.InputSetFrameBuffer, "ImfInputSetFrameBuffer"},
{(void**)&exr.InputReadPixels, "ImfInputReadPixels"},
{(void**)&exr.InputHeader, "ImfInputHeader"},
{(void**)&exr.HeaderDataWindow, "ImfHeaderDataWindow"},
{NULL}
};
#ifdef __linux__
//its some shitty c++ library, so while the C api that we use is stable, nothing else is so it has lots of random names.
if (!exr.handle)
exr.handle = Sys_LoadLibrary("libIlmImf-2_3.so.24", funcs); //debian sid(bullseye)
if (!exr.handle)
exr.handle = Sys_LoadLibrary("libIlmImf-2_2.so.23", funcs); //debian buster
if (!exr.handle)
exr.handle = Sys_LoadLibrary("libIlmImf-2_2.so.22", funcs); //debian stretch
#endif
//try the generic/dev name.
if (!exr.handle)
exr.handle = Sys_LoadLibrary("libIlmImf", funcs);
#endif
}
#ifdef _WIN32
#include <io.h>
#endif
static void *ReadEXRFile(qbyte *buf, size_t len, const char *fname, int *outwidth, int *outheight, uploadfmt_t *outformat)
{
char tname[] = "/tmp/exr.XXXXXX";
#ifndef _WIN32
int fd;
#endif
ImfInputFile *ctx;
const ImfHeader *hdr;
void *result;
if (!exr.handle)
{
Con_Printf("%s: libIlmImf not loaded\n", fname);
return NULL;
}
//shitty API that only supports filenames, so now that we've read it from a file, write it to a file so that it can be read. See, shitty.
#ifdef _WIN32
if (!_mktemp(tname))
{
DWORD sizewritten;
HANDLE fd = CreateFileA(tname, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY|FILE_FLAG_DELETE_ON_CLOSE, NULL);
WriteFile(fd, buf, len, &sizewritten, NULL); //assume we managed to write it all
ctx = exr.OpenInputFile(tname); //is this be ansi or oem or utf-16 or what? lets assume that it has no idea either.
CloseHandle(fd);
#else
fd = mkstemp(tname); //bsd4.3/posix1-2001
if (fd >= 0)
{
write(fd, buf, len);
ctx = exr.OpenInputFile(tname);
close(fd); //we don't need the input file now.
Sys_remove(tname);
#endif
if (ctx)
{
int xmin, xmax, ymin, ymax;
hdr = exr.InputHeader(ctx);
exr.HeaderDataWindow(hdr, &xmin,&ymin, &xmax,&ymax);
*outwidth = (xmax-xmin)+1;
*outheight = (ymax-ymin)+1;
result = BZ_Malloc(sizeof(short)*4**outwidth**outheight);
exr.InputSetFrameBuffer(ctx, (char*)result-xmin*8-ymin**outwidth*8, 1, *outwidth);
exr.InputReadPixels(ctx, ymin, ymax);
exr.CloseInputFile(ctx);
*outformat = PTI_RGBA16F; //output is always half-floats.
return result;
}
}
return NULL;
}
#endif
#ifndef NPFTE
@ -3672,6 +3883,8 @@ qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struc
case PTI_L8A8_SRGB: header.glinternalformat = 0x8C45/*GL_SLUMINANCE8_ALPHA8*/; header.glbaseinternalformat = 0x190A/*GL_LUMINANCE_ALPHA*/; header.glformat = 0x190A/*GL_LUMINANCE_ALPHA*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break;
case PTI_RGB8: header.glinternalformat = 0x8051/*GL_RGB8*/; header.glbaseinternalformat = 0x1907/*GL_RGB*/; header.glformat = 0x1907/*GL_RGB*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break;
case PTI_BGR8: header.glinternalformat = 0x8051/*GL_RGB8*/; header.glbaseinternalformat = 0x1907/*GL_RGB*/; header.glformat = 0x80E0/*GL_BGR*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break;
case PTI_RGB8_SRGB: header.glinternalformat = 0x8C41/*GL_SRGB8*/; header.glbaseinternalformat = 0x1907/*GL_RGB*/; header.glformat = 0x1907/*GL_RGB*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break;
case PTI_BGR8_SRGB: header.glinternalformat = 0x8C41/*GL_SRGB8*/; header.glbaseinternalformat = 0x1907/*GL_RGB*/; header.glformat = 0x80E0/*GL_BGR*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break;
case PTI_R16: header.glinternalformat = 0x822A/*GL_R16*/; header.glbaseinternalformat = 0x1903/*GL_RED*/; header.glformat = 0x1903/*GL_RED*/; header.gltype = 0x1403/*GL_UNSIGNED_SHORT*/; header.gltypesize = 2; break;
case PTI_RGBA16: header.glinternalformat = 0x805B/*GL_RGBA16*/; header.glbaseinternalformat = 0x1903/*GL_RED*/; header.glformat = 0x1903/*GL_RED*/; header.gltype = 0x1403/*GL_UNSIGNED_SHORT*/; header.gltypesize = 2; break;
case PTI_R16F: header.glinternalformat = 0x822D/*GL_R16F*/; header.glbaseinternalformat = 0x1903/*GL_RED*/; header.glformat = 0x1903/*GL_RED*/; header.gltype = 0x140B/*GL_HALF_FLOAT*/; header.gltypesize = 2; break;
@ -4597,6 +4810,8 @@ qboolean Image_WriteDDSFile(const char *filename, enum fs_relative fsroot, struc
case PTI_L8A8_SRGB: return false; //unsupported
case PTI_RGB8: return false; //unsupported
case PTI_BGR8: return false; //unsupported
case PTI_RGB8_SRGB: return false; //unsupported
case PTI_BGR8_SRGB: return false; //unsupported
case PTI_R16: h10.dxgiformat = 56/*DXGI_FORMAT_R16_UNORM*/; break;
case PTI_RGBA16: h10.dxgiformat = 11/*DXGI_FORMAT_R16G16B16A16_UNORM*/; break;
case PTI_R16F: h10.dxgiformat = 54/*DXGI_FORMAT_R16_FLOAT*/; break;
@ -5060,16 +5275,120 @@ static struct pendingtextureinfo *Image_ReadVTFFile(unsigned int flags, const ch
}
#endif
//This is for the version command
void Image_PrintInputFormatVersions(void)
{
Con_Printf("^3Image Formats:^7");
#ifdef IMAGEFMT_DDS
Con_Printf(" dds");
#endif
#ifdef IMAGEFMT_KTX
Con_Printf(" ktx");
#endif
#ifdef IMAGEFMT_VTF
Con_Printf(" vtf");
#endif
#ifdef IMAGEFMT_TGA
Con_Printf(" tga");
#endif
#ifdef AVAIL_PNGLIB
Con_Printf(" png");
#ifdef DYNAMIC_LIBPNG
if (!LIBPNG_LOADED())
Con_Printf("^h(unavailable, %s)", PNG_LIBPNG_VER_STRING);
else
Con_Printf("^h(dynamic, %s)", PNG_LIBPNG_VER_STRING);
#else
Con_Printf("^h(%s)", PNG_LIBPNG_VER_STRING);
#endif
#endif
#ifdef IMAGEFMT_BMP
Con_Printf(" bmp+ico");
#endif
#ifdef AVAIL_JPEGLIB
Con_Printf(" jpeg");
#ifdef DYNAMIC_LIBJPEG
if (!LIBJPEG_LOADED())
Con_Printf("^h(unavailable, %s)", PNG_LIBPNG_VER_STRING);
else
Con_Printf("^h(dynamic, %i, %d series)", JPEG_LIB_VERSION, ( JPEG_LIB_VERSION / 10 ) );
#else
Con_Printf("^h(%i, %d series)", JPEG_LIB_VERSION, ( JPEG_LIB_VERSION / 10 ) );
#endif
#endif
#ifdef IMAGEFMT_BLP
Con_Printf(" BLP");
#endif
#ifdef IMAGEFMT_PBM
Con_Printf(" pfm+pbm+pgm+ppm"/*"+pam"*/);
#endif
#ifdef IMAGEFMT_PSD
Con_Printf(" psd");
#endif
#ifdef IMAGEFMT_HDR
Con_Printf(" hdr");
#endif
#ifdef IMAGEFMT_ASTC
Con_Printf(" astc");
#endif
#ifdef IMAGEFMT_PKM
Con_Printf(" pkm");
#endif
#ifdef IMAGEFMT_EXR
Con_Printf(" exr");
if (!exr.handle)
Con_Printf("^h(unavailable)");
#endif
#ifdef IMAGEFMT_PCX
Con_Printf(" pcx");
#endif
#ifdef STB_IMAGE_IMPLEMENTATION
#ifdef STBI_ONLY_PNG
Con_Printf(" png^h(stbi)");
#endif
#ifdef STBI_ONLY_JPEG
Con_Printf(" jpeg^h(stbi)");
#endif
#ifdef STBI_ONLY_BMP
Con_Printf(" bmp^h(stbi)");
#endif
#ifdef STBI_ONLY_PSD
Con_Printf(" psd^h(stbi)");
#endif
#ifdef STBI_ONLY_TGA
Con_Printf(" tga^h(stbi)");
#endif
#ifdef STBI_ONLY_GIF
Con_Printf(" gif^h(stbi)");
#endif
#ifdef STBI_ONLY_HDR
Con_Printf(" hdr^h(stbi)");
#endif
#ifdef STBI_ONLY_PIC
Con_Printf(" pic^h(stbi)");
#endif
#ifdef STBI_ONLY_PNM
Con_Printf(" pnm^h(stbi)");
#endif
#endif
Con_Printf("\n");
}
//if force_rgba8 then it guarentees rgba8 or rgbx8, otherwise can return l8, etc
qbyte *ReadRawImageFile(qbyte *buf, int len, int *width, int *height, uploadfmt_t *format, qboolean force_rgba8, const char *fname)
{
qbyte *data;
*format = PTI_RGBX8;
#ifdef IMAGEFMT_TGA
if ((data = ReadTargaFile(buf, len, width, height, format, false, force_rgba8?PTI_RGBA8:PTI_INVALID)))
{
TRACE(("dbg: Read32BitImageFile: tga\n"));
return data;
}
#endif
#ifdef AVAIL_PNGLIB
if (len > 4 && (buf[0] == 137 && buf[1] == 'P' && buf[2] == 'N' && buf[3] == 'G') && (data = ReadPNGFile(fname, buf, len, width, height, format)))
@ -5124,7 +5443,40 @@ qbyte *ReadRawImageFile(qbyte *buf, int len, int *width, int *height, uploadfmt_
return data;
#endif
#if 1//def IMAGEFMT_LMP
#ifdef IMAGEFMT_EXR
if (len > 4 && (buf[0]|(buf[1]<<8)|(buf[2]<<16)|(buf[3]<<24)) == 20000630 && (data = ReadEXRFile(buf, len, fname, width, height, format)))
return data;
#endif
#ifdef STB_IMAGE_IMPLEMENTATION
{
int components;
//Note: I don't like passing STBI_default, because it'll return 24bit data which we'll then have to pad anyway.
//but there's no other easy way to check how many channels are actually valid.
if ((data = stbi_load_from_memory(buf, len, width, height, &components, STBI_default)))
{
switch(components)
{
case STBI_grey:
*format = PTI_L8;
return data;
case STBI_grey_alpha:
*format = PTI_L8A8;
return data;
case STBI_rgb:
*format = PTI_RGB8;
return data;
case STBI_rgb_alpha:
*format = PTI_RGBA8;
return data;
}
//erk...?
stbi_image_free(data);
}
}
#endif
#ifdef IMAGEFMT_LMP
if (len >= 8) //.lmp has no magic id. guess at it.
{
int w = LittleLong(((int*)buf)[0]);
@ -5278,6 +5630,58 @@ static struct
#endif
};
static void Image_MipMap3X8 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight)
{
int i, j;
qbyte *inrow;
int rowwidth = inwidth*3; //rowwidth is the byte width of the input
inrow = in;
//mips round down, except for when the input is 1. which bugs out.
if (inwidth <= 1 && inheight <= 1)
{
out[0] = in[0];
out[1] = in[1];
out[2] = in[2];
}
else if (inheight <= 1)
{
//single row, don't peek at the next
for (in = inrow, j=0 ; j<outwidth ; j++, out+=3, in+=6)
{
out[0] = (in[0] + in[3])>>1;
out[1] = (in[1] + in[4])>>1;
out[2] = (in[2] + in[5])>>1;
}
}
else if (inwidth <= 1)
{
//single colum, peek only at this pixel
for (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)
{
for (in = inrow, j=0 ; j<outwidth ; j++, out+=3, in+=6)
{
out[0] = (in[0] + in[rowwidth+0])>>1;
out[1] = (in[1] + in[rowwidth+1])>>1;
out[2] = (in[2] + in[rowwidth+2])>>1;
}
}
}
else
{
for (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)
{
for (in = inrow, j=0 ; j<outwidth ; j++, out+=3, in+=6)
{
out[0] = (in[0] + in[3] + in[rowwidth+0] + in[rowwidth+3])>>2;
out[1] = (in[1] + in[4] + in[rowwidth+1] + in[rowwidth+4])>>2;
out[2] = (in[2] + in[5] + in[rowwidth+2] + in[rowwidth+5])>>2;
}
}
}
}
static void Image_MipMap4X8 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight)
{
int i, j;
@ -5391,6 +5795,101 @@ static void Image_MipMap4X16 (unsigned short *in, int inwidth, int inheight, uns
}
}
static float HalfToFloat(unsigned short val)
{
union
{
float f;
unsigned int u;
} u;
u.u = (((val&0x7c00)>>10)-15+127)<<23; //read exponent, rebias it, and reshift.
u.u |= ((val & 0x3ff)<<13) | ((val & 0x3ff)<<3) | ((val & 0x3ff)>>7); //shift up the mantissa, and fold a little
u.u |= (val&0x8000)<<16; //retain the sign bit.
return u.f;
}
static unsigned short FloatToHalf(float val)
{
union
{
float f;
unsigned int u;
} u = {val};
int e = 0;
int m;
e = ((u.u>>23)&0xff) - 127;
if (e < -15)
return 0; //too small exponent, treat it as a 0 denormal
if (e > 15)
m = 0; //infinity instead of a nan
else
m = (u.u&((1<<23)-1))>>13;
return ((u.u>>16)&0x8000) | ((e+15)<<10) | m;
}
fte_inlinestatic unsigned short HalfFloatBlend2(unsigned short a, unsigned short b)
{
return FloatToHalf((HalfToFloat(a) + HalfToFloat(b))/2);
}
fte_inlinestatic unsigned short HalfFloatBlend4(unsigned short a, unsigned short b, unsigned short c, unsigned short d)
{
return FloatToHalf((HalfToFloat(a) + HalfToFloat(b) + HalfToFloat(c) + HalfToFloat(d))/4);
}
static void Image_MipMap4X16F (unsigned short *in, int inwidth, int inheight, unsigned short *out, int outwidth, int outheight)
{
int i, j;
unsigned short *inrow;
int rowwidth = inwidth*4; //rowwidth is the byte width of the input
inrow = in;
//mips round down, except for when the input is 1. which bugs out.
if (inwidth <= 1 && inheight <= 1)
{
out[0] = in[0];
out[1] = in[1];
out[2] = in[2];
out[3] = in[3];
}
else if (inheight <= 1)
{
//single row, don't peek at the next
for (in = inrow, j=0 ; j<outwidth ; j++, out+=4, in+=8)
{
out[0] = HalfFloatBlend2(in[0], in[4]);
out[1] = HalfFloatBlend2(in[1], in[5]);
out[2] = HalfFloatBlend2(in[2], in[6]);
out[3] = HalfFloatBlend2(in[3], in[7]);
}
}
else if (inwidth <= 1)
{
//single colum, peek only at this pixel
for (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)
{
for (in = inrow, j=0 ; j<outwidth ; j++, out+=4, in+=8)
{
out[0] = HalfFloatBlend2(in[0], in[rowwidth+0]);
out[1] = HalfFloatBlend2(in[1], in[rowwidth+1]);
out[2] = HalfFloatBlend2(in[2], in[rowwidth+2]);
out[3] = HalfFloatBlend2(in[3], in[rowwidth+3]);
}
}
}
else
{
for (i=0 ; i<outheight ; i++, inrow+=rowwidth*2)
{
for (in = inrow, j=0 ; j<outwidth ; j++, out+=4, in+=8)
{
out[0] = HalfFloatBlend4(in[0], in[4], in[rowwidth+0], in[rowwidth+4]);
out[1] = HalfFloatBlend4(in[1], in[5], in[rowwidth+1], in[rowwidth+5]);
out[2] = HalfFloatBlend4(in[2], in[6], in[rowwidth+2], in[rowwidth+6]);
out[3] = HalfFloatBlend4(in[3], in[7], in[rowwidth+3], in[rowwidth+7]);
}
}
}
}
static void Image_MipMap4X32F (float *in, int inwidth, int inheight, float *out, int outwidth, int outheight)
{
int i, j;
@ -5597,6 +6096,26 @@ static void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int fla
mips->mipcount = mip+1;
}
break;
case PTI_RGBA16F:
for (mip = mips->mipcount; mip < 32; mip++)
{
mips->mip[mip].width = mips->mip[mip-1].width >> 1;
mips->mip[mip].height = mips->mip[mip-1].height >> 1;
mips->mip[mip].depth = 1;
if (mips->mip[mip].width < 1 && mips->mip[mip].height < 1)
break;
if (mips->mip[mip].width < 1)
mips->mip[mip].width = 1;
if (mips->mip[mip].height < 1)
mips->mip[mip].height = 1;
mips->mip[mip].datasize = ((mips->mip[mip].width+3)&~3) * mips->mip[mip].height * sizeof(unsigned short)*4;
mips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize);
mips->mip[mip].needfree = true;
Image_MipMap4X16F(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height);
mips->mipcount = mip+1;
}
break;
case PTI_RGBA16:
for (mip = mips->mipcount; mip < 32; mip++)
{
@ -5617,6 +6136,29 @@ static void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int fla
mips->mipcount = mip+1;
}
break;
case PTI_RGB8_SRGB:
case PTI_BGR8_SRGB:
case PTI_RGB8:
case PTI_BGR8:
for (mip = mips->mipcount; mip < 32; mip++)
{
mips->mip[mip].width = mips->mip[mip-1].width >> 1;
mips->mip[mip].height = mips->mip[mip-1].height >> 1;
mips->mip[mip].depth = 1;
if (mips->mip[mip].width < 1 && mips->mip[mip].height < 1)
break;
if (mips->mip[mip].width < 1)
mips->mip[mip].width = 1;
if (mips->mip[mip].height < 1)
mips->mip[mip].height = 1;
mips->mip[mip].datasize = ((mips->mip[mip].width+3)&~3) * mips->mip[mip].height * sizeof(qbyte)*4;
mips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize);
mips->mip[mip].needfree = true;
Image_MipMap3X8(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height);
mips->mipcount = mip+1;
}
break;
case PTI_RGBA8_SRGB:
case PTI_RGBX8_SRGB:
case PTI_BGRA8_SRGB:
@ -7913,6 +8455,8 @@ void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes,
case PTI_RGB8:
case PTI_BGR8:
case PTI_RGB8_SRGB:
case PTI_BGR8_SRGB:
b = 3;
break;
case PTI_L8:
@ -8051,6 +8595,8 @@ const char *Image_FormatName(uploadfmt_t fmt)
case PTI_DEPTH24_8: return "DEPTH24_8";
case PTI_RGB8: return "RGB8";
case PTI_BGR8: return "BGR8";
case PTI_RGB8_SRGB: return "RGB8_SRGB";
case PTI_BGR8_SRGB: return "BGR8_SRGB";
case PTI_L8: return "L8";
case PTI_L8_SRGB: return "L8_SRGB";
case PTI_L8A8: return "L8A8";
@ -9201,6 +9747,8 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
case PTI_RGB565:
case PTI_RGB8:
case PTI_BGR8:
case PTI_RGB8_SRGB:
case PTI_BGR8_SRGB:
case PTI_E5BGR9:
case PTI_RGBX8:
case PTI_BGRX8:
@ -9241,6 +9789,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
uploadfmt_t nf = PTI_MAX;
switch(mips->encoding)
{
case PTI_R8: nf = PTI_INVALID; break;
case PTI_L8: nf = PTI_L8_SRGB; break;
case PTI_L8A8: nf = PTI_L8A8_SRGB; break;
case PTI_LLLX8: nf = PTI_RGBX8_SRGB; break;
@ -9249,6 +9798,8 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
case PTI_RGBX8: nf = PTI_RGBX8_SRGB; break;
case PTI_BGRA8: nf = PTI_BGRA8_SRGB; break;
case PTI_BGRX8: nf = PTI_BGRX8_SRGB; break;
case PTI_RGB8: nf = PTI_RGB8_SRGB; break;
case PTI_BGR8: nf = PTI_BGR8_SRGB; break;
case PTI_BC1_RGB: nf = PTI_BC1_RGB_SRGB; break;
case PTI_BC1_RGBA: nf = PTI_BC1_RGBA_SRGB; break;
case PTI_BC2_RGBA: nf = PTI_BC2_RGBA_SRGB; break;
@ -9272,6 +9823,16 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
case PTI_ASTC_10X10_LDR:nf = PTI_ASTC_10X10_SRGB; break;
case PTI_ASTC_12X10_LDR:nf = PTI_ASTC_12X10_SRGB; break;
case PTI_ASTC_12X12_LDR:nf = PTI_ASTC_12X12_SRGB; break;
//these formats are inherantly linear. oh well.
case PTI_R16:
case PTI_R16F:
case PTI_R32F:
case PTI_RGBA16:
case PTI_RGBA16F:
case PTI_RGBA32F:
nf = mips->encoding;
break;
default:
if (freedata)
BZ_Free(rgbadata);
@ -9283,8 +9844,12 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
{ //srgb->linear
int m = mips->mip[0].width*mips->mip[0].height*mips->mip[0].depth;
switch(nf)
switch(mips->encoding)
{
case PTI_RGB8:
case PTI_BGR8:
m*=3;
//fallthrough
case PTI_R8:
case PTI_L8:
for (i = 0; i < m; i++)
@ -9296,7 +9861,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
((qbyte*)rgbadata)[i+0] = 255*Image_LinearFloatFromsRGBFloat(((qbyte*)rgbadata)[i+0] * (1.0/255));
break;
case PTI_R16:
for (i = 0; i < m; i+=4)
for (i = 0; i < m; i++)
((unsigned short*)rgbadata)[i+0] = 0xffff*Image_LinearFloatFromsRGBFloat(((unsigned short*)rgbadata)[i+0] * (1.0/0xffff));
break;
case PTI_RGBA16:
@ -9406,6 +9971,18 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
}
}
break;
case PTI_RGBA16F:
{
unsigned short *fte_restrict premul = (unsigned short*)mips->mip[0].data;
for (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++, premul+=4)
{
float a = HalfToFloat(premul[3]);
premul[0] = FloatToHalf(HalfToFloat(premul[0]) * a);
premul[1] = FloatToHalf(HalfToFloat(premul[1]) * a);
premul[2] = FloatToHalf(HalfToFloat(premul[2]) * a);
}
}
break;
case PTI_RGBA16:
{
unsigned short *fte_restrict premul = (unsigned short*)mips->mip[0].data;
@ -9458,7 +10035,7 @@ static qboolean Image_LoadRawTexture(texid_t tex, unsigned int flags, void *rawd
{
struct pendingtextureinfo *mips;
mips = Z_Malloc(sizeof(*mips));
mips->type = (flags & IF_3DMAP)?PTI_3D:PTI_2D;
mips->type = (flags&IF_TEXTYPE)>>IF_TEXTYPESHIFT;
if (!Image_GenMip0(mips, flags, rawdata, palettedata, imgwidth, imgheight, fmt, true))
{
@ -9519,6 +10096,37 @@ static struct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char
mips = Image_ReadASTCFile(flags, fname, filedata, filesize);
#endif
#ifdef STBI_ONLY_GIF
if (!mips)
{
int *delays = NULL;
int w, h, d;
int comp;
stbi_uc *data = stbi_load_gif_from_memory(filedata, filesize, &delays, &w, &h, &d, &comp, 4); //force 4, we're not really ready for other types of 2d arrays.
if (data)
{
mips = Z_Malloc(sizeof(*mips));
mips->mipcount = 1; //this format doesn't support mipmaps. so there's only one level.
mips->type = PTI_2D_ARRAY; //2d arrays are basically just a 3d texture with weird mips (meaning they can load with gl's glTexStorage3D
mips->extrafree = delays;
switch(comp)
{
case 1: mips->encoding = PTI_L8; break;
case 2: mips->encoding = PTI_L8A8; break;
case 3: mips->encoding = PTI_RGB8; break;
case 4: mips->encoding = PTI_RGBA8; break;
default: mips->encoding = PTI_INVALID; break;
}
mips->mip[0].data = data;
mips->mip[0].datasize = comp*w*h*d;
mips->mip[0].width = w;
mips->mip[0].height = h;
mips->mip[0].depth = d;
mips->mip[0].needfree = true;
}
}
#endif
//the above formats are assumed to have consumed filedata somehow (probably storing into mips->extradata)
if (mips)
{
@ -10142,6 +10750,7 @@ static void Image_LoadHiResTextureWorker(void *ctx, void *data, size_t a, size_t
VFS_READ(f, buf, fsize);
VFS_CLOSE(f);
#ifdef IMAGEFMT_TGA
if (locflags & IF_TRYBUMP)
{ //it was supposed to be a heightmap image (that we need to convert to normalmap)
qbyte *d;
@ -10159,6 +10768,7 @@ static void Image_LoadHiResTextureWorker(void *ctx, void *data, size_t a, size_t
}
//guess not, fall back to normalmaps
}
#endif
if (Image_LoadTextureFromMemory(tex, tex->flags, tex->ident, fname, buf, fsize))
{
@ -10785,6 +11395,21 @@ void Image_Formats_f(void)
//may not create any images yet.
void Image_Init(void)
{
static qboolean initedlibs;
if (!initedlibs)
{
initedlibs = true;
#ifdef AVAIL_JPEGLIB
LibJPEG_Init();
#endif
#ifdef AVAIL_PNGLIB
LibPNG_Init();
#endif
#ifdef IMAGEFMT_EXR
InitLibrary_OpenEXR();
#endif
}
wadmutex = Sys_CreateMutex();
memset(imagetablebuckets, 0, sizeof(imagetablebuckets));
Hash_InitTable(&imagetable, sizeof(imagetablebuckets)/sizeof(imagetablebuckets[0]), imagetablebuckets);
@ -11046,6 +11671,14 @@ int MipColor(int r, int g, int b)
return best;
}
#ifdef STB_IMAGE_WRITE_IMPLEMENTATION
static void stbiwritefunc(void *context, void *data, int size)
{
vfsfile_t *f = context;
VFS_WRITE(f, data, size);
}
#endif
qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer, int numbuffers, qintptr_t bytestride, int width, int height, enum uploadfmt fmt, qboolean writemeta)
{
char ext[8];
@ -11070,6 +11703,70 @@ qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer,
COM_FileExtension(filename, ext, sizeof(ext));
#ifdef STB_IMAGE_WRITE_IMPLEMENTATION
if (numbuffers == 1)
{
vfsfile_t *outfile = NULL;
qboolean ret = false;
int comp = 0;
if (comp == PTI_L8)
comp = 1;
else if (comp == PTI_L8A8)
comp = 2;
else if (comp == PTI_RGB8)
comp = 3;
else if (comp == PTI_RGBA8)
comp = 4;
else if (comp == PTI_RGBA32F)
comp = -4;
else if (comp == PTI_R32F)
comp = -1;
#ifdef STBIW_ONLY_PNG
if (!Q_strcasecmp(ext, "png") && comp>0)
{ //does NOT support pngs
//lacks metadata
outfile=FS_OpenVFS(filename, "wb", FS_GAMEONLY);
ret = stbi_write_png_to_func(stbiwritefunc, outfile, width, height, comp, buffer[0], bytestride);
}
#endif
#ifdef STBIW_ONLY_JPEG
if ((!Q_strcasecmp(ext, "jpg") || !Q_strcasecmp(ext, "jpeg")) && comp>0 && bytestride == width*sizeof(float)*comp)
{
//lacks metadata
outfile=FS_OpenVFS(filename, "wb", FS_GAMEONLY);
ret = stbi_write_jpg_to_func(stbiwritefunc, outfile, width, height, comp, buffer[0], scr_sshot_compression.value/*its in percent*/);
}
#endif
#ifdef STBIW_ONLY_HDR
if (!Q_strcasecmp(ext, "hdr") && comp<0 && bytestride == width*sizeof(float)*-comp)
{
outfile=FS_OpenVFS(filename, "wb", FS_GAMEONLY);
ret = stbi_write_hdr_to_func(stbiwritefunc, outfile, width, height, -comp, buffer[0]);
}
#endif
#ifdef STBIW_ONLY_TGA
if (!Q_strcasecmp(ext, "tga") && comp>0 && bytestride == width*sizeof(float)*comp)
{
outfile=FS_OpenVFS(filename, "wb", FS_GAMEONLY);
ret = stbi_write_tga_to_func(stbiwritefunc, outfile, width, height, comp, buffer[0]);
}
#endif
#ifdef STBIW_ONLY_BMP
if (!Q_strcasecmp(ext, "bmp") && comp>0 && bytestride == width*sizeof(float)*comp)
{
outfile=FS_OpenVFS(filename, "wb", FS_GAMEONLY);
ret = stbi_write_bmp_to_func(stbiwritefunc, outfile, width, height, comp, buffer[0]);
}
#endif
if (outfile)
{
VFS_CLOSE(outfile);
return ret;
}
}
#endif
#ifdef AVAIL_PNGLIB
if (!Q_strcasecmp(ext, "png") || !Q_strcasecmp(ext, "pns"))
{
@ -11078,21 +11775,18 @@ qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer,
//actual stereo is also supported. huzzah.
return Image_WritePNG(filename, fsroot, scr_sshot_compression.value, buffer, numbuffers, bytestride, width, height, fmt, writemeta);
}
else
#endif
#ifdef AVAIL_JPEGLIB
if (!Q_strcasecmp(ext, "jpeg") || !Q_strcasecmp(ext, "jpg") || !Q_strcasecmp(ext, "jps"))
{
return screenshotJPEG(filename, fsroot, scr_sshot_compression.value, buffer[0], bytestride, width, height, fmt, writemeta);
}
else
#endif
#ifdef IMAGEFMT_BMP
if (!Q_strcasecmp(ext, "bmp"))
{
return WriteBMPFile(filename, fsroot, buffer[0], bytestride, width, height, fmt);
}
else
#endif
#ifdef IMAGEFMT_PCX
if (!Q_strcasecmp(ext, "pcx"))
@ -11137,13 +11831,15 @@ qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer,
WritePCXfile (filename, fsroot, dstbuf, width, height, width, host_basepal, false);
free(dstbuf);
return true;
}
else
#endif
#ifdef IMAGEFMT_TGA
if (!Q_strcasecmp(ext, "tga")) //tga
return WriteTGA(filename, fsroot, buffer[0], bytestride, width, height, fmt);
#endif
#ifdef IMAGEFMT_KTX
else if (!Q_strcasecmp(ext, "ktx") && bytestride > 0) //ktx
if (!Q_strcasecmp(ext, "ktx") && bytestride > 0) //ktx
{
struct pendingtextureinfo out = {PTI_2D};
out.encoding = fmt;
@ -11157,7 +11853,7 @@ qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer,
}
#endif
#ifdef IMAGEFMT_DDS
else if (!Q_strcasecmp(ext, "dds") && bytestride > 0) //dds
if (!Q_strcasecmp(ext, "dds") && bytestride > 0) //dds
{
struct pendingtextureinfo out = {PTI_2D};
out.encoding = fmt;
@ -11170,7 +11866,7 @@ qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer,
return Image_WriteDDSFile(filename, fsroot, &out);
}
#endif
else //extension / type not recognised.
return false;
return true;
//extension / type not recognised.
return false;
}

View file

@ -1923,7 +1923,7 @@ void M_Complex_Key(emenu_t *currentmenu, int key, int unicode)
if (currentmenu->key(key, currentmenu))
return;
if (currentmenu->selecteditem && currentmenu->selecteditem->common.type == mt_custom && (key == K_DOWNARROW || key == K_KP_DOWNARROW || key == K_GP_DPAD_DOWN || key == K_GP_A || key == K_GP_B || key == K_UPARROW || key == K_KP_UPARROW || key == K_GP_DPAD_UP || key == K_TAB))
if (currentmenu->selecteditem && currentmenu->selecteditem->common.type == mt_custom && (key == K_DOWNARROW || key == K_KP_DOWNARROW || key == K_GP_DPAD_DOWN || key == K_GP_A || key == K_GP_B || key == K_UPARROW || key == K_KP_UPARROW || key == K_GP_DPAD_UP || key == K_TAB || key == K_MWHEELUP || key == K_MWHEELDOWN))
if (currentmenu->selecteditem->custom.key)
if (currentmenu->selecteditem->custom.key(&currentmenu->selecteditem->custom, currentmenu, key, unicode))
return;

View file

@ -1647,9 +1647,10 @@ struct cin_s
#endif
struct {
qbyte *filmimage; //rgba
int imagewidth;
int imageheight;
qbyte *data;
uploadfmt_t format;
int width;
int height;
} image;
struct {
@ -2276,14 +2277,14 @@ static cin_t *Media_RoQ_TryLoad(char *name)
#ifndef MINIMAL
static void Media_Static_Shutdown(struct cin_s *cin)
{
BZ_Free(cin->image.filmimage);
cin->image.filmimage = NULL;
BZ_Free(cin->image.data);
cin->image.data = NULL;
}
static qboolean Media_Static_DecodeFrame (cin_t *cin, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ctx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ctx)
{
if (forcevideo)
uploadtexture(ctx, TF_RGBA32, cin->image.imagewidth, cin->image.imageheight, cin->image.filmimage, NULL);
uploadtexture(ctx, cin->image.format, cin->image.width, cin->image.height, cin->image.data, NULL);
return true;
}
@ -2313,21 +2314,10 @@ static cin_t *Media_Static_TryLoad(char *name)
return NULL;
}
if ((staticfilmimage = ReadPCXFile(file, fsize, &imagewidth, &imageheight)) || //convert to 32 rgba if not corrupt
(staticfilmimage = ReadTargaFile(file, fsize, &imagewidth, &imageheight, &format, false, PTI_RGBA8)) ||
#ifdef AVAIL_JPEGLIB
(staticfilmimage = ReadJPEGFile(file, fsize, &imagewidth, &imageheight)) ||
#endif
#ifdef AVAIL_PNGLIB
(staticfilmimage = ReadPNGFile(fullname, file, fsize, &imagewidth, &imageheight, &format)) ||
#endif
0)
staticfilmimage = ReadRawImageFile(file, fsize, &imagewidth, &imageheight, &format, false, fullname);
FS_FreeFile(file); //done with the file now
if (!staticfilmimage)
{
FS_FreeFile(file); //got image data
}
else
{
FS_FreeFile(file); //got image data
Con_Printf("Static cinematic format not supported.\n"); //not supported format
return NULL;
}
@ -2336,9 +2326,10 @@ static cin_t *Media_Static_TryLoad(char *name)
cin->decodeframe = Media_Static_DecodeFrame;
cin->shutdown = Media_Static_Shutdown;
cin->image.filmimage = staticfilmimage;
cin->image.imagewidth = imagewidth;
cin->image.imageheight = imageheight;
cin->image.data = staticfilmimage;
cin->image.format = format;
cin->image.width = imagewidth;
cin->image.height = imageheight;
return cin;
}

View file

@ -880,6 +880,7 @@ const char *presetexec[] =
"seta cl_fullpitch 1;seta maxpitch \"\";seta minpitch \"\";" //mimic quakespasm where possible.
"seta r_graphics 1;"
"seta r_renderscale 1;"
"seta gl_texture_anisotropic_filtering 0;"
, // fast options (for deathmatch)
"gl_texturemode ln;"

View file

@ -131,25 +131,26 @@ void Menu_Push(menu_t *menu, qboolean prompt)
}
void Menu_PopAll(void)
{
qboolean popped = false;
menu_t **link, *menu;
for (link = &topmenu; *link;)
menu_t **menus, *menu;
size_t count, i;
//first loop to count them
for (count = 0, menu = topmenu; menu; menu = menu->prev)
{
menu = *link;
if (menu->persist)
link = &(*link)->prev;
else
{
*link = menu->prev;
menu->prev = NULL;
if (menu->release)
menu->release(menu);
popped = true;
}
continue;
count++;
}
if (popped)
Menu_UpdateFocus();
menus = alloca(sizeof(*menus)*count);
//second loop to track them
for (i = 0, menu = topmenu; i < count && menu; menu = menu->prev)
{
if (menu->persist)
continue;
menus[i++] = menu;
}
//third link to actually unlink them safely without unlinking multiple times etc (grr menuqc mods re-grabbing focus when closing)
for (i = 0; i < count; i++)
Menu_Unlink(menus[i]);
}
void Menu_Draw(void)

View file

@ -131,6 +131,7 @@ extern void (*VID_DeInit) (void);
extern char *(*VID_GetRGBInfo) (int *stride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt); //if stride is negative, then the return value points to the last line intead of the first. this allows it to be freed normally.
extern void (*VID_SetWindowCaption) (const char *msg);
extern void *SCR_ScreenShot_Capture (int fbwidth, int fbheight, int *stride, enum uploadfmt *fmt, qboolean no2d);
extern void SCR_Init (void);
extern void SCR_DeInit (void);
extern qboolean (*SCR_UpdateScreen) (void);

View file

@ -7082,7 +7082,7 @@ void CSQC_Shutdown(void)
PR_ReleaseFonts(kdm_game);
PR_Common_Shutdown(csqcprogs, false);
World_Destroy(&csqc_world);
csqcprogs->CloseProgs(csqcprogs);
csqcprogs->Shutdown(csqcprogs);
csqc_world.progs = csqcprogs = NULL;
}
else

View file

@ -1465,14 +1465,15 @@ void QCBUILTIN PF_isdemo (pubprogfuncs_t *prinst, struct globalvars_s *pr_global
//float clientstate(void) = #62;
void QCBUILTIN PF_clientstate (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{
if (isDedicated)
G_FLOAT(OFS_RETURN) = 0;
//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)
|| sv_state>=ss_loading ) //or we're going to connect to ourselves once we get our act together
G_FLOAT(OFS_RETURN) = 2/*nq ca_connected*/;
else
{
//fit in with netquake (we never run a menu.dat dedicated)
//we return 2 for trying to connect, to avoid bugs with certain menuqc mods
G_FLOAT(OFS_RETURN) = (cls.state >= ca_connected||CL_TryingToConnect()) ? 2 : 1;
}
G_FLOAT(OFS_RETURN) = 1/*nq ca_disconnected*/;
}
//too specific to the prinst's builtins.
@ -2688,7 +2689,7 @@ void MP_Shutdown (void)
PR_ExecuteProgram(menu_world.progs, temp);
PR_Common_Shutdown(menu_world.progs, false);
menu_world.progs->CloseProgs(menu_world.progs);
menu_world.progs->Shutdown(menu_world.progs);
memset(&menu_world, 0, sizeof(menu_world));
PR_ReleaseFonts(kdm_menu);
@ -2857,7 +2858,6 @@ qboolean MP_Init (void)
menuqc.keyevent = MP_KeyEvent;
menuqc.joyaxis = MP_JoystickAxis;
menuqc.release = MP_TryRelease;
menuqc.persist = true; //don't bother trying to kill it...
menutime = Sys_DoubleTime();
if (!menu_world.progs)

View file

@ -359,7 +359,7 @@ void COM_InsertWork(wgroup_t tg, void(*func)(void *ctx, void *data, size_t a, si
qboolean COM_HasWork(void);
void COM_WorkerFullSync(void);
void COM_DestroyWorkerThread(void);
void COM_WorkerPartialSync(void *priorityctx, int *address, int value);
void COM_WorkerPartialSync(void *priorityctx, int *address, int value); //aka: while(*address==value)wait();
extern void *com_resourcemutex; //random mutex to simplify resource creation type stuff.
void COM_WorkerAbort(char *message); //calls sys_error on the main thread, if running on a worker.
#ifdef _DEBUG

View file

@ -425,7 +425,7 @@ enum imageflags
IF_CUBEMAP = 1<<11, /*waning - don't test directly*/
IF_2DARRAY = IF_3DMAP|IF_CUBEMAP,
IF_TEXTYPE = (1<<10) | (1<<11), /*0=2d, 1=3d, 2=cubeface, 3=2d array texture*/
IF_TEXTYPESHIFT = 10, /*0=2d, 1=3d, 2-7=cubeface*/
IF_TEXTYPESHIFT = 10, /*0=2d, 1=3d, 2=cubeface, 3=array*/
IF_MIPCAP = 1<<12, //allow the use of d_mipcap
IF_PREMULTIPLYALPHA = 1<<13, //rgb *= alpha
@ -468,6 +468,7 @@ void Image_Upload (texid_t tex, uploadfmt_t fmt, void *data, void *palette, in
void Image_Purge(void); //purge any textures which are not needed any more (releases memory, but doesn't give null pointers).
void Image_Init(void);
void Image_Shutdown(void);
void Image_PrintInputFormatVersions(void); //for version info
qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struct pendingtextureinfo *mips);
qboolean Image_WriteDDSFile(const char *filename, enum fs_relative fsroot, struct pendingtextureinfo *mips);
void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes, unsigned int *blockwidth, unsigned int *blockheight);

View file

@ -729,13 +729,6 @@ void R_ToggleFullscreen_f(void)
void Renderer_Init(void)
{
#ifdef AVAIL_JPEGLIB
LibJPEG_Init();
#endif
#ifdef AVAIL_PNGLIB
LibPNG_Init();
#endif
currentrendererstate.renderer = NULL;
qrenderer = QR_NONE;
@ -843,8 +836,12 @@ void Renderer_Init(void)
Cmd_AddCommand("sky", R_ForceSky_f); //QS compat
Cmd_AddCommand("loadsky", R_ForceSky_f);//DP compat
#ifdef IMAGEFMT_TGA
Cvar_Register(&r_dodgytgafiles, "Hacky bug workarounds");
#endif
#ifdef IMAGEFMT_PCX
Cvar_Register(&r_dodgypcxfiles, "Hacky bug workarounds");
#endif
Cvar_Register(&r_dodgymiptex, "Hacky bug workarounds");
r_imageextensions.enginevalue = r_defaultimageextensions;
Cvar_Register(&r_imageextensions, GRAPHICALNICETIES);

View file

@ -99,6 +99,8 @@ typedef enum uploadfmt
PTI_BGRX8_SRGB, //no alpha channel
PTI_RGB8, //24bit packed format. generally not supported
PTI_BGR8, //24bit packed format. generally not supported
PTI_RGB8_SRGB, //24bit packed format. generally not supported
PTI_BGR8_SRGB, //24bit packed format. generally not supported
PTI_L8, //8bit format. luminance gets flooded to all RGB channels. might be supported using swizzles.
PTI_L8A8, //16bit format. L=luminance. might be supported using swizzles.
PTI_L8_SRGB, //8bit format. luminance gets flooded to all RGB channels. might be supported using swizzles.

View file

@ -706,7 +706,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//I'm making my own restrict, because msvc's headers can't cope if I #define restrict to __restrict, and quite possibly other platforms too
#if __STDC_VERSION__ >= 199901L
#define fte_restrict restrict
#elif defined(_MSC_VER) && _MSC_VER >= 1400
#elif defined(_MSC_VER) && _MSC_VER >= 1400 || __GNUC__ >= 4
#define fte_restrict __restrict
#else
#define fte_restrict
@ -748,7 +748,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//gcc will generally inline where it can - so long as its static. but that doesn't stop it warning
#define fte_inline __attribute__((unused)) static
#define fte_inlinebody static
#define fte_inlinestatic static
#if __GNUC__ > 5
#define fte_inlinestatic static inline
#else
#define fte_inlinestatic static
#endif
#else
//make it static so we at least don't get errors (might still get warnings. see above)
#define fte_inline static

View file

@ -2117,7 +2117,7 @@ const char *COM_GetFileExtension (const char *in, const char *term)
if (!term)
term = in + strlen(in);
for (dot = term; dot >= in && *dot != '/' && *dot != '\\'; dot--)
for (dot = term-1; dot >= in && *dot != '/' && *dot != '\\'; dot--)
{
if (*dot == '.')
return dot;
@ -4975,53 +4975,7 @@ static void COM_Version_f (void)
#endif
#ifdef HAVE_CLIENT
Con_Printf("^3Image Formats:^7");
#ifdef IMAGEFMT_DDS
Con_Printf(" dds");
#endif
#ifdef IMAGEFMT_KTX
Con_Printf(" ktx");
#endif
Con_Printf(" tga");
#if defined(AVAIL_PNGLIB)
Con_Printf(" png");
#ifdef DYNAMIC_LIBPNG
Con_Printf("^h(dynamic, %s)", PNG_LIBPNG_VER_STRING);
#else
Con_Printf("^h(%s)", PNG_LIBPNG_VER_STRING);
#endif
#else
Con_DPrintf(" ^h(disabled: png)");
#endif
#ifdef IMAGEFMT_BMP
Con_Printf(" bmp+ico");
#endif
#if defined(AVAIL_JPEGLIB)
Con_Printf(" jpeg");
#ifdef DYNAMIC_LIBJPEG
Con_Printf("^h(dynamic, %i, %d series)", JPEG_LIB_VERSION, ( JPEG_LIB_VERSION / 10 ) );
#else
Con_Printf("^h(%i, %d series)", JPEG_LIB_VERSION, ( JPEG_LIB_VERSION / 10 ) );
#endif
#else
Con_DPrintf(" ^h(disabled: jpeg)");
#endif
#ifdef IMAGEFMT_PBM
Con_Printf(" pfm+pbm+pgm+ppm"/*"+pam"*/);
#endif
#ifdef IMAGEFMT_PSD
Con_Printf(" psd");
#endif
#ifdef IMAGEFMT_HDR
Con_Printf(" hdr");
#endif
#ifdef IMAGEFMT_PKM
Con_Printf(" pkm");
#endif
#ifdef IMAGEFMT_PCX
Con_Printf(" pcx");
#endif
Con_Printf("\n");
Image_PrintInputFormatVersions();
Con_Printf("^3VoiceChat:^7");
#if !defined(VOICECHAT)

View file

@ -156,7 +156,7 @@
#define HAVE_MEDIA_ENCODER //capture/capturedemo work.
#undef HAVE_SPEECHTOTEXT //windows speech-to-text thing
//FIXME
//FIXME: Stuff that Spike has added that Eukara needs to decide whether to keep or not.
#define HAVE_OPUS
//#define HAVE_SPEEX
//#define HAVE_OPENSSL
@ -174,6 +174,18 @@
//#define USE_INTERNAL_ODE
//#define USE_INTERNAL_BULLET
//#define MENU_NATIVECODE
//#define DECOMPRESS_ASTC
//#define HAVE_HTTPSV
//#define IMAGEFMT_ASTC
//#define IMAGEFMT_JPG
//#define IMAGEFMT_GIF
//#define IMAGEFMT_PNG
#define IMAGEFMT_TGA
#define IMAGEFMT_LMP
//#define IMAGEFMT_EXR //openexr, via Industrial Light & Magic's rgba api, giving half-float data.
//#define MODELFMT_MDX
//#define MODELFMT_OBJ
//#define AVAIL_STBI //make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies.
#ifdef COMPILE_OPTS

View file

@ -95,12 +95,19 @@
#define IMAGEFMT_PSD //baselayer only.
#define IMAGEFMT_HDR //an RGBE format.
#define IMAGEFMT_DDS //.dds files embed mipmaps and texture compression. faster to load.
#define IMAGEFMT_TGA //somewhat mandatory
#define IMAGEFMT_LMP //mandatory for quake
#define IMAGEFMT_PNG //common in quakeworld, useful for screenshots.
#define IMAGEFMT_JPG //common in quake3, useful for screenshots.
//#define IMAGEFMT_GIF //for the luls. loads as a texture2DArray
//#define IMAGEFMT_BLP //legacy crap
#define IMAGEFMT_BMP //windows bmp. yuck. also includes .ico for the luls
#define IMAGEFMT_PCX //paletted junk. required for qw player skins, q2 and a few old skyboxes.
#define IMAGEFMT_EXR //openexr, via Industrial Light & Magic's rgba api, giving half-float data.
//#define IMAGEFMT_VTF //hl2 image format
#define AVAIL_PNGLIB //.png image format support (read+screenshots)
#define AVAIL_JPEGLIB //.jpeg image format support (read+screenshots)
//#define AVAIL_STBI //make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies.
#define PACKAGE_TEXWAD //quake's image wad support
#define AVAIL_FREETYPE //for truetype font rendering
#define DECOMPRESS_ETC2 //decompress etc2(core in gles3/gl4.3) if the graphics driver doesn't support it (eg d3d or crappy gpus with vulkan).

View file

@ -85,27 +85,38 @@
//#define PSKMODELS //unreal's interchange format. Undesirable in terms of load times.
//#define HALFLIFEMODELS //horrible format that doesn't interact well with the rest of FTE's code. Unusable tools (due to license reasons).
//#define INTERQUAKEMODELS //Preferred model format, at least from an idealism perspective.
//#define MODELFMT_MDX //kingpin's format (for hitboxes+geomsets).
//#define MODELFMT_OBJ //lame mesh-only format that needs far too much processing and even lacks a proper magic identifier too
//#define RAGDOLL //ragdoll support. requires RBE support (via a plugin...).
//Image formats
//#define IMAGEFMT_KTX //Khronos TeXture. common on gles3 devices for etc2 compression
//#define IMAGEFMT_PKM //file format generally written by etcpack or android's etc1tool. doesn't support mips.
//#define IMAGEFMT_ASTC //lame simple header around a single astc image. not needed for astc in ktx files etc. its better to use ktx files.
//#define IMAGEFMT_PBM //pbm/ppm/pgm/pfm family formats.
//#define IMAGEFMT_PSD //baselayer only.
//#define IMAGEFMT_HDR //an RGBE format.
//#define IMAGEFMT_DDS //.dds files embed mipmaps and texture compression. faster to load.
#define IMAGEFMT_TGA //somewhat mandatory
#define IMAGEFMT_LMP //mandatory for quake
#define IMAGEFMT_PNG //common in quakeworld, useful for screenshots.
//#define IMAGEFMT_JPG //common in quake3, useful for screenshots.
//#define IMAGEFMT_GIF //for the luls. loads as a texture2DArray
//#define IMAGEFMT_BLP //legacy crap
//#define IMAGEFMT_BMP //windows bmp. yuck. also includes .ico for the luls
//#define IMAGEFMT_PCX //paletted junk. required for qw player skins, q2 and a few old skyboxes.
//#define IMAGEFMT_EXR //openexr, via Industrial Light & Magic's rgba api, giving half-float data.
//#define IMAGEFMT_VTF //hl2 image format
#define AVAIL_PNGLIB //.png image format support (read+screenshots)
//#define AVAIL_JPEGLIB //.jpeg image format support (read+screenshots)
//#define AVAIL_STBI //make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies.
#define PACKAGE_TEXWAD //quake's image wad support
//#define AVAIL_FREETYPE //for truetype font rendering
//#define DECOMPRESS_ETC2 //decompress etc2(core in gles3/gl4.3) if the graphics driver doesn't support it (eg d3d or crappy gpus with vulkan).
//#define DECOMPRESS_S3TC //allows bc1-3 to work even when drivers don't support it. This is probably only an issue on mobile chips. WARNING: not entirely sure if all patents expired yet...
//#define DECOMPRESS_RGTC //bc4+bc5
//#define DECOMPRESS_BPTC //bc6+bc7
//#define DECOMPRESS_ASTC //ASTC, for drivers that don't support it properly.
// Game/Gamecode Support
//#define CSQC_DAT
@ -140,6 +151,7 @@
//#define HAVE_WINSSPI //on windows
//#define FTPSERVER //sv_ftp cvar.
//#define WEBCLIENT //uri_get+any internal downloads etc
#define HAVE_HTTPSV //net_enable_http/websocket
//#define TCPCONNECT //support for playing over tcp sockets, instead of just udp. compatible with qizmo.
//#define IRCCONNECT //lame support for routing game packets via irc server. not a good idea.
//#define SUPPORT_ICE //Internet Connectivity Establishment, for use by plugins to establish voice or game connections.

View file

@ -83,27 +83,38 @@
//#define PSKMODELS //unreal's interchange format. Undesirable in terms of load times.
//#define HALFLIFEMODELS //horrible format that doesn't interact well with the rest of FTE's code. Unusable tools (due to license reasons).
#define INTERQUAKEMODELS //Preferred model format, at least from an idealism perspective.
//#define MODELFMT_MDX //kingpin's format (for hitboxes+geomsets).
//#define MODELFMT_OBJ //lame mesh-only format that needs far too much processing and even lacks a proper magic identifier too
#define RAGDOLL //ragdoll support. requires RBE support (via a plugin...).
//Image formats
#define IMAGEFMT_KTX //Khronos TeXture. common on gles3 devices for etc2 compression
//#define IMAGEFMT_PKM //file format generally written by etcpack or android's etc1tool. doesn't support mips.
//#define IMAGEFMT_ASTC //lame simple header around a single astc image. not needed for astc in ktx files etc. its better to use ktx files.
//#define IMAGEFMT_PBM //pbm/ppm/pgm/pfm family formats.
//#define IMAGEFMT_PSD //baselayer only.
//#define IMAGEFMT_HDR //an RGBE format.
#define IMAGEFMT_DDS //.dds files embed mipmaps and texture compression. faster to load.
#define IMAGEFMT_TGA //somewhat mandatory
#define IMAGEFMT_LMP //mandatory for quake
#define IMAGEFMT_PNG //common in quakeworld, useful for screenshots.
//#define IMAGEFMT_JPG //common in quake3, useful for screenshots.
//#define IMAGEFMT_GIF //for the luls. loads as a texture2DArray
//#define IMAGEFMT_BLP //legacy crap
//#define IMAGEFMT_BMP //windows bmp. yuck. also includes .ico for the luls
//#define IMAGEFMT_PCX //paletted junk. required for qw player skins, q2 and a few old skyboxes.
//#define IMAGEFMT_EXR //openexr, via Industrial Light & Magic's rgba api, giving half-float data.
#define IMAGEFMT_VTF //hl2 image format
#define AVAIL_PNGLIB //.png image format support (read+screenshots)
//#define AVAIL_JPEGLIB //.jpeg image format support (read+screenshots)
//#define AVAIL_STBI //make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies.
//#define PACKAGE_TEXWAD //quake's image wad support
#define AVAIL_FREETYPE //for truetype font rendering
#define DECOMPRESS_ETC2 //decompress etc2(core in gles3/gl4.3) if the graphics driver doesn't support it (eg d3d or crappy gpus with vulkan).
//#define DECOMPRESS_S3TC //allows bc1-3 to work even when drivers don't support it. This is probably only an issue on mobile chips. WARNING: not entirely sure if all patents expired yet...
//#define DECOMPRESS_RGTC //bc4+bc5
//#define DECOMPRESS_BPTC //bc6+bc7
//#define DECOMPRESS_ASTC //ASTC, for drivers that don't support it properly.
// Game/Gamecode Support
#define CSQC_DAT
@ -138,6 +149,7 @@
#define HAVE_WINSSPI //on windows
//#define FTPSERVER //sv_ftp cvar.
#define WEBCLIENT //uri_get+any internal downloads etc
#define HAVE_HTTPSV //net_enable_http/websocket
//#define TCPCONNECT //support for playing over tcp sockets, instead of just udp. compatible with qizmo.
//#define IRCCONNECT //lame support for routing game packets via irc server. not a good idea.
#define SUPPORT_ICE //Internet Connectivity Establishment, for use by plugins to establish voice or game connections.

View file

@ -172,15 +172,27 @@
#undef AVAIL_FREETYPE // for truetype font rendering
#undef SERVER_DEMO_PLAYBACK //outdated crap
//FIXME
//FIXME: Stuff that Spike has added that eukara needs to decide whether to use or not.
#define HAVE_OPUS
//#define HAVE_OPENSSL
//#define IMAGEFMT_HDR
//#define IMAGEFMT_PBM
//#define IMAGEFMT_PSD
#define IMAGEFMT_TGA
#define IMAGEFMT_LMP
//#define IMAGEFMT_PNG
//#define IMAGEFMT_JPG
//#define IMAGEFMT_GIF
//#define IMAGEFMT_EXR //openexr, via Industrial Light & Magic's rgba api, giving half-float data.
//#define AVAIL_STBI //make use of Sean T. Barrett's lightweight public domain stb_image[_write] single-file-library, to avoid libpng/libjpeg dependancies.
//#define IPLOG
//#define AVAIL_BOTLIB
//#define AVAIL_BZLIB
//#define DECOMPRESS_ASTC
//#define IMAGEFMT_ASTC
//#define HAVE_HTTPSV
//#define MODELFMT_MDX
//#define MODELFMT_OBJ
#ifdef COMPILE_OPTS
//things to configure qclib, which annoyingly doesn't include this file itself

View file

@ -784,6 +784,7 @@ COM_Dir_f
static int QDECL COM_Dir_List(const char *name, qofs_t size, time_t mtime, void *parm, searchpathfuncs_t *spath)
{
searchpath_t *s;
const char *ext;
char link[512];
char *colour;
flocation_t loc;
@ -806,43 +807,56 @@ static int QDECL COM_Dir_List(const char *name, qofs_t size, time_t mtime, void
else if (loc.search->handle == spath)
{
colour = "^2";
COM_FileExtension(name, link, sizeof(link));
if ((!Q_strcasecmp(link, "bsp") || !Q_strcasecmp(link, "map") || !Q_strcasecmp(link, "hmp")) && !strncmp(name, "maps/", 5) && strncmp(name, "maps/b_", 7))
ext = COM_GetFileExtension(name, NULL);
if (!Q_strcasecmp(ext, ".gz"))
ext = COM_GetFileExtension(name, ext);
if (*ext == '.')
{
ext++;
if (strchr(ext, '.'))
{
COM_StripAllExtensions(ext, link, sizeof(link));
ext = link;
}
}
if ((!Q_strcasecmp(ext, "bsp") || !Q_strcasecmp(ext, "map") || !Q_strcasecmp(ext, "hmp")) && !strncmp(name, "maps/", 5) && strncmp(name, "maps/b_", 7))
{
Q_snprintfz(link, sizeof(link), "\\tip\\Change Map\\map\\%s", name+5);
colour = "^4"; //disconnects
}
else if (!Q_strcasecmp(link, "bsp") || !Q_strcasecmp(link, "spr") || !Q_strcasecmp(link, "mdl") || !Q_strcasecmp(link, "md3") || !Q_strcasecmp(link, "iqm") ||
!Q_strcasecmp(link, "vvm") || !Q_strcasecmp(link, "psk") || !Q_strcasecmp(link, "dpm") || !Q_strcasecmp(link, "zym") || !Q_strcasecmp(link, "md5mesh") ||
!Q_strcasecmp(link, "mdx") || !Q_strcasecmp(link, "md2") || !Q_strcasecmp(link, "obj") ||
!Q_strcasecmp(link, "md5anim") || !Q_strcasecmp(link, "gltf") || !Q_strcasecmp(link, "glb") || !Q_strcasecmp(link, "ase") || !Q_strcasecmp(link, "lwo"))
else if (!Q_strcasecmp(ext, "bsp") || !Q_strcasecmp(ext, "spr") || !Q_strcasecmp(ext, "mdl") || !Q_strcasecmp(ext, "md3") || !Q_strcasecmp(ext, "iqm") ||
!Q_strcasecmp(ext, "vvm") || !Q_strcasecmp(ext, "psk") || !Q_strcasecmp(ext, "dpm") || !Q_strcasecmp(ext, "zym") || !Q_strcasecmp(ext, "md5mesh") ||
!Q_strcasecmp(ext, "mdx") || !Q_strcasecmp(ext, "md2") || !Q_strcasecmp(ext, "obj") ||
!Q_strcasecmp(ext, "md5anim") || !Q_strcasecmp(ext, "gltf") || !Q_strcasecmp(ext, "glb") || !Q_strcasecmp(ext, "ase") || !Q_strcasecmp(ext, "lwo"))
Q_snprintfz(link, sizeof(link), "\\tip\\Open in Model Viewer\\modelviewer\\%s", name);
else if (!Q_strcasecmp(link, "qc") || !Q_strcasecmp(link, "src") || !Q_strcasecmp(link, "qh") || !Q_strcasecmp(link, "h") || !Q_strcasecmp(link, "c")
|| !Q_strcasecmp(link, "cfg") || !Q_strcasecmp(link, "rc")
|| !Q_strcasecmp(link, "txt") || !Q_strcasecmp(link, "log")
|| !Q_strcasecmp(link, "ent") || !Q_strcasecmp(link, "rtlights")
|| !Q_strcasecmp(link, "glsl") || !Q_strcasecmp(link, "hlsl")
|| !Q_strcasecmp(link, "shader") || !Q_strcasecmp(link, "framegroups")
|| !Q_strcasecmp(link, "vmt")
else if (!Q_strcasecmp(ext, "qc") || !Q_strcasecmp(ext, "src") || !Q_strcasecmp(ext, "qh") || !Q_strcasecmp(ext, "h") || !Q_strcasecmp(ext, "c")
|| !Q_strcasecmp(ext, "cfg") || !Q_strcasecmp(ext, "rc")
|| !Q_strcasecmp(ext, "txt") || !Q_strcasecmp(ext, "log")
|| !Q_strcasecmp(ext, "ent") || !Q_strcasecmp(ext, "rtlights")
|| !Q_strcasecmp(ext, "glsl") || !Q_strcasecmp(ext, "hlsl")
|| !Q_strcasecmp(ext, "shader") || !Q_strcasecmp(ext, "framegroups")
|| !Q_strcasecmp(ext, "vmt")
)
Q_snprintfz(link, sizeof(link), "\\tip\\Open in Text Editor\\edit\\%s", name);
else if (!Q_strcasecmp(link, "tga") || !Q_strcasecmp(link, "png") || !Q_strcasecmp(link, "jpg") || !Q_strcasecmp(link, "jpeg")|| !Q_strcasecmp(link, "lmp") || !Q_strcasecmp(link, "ico") ||
!Q_strcasecmp(link, "pcx") || !Q_strcasecmp(link, "bmp") || !Q_strcasecmp(link, "dds") || !Q_strcasecmp(link, "ktx") || !Q_strcasecmp(link, "vtf") || !Q_strcasecmp(link, "psd") ||
!Q_strcasecmp(link, "astc")|| !Q_strcasecmp(link, "htga")||
!Q_strcasecmp(link, "pbm") || !Q_strcasecmp(link, "ppm") || !Q_strcasecmp(link, "pgm") || !Q_strcasecmp(link, "pam") || !Q_strcasecmp(link, "pfm") || !Q_strcasecmp(link, "hdr") )
else if (!Q_strcasecmp(ext, "tga") || !Q_strcasecmp(ext, "png") || !Q_strcasecmp(ext, "jpg") || !Q_strcasecmp(ext, "jpeg")|| !Q_strcasecmp(ext, "lmp") || !Q_strcasecmp(ext, "ico") ||
!Q_strcasecmp(ext, "pcx") || !Q_strcasecmp(ext, "bmp") || !Q_strcasecmp(ext, "dds") || !Q_strcasecmp(ext, "ktx") || !Q_strcasecmp(ext, "vtf") || !Q_strcasecmp(ext, "psd") ||
!Q_strcasecmp(ext, "astc")|| !Q_strcasecmp(ext, "htga")|| !Q_strcasecmp(ext, "exr") ||
!Q_strcasecmp(ext, "pbm") || !Q_strcasecmp(ext, "ppm") || !Q_strcasecmp(ext, "pgm") || !Q_strcasecmp(ext, "pam") || !Q_strcasecmp(ext, "pfm") || !Q_strcasecmp(ext, "hdr") )
{
//FIXME: image replacements are getting in the way here.
Q_snprintfz(link, sizeof(link), "\\tiprawimg\\%s\\tip\\(note: image replacement rules are context-dependant, including base path, sub path, extension, or complete replacement via a shader)", name);
colour = "^6"; //shown on mouseover
}
else if (!Q_strcasecmp(link, "qwd") || !Q_strcasecmp(link, "dem") || !Q_strcasecmp(link, "mvd") || !Q_strcasecmp(link, "dm2"))
else if (!Q_strcasecmp(ext, "qwd") || !Q_strcasecmp(ext, "dem") || !Q_strcasecmp(ext, "mvd") || !Q_strcasecmp(ext, "dm2"))
{
Q_snprintfz(link, sizeof(link), "\\tip\\Play Demo\\demo\\%s", name);
colour = "^4"; //disconnects
}
else if (!Q_strcasecmp(link, "roq") || !Q_strcasecmp(link, "cin") || !Q_strcasecmp(link, "avi") || !Q_strcasecmp(link, "mp4") || !Q_strcasecmp(link, "mkv"))
else if (!Q_strcasecmp(ext, "roq") || !Q_strcasecmp(ext, "cin") || !Q_strcasecmp(ext, "avi") || !Q_strcasecmp(ext, "mp4") || !Q_strcasecmp(ext, "mkv"))
Q_snprintfz(link, sizeof(link), "\\tip\\Play Film\\film\\%s", name);
else if (!Q_strcasecmp(link, "wav") || !Q_strcasecmp(link, "ogg") || !Q_strcasecmp(link, "mp3") || !Q_strcasecmp(link, "opus") || !Q_strcasecmp(link, "flac"))
else if (!Q_strcasecmp(ext, "wav") || !Q_strcasecmp(ext, "ogg") || !Q_strcasecmp(ext, "mp3") || !Q_strcasecmp(ext, "opus") || !Q_strcasecmp(ext, "flac"))
Q_snprintfz(link, sizeof(link), "\\tip\\Play Audio\\playaudio\\%s", name);
else
{

View file

@ -589,15 +589,15 @@ int PR_DPrintf (const char *fmt, ...)
string_t PR_TempString(pubprogfuncs_t *prinst, const char *str)
{
char *tmp;
if (!prinst->tempstringbase)
if (!prinst->user.tempstringbase)
return prinst->TempString(prinst, str);
if (!str || !*str)
return 0;
if (prinst->tempstringnum == MAX_TEMPSTRS)
prinst->tempstringnum = 0;
tmp = prinst->tempstringbase + (prinst->tempstringnum++)*MAXTEMPBUFFERLEN;
if (prinst->user.tempstringnum == MAX_TEMPSTRS)
prinst->user.tempstringnum = 0;
tmp = prinst->user.tempstringbase + (prinst->user.tempstringnum++)*MAXTEMPBUFFERLEN;
Q_strncpyz(tmp, str, MAXTEMPBUFFERLEN);
return tmp - prinst->stringtable;
@ -613,10 +613,10 @@ void PF_InitTempStrings(pubprogfuncs_t *prinst)
pr_tempstringsize.flags |= CVAR_NOSET;
if (pr_tempstringcount.value >= 2)
prinst->tempstringbase = prinst->AddString(prinst, "", MAXTEMPBUFFERLEN*MAX_TEMPSTRS, false);
prinst->user.tempstringbase = prinst->AddString(prinst, "", MAXTEMPBUFFERLEN*MAX_TEMPSTRS, false);
else
prinst->tempstringbase = 0;
prinst->tempstringnum = 0;
prinst->user.tempstringbase = 0;
prinst->user.tempstringnum = 0;
}
//#define RETURN_EDICT(pf, e) (((int *)pr_globals)[OFS_RETURN] = EDICT_TO_PROG(pf, e))
@ -2925,7 +2925,7 @@ void QCBUILTIN PF_edict_for_num(pubprogfuncs_t *prinst, struct globalvars_s *pr_
else
{
G_INT(OFS_RETURN) = num; //just directly store it. if its not spawned yet we'll need to catch that elsewhere anyway.
if (G_WEDICT(prinst, OFS_RETURN))
if (!G_WEDICT(prinst, OFS_RETURN))
RETURN_EDICT(prinst, w->edicts); //hoi! it wasn't valid!
}
}

View file

@ -376,7 +376,7 @@ void Q23BSP_FindTouchedLeafs(model_t *mod, struct pvscache_s *ent, const float *
/*sv_move.c*/
#if defined(CSQC_DAT) || !defined(CLIENTONLY)
qboolean World_CheckBottom (world_t *world, wedict_t *ent, vec3_t up);
qboolean World_movestep (world_t *world, wedict_t *ent, vec3_t move, vec3_t axis[3], qboolean relink, qboolean noenemy, void (*set_move_trace)(pubprogfuncs_t *prinst, trace_t *trace));
qboolean World_movestep (world_t *world, wedict_t *ent, vec3_t move, vec3_t axis[3], qboolean relink, qboolean noenemy, void (*set_move_trace)(pubprogfuncs_t *inst, trace_t *trace));
qboolean World_MoveToGoal (world_t *world, wedict_t *ent, float dist);
qboolean World_GetEntGravityAxis(wedict_t *ent, vec3_t axis[3]);
#endif

View file

@ -197,6 +197,12 @@ qboolean D3D11_LoadTextureMips(image_t *tex, const struct pendingtextureinfo *mi
case PTI_BGR8:
// tdesc.Format = DXGI_FORMAT_B8G8R8_UNORM;
break;
case PTI_RGB8_SRGB:
// tdesc.Format = DXGI_FORMAT_R8G8B8_SRGB;
break;
case PTI_BGR8_SRGB:
// tdesc.Format = DXGI_FORMAT_B8G8R8_SRGB;
break;
case PTI_RGBA8:
tdesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
break;

View file

@ -1753,11 +1753,10 @@ static texid_t Font_LoadFallbackConchars(void)
texid_t tex;
int width, height;
unsigned int i;
qbyte *lump;
uploadfmt_t format;
lump = ReadTargaFile(default_conchar, sizeof(default_conchar), &width, &height, &format, false, PTI_INVALID);
qbyte *lump = ReadRawImageFile(default_conchar, sizeof(default_conchar), &width, &height, &format, false, "conchars");
if (!lump || (format != PTI_RGBX8 && format != PTI_RGBA8 && format != PTI_LLLX8))
Sys_Error("Corrupt internal drawchars (%i)", format);
return r_nulltex;
/*convert greyscale to alpha*/
for (i = 0; i < width*height; i++)
{

View file

@ -736,6 +736,11 @@ static int Shader_SetImageFlags(parsestate_t *parsestate, shaderpass_t *pass, ch
*name+=6;
flags = (flags&~IF_TEXTYPE) | IF_CUBEMAP;
}
else if (!Q_strnicmp(*name, "$2darray:", 9))
{
*name+=9;
flags = (flags&~IF_TEXTYPE) | IF_2DARRAY;
}
else if (!Q_strnicmp(*name, "$srgb:", 6))
{
*name+=6;

View file

@ -2484,8 +2484,6 @@ static void install_grabs(void)
{
if (!mouse_grabbed)
{
Con_DLPrintf(2, "Grabbing mouse\n");
mouse_grabbed = true;
//XGrabPointer can cause alt+tab type shortcuts to be skipped by the window manager. This means we don't want to use it unless we have no choice.
//the grab is purely to constrain the pointer to the window
if (GrabSuccess != x11.pXGrabPointer(vid_dpy, DefaultRootWindow(vid_dpy),
@ -2495,7 +2493,13 @@ static void install_grabs(void)
vid_window,
None,
CurrentTime))
{
Con_Printf("Pointer grab failed\n");
return;
}
Con_DLPrintf(2, "Grabbed mouse\n");
mouse_grabbed = true;
if (x11_input_method == XIM_DGA)
{
@ -2637,7 +2641,7 @@ static void GetEvent(void)
default:button = 0; break;
}
if (button)
IN_KeyEvent(*qdev, (event.xcookie.evtype==XI_RawButtonPress), button, 0);
IN_KeyEvent(*qdev, (event.xcookie.evtype==XI_RawButtonPress), button, 0);
}
break;
case XI_RawMotion:

View file

@ -2958,6 +2958,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"!!cvardf r_tessellation_level=5\n"
"!!samps !EIGHTBIT diffuse normalmap specular fullbright upper lower reflectmask reflectcube\n"
"!!samps =EIGHTBIT paletted 1\n"
"!!samps =OCCLUDE occlusion\n"
//!!permu VC // adds rgba vertex colour multipliers
//!!permu SPECULAR // auto-added when gl_specular>0
//!!permu OFFSETMAPPING // auto-added when r_glsl_offsetmapping is set
@ -2966,6 +2967,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
//!!permu SG // specularmap is rgb:F0, a:Roughness (instead of exponent)
//!!permu PBR // an attempt at pbr logic (enabled from ORM or SG)
//!!permu NOOCCLUDE // ignores the use of ORM's occlusion... yeah, stupid.
//!!permu OCCLUDE // use an explicit occlusion texturemap (separate from roughness+metalness).
//!!permu EIGHTBIT // uses software-style paletted colourmap lookups
//!!permu ALPHATEST // if defined, this is the required alpha level (more versatile than doing it at the q3shader level)
@ -3274,12 +3276,12 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"#define ambientrgb (specrgb+col.rgb)\n"
"vec3 specrgb = mix(vec3(dielectricSpecular), col.rgb, metalness);\n"
"col.rgb = col.rgb * (1.0 - dielectricSpecular) * (1.0-metalness);\n"
"#elif defined(SG) //pbr-style specular+glossiness\n"
"#elif defined(SG) //pbr-style specular+glossiness, without occlusion\n"
//occlusion needs to be baked in. :(
"#define roughness (1.0-specs.a)\n"
"#define gloss (specs.a)\n"
"#define specrgb specs.rgb\n"
"#define ambientrgb (specs.rgb+col.rgb)\n"
"#define ambientrgb (specrgb+col.rgb)\n"
"#else //blinn-phong\n"
"#define roughness (1.0-specs.a)\n"
"#define gloss specs.a\n"
@ -3323,7 +3325,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\n"
"#endif\n"
"#if defined(occlusion) && !defined(NOOCCLUDE)\n"
"#ifdef OCCLUDE\n"
"col.rgb *= texture2D(s_occlusion, tc).r; \n"
"#elif defined(occlusion) && !defined(NOOCCLUDE)\n"
"col.rgb *= occlusion;\n"
"#endif\n"
"col *= light * e_colourident;\n"

View file

@ -664,7 +664,7 @@ int DecompileReadData(char *srcfilename, char *buf, size_t bufsize)
}
}
else
Sys_Error("Unrecognised progs version");
externs->Sys_Error("Unrecognised progs version");
numfunctions = progs.numfunctions;
functions = (dfunction_t*)(buf+progs.ofs_functions);

View file

@ -994,7 +994,7 @@ reeval:
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
{
pr_xstatement = st-pr_statements;
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused);
}
ptr = QCPOINTERM(i);
OPC->_float = ptr->_float *= OPA->_float;
@ -1004,7 +1004,7 @@ reeval:
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
{
pr_xstatement = st-pr_statements;
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused);
}
ptr = QCPOINTERM(i);
tmpf = OPA->_float; //don't break on vec*=vec_x;
@ -1020,7 +1020,7 @@ reeval:
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
{
pr_xstatement = st-pr_statements;
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused);
}
ptr = QCPOINTERM(i);
OPC->_float = ptr->_float /= OPA->_float;
@ -1038,7 +1038,7 @@ reeval:
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
{
pr_xstatement = st-pr_statements;
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused);
}
ptr = QCPOINTERM(i);
OPC->_float = ptr->_float += OPA->_float;
@ -1048,7 +1048,7 @@ reeval:
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
{
pr_xstatement = st-pr_statements;
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused);
}
ptr = QCPOINTERM(i);
OPC->_vector[0] = ptr->_vector[0] += OPA->_vector[0];
@ -1068,7 +1068,7 @@ reeval:
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
{
pr_xstatement = st-pr_statements;
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused);
}
ptr = QCPOINTERM(i);
OPC->_float = ptr->_float -= OPA->_float;
@ -1078,7 +1078,7 @@ reeval:
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
{
pr_xstatement = st-pr_statements;
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused);
}
ptr = QCPOINTERM(i);
OPC->_vector[0] = ptr->_vector[0] -= OPA->_vector[0];
@ -1093,7 +1093,7 @@ reeval:
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
{
pr_xstatement = st-pr_statements;
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused);
}
ptr = QCPOINTERM(i);
ptr->_float = (int)ptr->_float | (int)OPA->_float;
@ -1106,7 +1106,7 @@ reeval:
errorif (QCPOINTERWRITEFAIL(i, sizeof(float)))
{
pr_xstatement = st-pr_statements;
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, prinst.addressableused);
PR_RunError (&progfuncs->funcs, "bad pointer write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), i, (unsigned)prinst.addressableused);
}
ptr = QCPOINTERM(i);
ptr->_float = (int)ptr->_float & ~(int)OPA->_float;
@ -1290,7 +1290,7 @@ reeval:
errorif (OPB->_int < 0 || OPB->_int*4 >= current_progstate->globals_size)
{
pr_xstatement = st-pr_statements;
PR_RunError (&progfuncs->funcs, "bad indexed global write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), OPB->_int, prinst.addressableused);
PR_RunError (&progfuncs->funcs, "bad indexed global write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), OPB->_int, (unsigned)prinst.addressableused);
}
ptr = ((eval_t *)&glob[OPB->_int]);
ptr->_int = OPA->_int;
@ -1299,7 +1299,7 @@ reeval:
errorif (OPB->_int < 0 || (OPB->_int+2)*4 >= current_progstate->globals_size)
{
pr_xstatement = st-pr_statements;
PR_RunError (&progfuncs->funcs, "bad indexed global write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), OPB->_int, prinst.addressableused);
PR_RunError (&progfuncs->funcs, "bad indexed global write in %s (%x >= %x)", PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), OPB->_int, (unsigned)prinst.addressableused);
}
ptr = ((eval_t *)&glob[OPB->_int]);
ptr->_vector[0] = OPA->_vector[0];

View file

@ -128,7 +128,7 @@ void *PRAddressableExtend(progfuncs_t *progfuncs, void *src, size_t srcsize, int
}
if (prinst.addressableused + ammount > prinst.addressablesize)
Sys_Error("Not enough addressable memory for progs VM (using %gmb)", prinst.addressablesize/(1024.0*1024));
externs->Sys_Error("Not enough addressable memory for progs VM (using %gmb)", prinst.addressablesize/(1024.0*1024));
}
prinst.addressableused += ammount;
@ -136,7 +136,7 @@ void *PRAddressableExtend(progfuncs_t *progfuncs, void *src, size_t srcsize, int
#if defined(_WIN32) && !defined(WINRT)
if (!VirtualAlloc (prinst.addressablehunk, prinst.addressableused, MEM_COMMIT, PAGE_READWRITE))
Sys_Error("VirtualAlloc failed. Blame windows.");
externs->Sys_Error("VirtualAlloc failed. Blame windows.");
#endif
ptr = &prinst.addressablehunk[prinst.addressableused-ammount];
@ -472,7 +472,7 @@ void PRAddressableFlush(progfuncs_t *progfuncs, size_t totalammount)
// memset(prinst.addressablehunk, 0xff, totalammount);
#endif
if (!prinst.addressablehunk)
Sys_Error("Out of memory\n");
externs->Sys_Error("Out of memory\n");
prinst.addressablesize = totalammount;
progfuncs->funcs.stringtablemaxsize = totalammount;
}
@ -692,7 +692,7 @@ func_t PDECL PR_FindFunc(pubprogfuncs_t *ppf, const char *funcname, progsnum_t p
return (f - ps->functions) | (pnum << 24);
return *(int *)&ps->globals[var32->ofs];
}
Sys_Error("Error with def size (PR_FindFunc)");
externs->Sys_Error("Error with def size (PR_FindFunc)");
}
return 0;
}
@ -791,7 +791,7 @@ eval_t *PDECL PR_FindGlobal(pubprogfuncs_t *ppf, const char *globname, progsnum_
*type = var32->type;
return (eval_t *)&cp->globals[var32->ofs];
}
Sys_Error("Error with def size (PR_FindGlobal)");
externs->Sys_Error("Error with def size (PR_FindGlobal)");
return NULL;
}
@ -1006,13 +1006,14 @@ const char *ASMCALL PR_StringToNative (pubprogfuncs_t *ppf, string_t str)
if (((unsigned int)str & STRING_SPECMASK) == STRING_TEMP)
{
unsigned int i = str & ~STRING_SPECMASK;
if (i >= prinst.numtempstrings || !prinst.tempstrings[i])
tempstr_t *ts;
if (i >= prinst.maxtempstrings || !(ts=prinst.tempstrings[i]))
{
if (!progfuncs->funcs.debug_trace)
PR_RunWarning(&progfuncs->funcs, "invalid temp string %x\n", str);
return "";
}
return prinst.tempstrings[i]->value;
return ts->value;
}
if ((unsigned int)str >= (unsigned int)prinst.addressableused)
@ -1030,9 +1031,9 @@ eval_t *PR_GetReadTempStringPtr(progfuncs_t *progfuncs, string_t str, size_t off
if (((unsigned int)str & STRING_SPECMASK) != STRING_TEMP)
{
unsigned int i = str & ~STRING_SPECMASK;
if (i < prinst.numtempstrings && !prinst.tempstrings[i])
tempstr_t *temp;
if (i < prinst.maxtempstrings && (temp=prinst.tempstrings[i]))
{
tempstr_t *temp = prinst.tempstrings[i];
if (offset + datasize < temp->size)
return (eval_t*)(temp->value + offset);
else
@ -1046,9 +1047,9 @@ eval_t *PR_GetWriteTempStringPtr(progfuncs_t *progfuncs, string_t str, size_t of
if (((unsigned int)str & STRING_SPECMASK) != STRING_TEMP)
{
unsigned int i = str & ~STRING_SPECMASK;
if (i < prinst.numtempstrings && !prinst.tempstrings[i])
tempstr_t *temp;
if (i < prinst.maxtempstrings && (temp=prinst.tempstrings[i]))
{
tempstr_t *temp = prinst.tempstrings[i];
if (offset + datasize >= temp->size)
{ //access is beyond the current size. expand it.
unsigned int newsize;
@ -1109,10 +1110,113 @@ void QCBUILTIN PF_memsetval (pubprogfuncs_t *inst, struct globalvars_s *globals)
*(int*)(inst->stringtable + dst) = val;
}
//#define GCTIMINGS
#ifdef THREADEDGC
#include "quakedef.h"
struct qcgccontext_s
{
int done;
size_t clearedtemps; //number of temps that were swept away
progfuncs_t *progfuncs; //careful!
size_t numtemps; //so it doesn't go stale
tempstr_t **tempstrings;//so we don't get confused over temps added while marking
size_t memsize;
unsigned int amem[1];
};
void PR_QCGC_Done(void *ctx, void *data, size_t a, size_t b)
{
struct qcgccontext_s *gc = ctx;
gc->done = true;
}
void PR_QCGC_Thread(void *ctx, void *data, size_t a, size_t b)
{
struct qcgccontext_s *gc = ctx;
unsigned int p, r_d;
char *marked, *t;
unsigned int *str;
size_t numtemps = gc->numtemps;
#ifdef GCTIMINGS
unsigned int r_l;
double starttime, markedtime, endtime;
starttime = Sys_DoubleTime();
#endif
marked = malloc(sizeof(*marked) * numtemps);
memset(marked, 0, sizeof(*marked) * numtemps);
//mark everything the qc has access to, even if it isn't even a string!
//note that I did try specifically checking only data explicitly marked as a string type, but that was:
//a) a smidge slower (lots of extra loops and conditions I guess)
//b) doesn't work with pointers/structs (yes, we assume it'll all be aligned).
//c) both methods got the same number of false positives in my test (2, probably dead strunzoned references)
for (str = gc->amem, p = 0; p < gc->memsize; p+=sizeof(*str), str++)
{
if ((*str & STRING_SPECMASK) == STRING_TEMP)
{
unsigned int idx = *str &~ STRING_SPECMASK;
if (idx < numtemps)
marked[idx] = true;
}
}
#ifdef GCTIMINGS
markedtime = Sys_DoubleTime();
#endif
//sweep
#ifdef GCTIMINGS
r_l = 0;
#endif
r_d = 0;
for (p = 0; p < numtemps; p++)
{
if (marked[p])
{
#ifdef GCTIMINGS
r_l++;
#endif
}
else
break;
}
// prinst.nexttempstring = p;
for (; p < numtemps; p++)
{
if (marked[p])
{ //still live...
#ifdef GCTIMINGS
r_l++;
#endif
}
else if (gc->tempstrings[p])
{ //not marked, but was valid at the time our snapshot was taken
r_d++;
//FIXME: Security Race: its possible for a mod to do weird manipulations to access the tempstring while we're still freeing it, allowing it to read something outside of its sandbox.
//one option would be to have the main thread bounce it back to the worker after its complete, so we can actually free the memory only after main thread has acknowledged that its tempstrings are nulled.
gc->prinst.tempstrings[p] = NULL;
gc->externs->memfree(gc->tempstrings[p]);
}
}
gc->clearedtemps = r_d;
free(marked);
#ifdef GCTIMINGS
endtime = Sys_DoubleTime();
gc->externs->Printf("live: %u, dead: %u, threadtime: mark=%f, sweep=%f, total=%f\n", r_l, r_d, (markedtime - starttime), (endtime - markedtime), endtime-starttime);
#endif
COM_InsertWork(WG_MAIN, PR_QCGC_Done, gc, NULL, 0, 0);
}
static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, unsigned int len)
{
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
progfuncs_t *fte_restrict progfuncs = (progfuncs_t *)ppf;
tempstr_t **ntable;
int newmax;
int i;
@ -1120,37 +1224,36 @@ static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str,
if (!str)
return 0;
if (prinst.numtempstrings == prinst.maxtempstrings)
if (prinst.livetemps == prinst.maxtempstrings)
{
newmax = prinst.maxtempstrings + 1024;
//need to wait for the gc to finish, otherwise it might be wiping freed strings that we're still using.
while (prinst.gccontext)
{
COM_WorkerPartialSync(prinst.gccontext, &prinst.gccontext->done, false);
PR_RunGC(progfuncs);
}
newmax = prinst.maxtempstrings*2 + 1024;
ntable = progfuncs->funcs.parms->memalloc(sizeof(char*) * newmax);
memcpy(ntable, prinst.tempstrings, sizeof(char*) * prinst.numtempstrings);
#ifdef QCGC
memset(ntable+prinst.maxtempstrings, 0, sizeof(char*) * (newmax-prinst.numtempstrings));
#endif
memcpy(ntable, prinst.tempstrings, sizeof(char*) * prinst.maxtempstrings);
memset(ntable+prinst.maxtempstrings, 0, sizeof(char*) * (newmax-prinst.maxtempstrings));
prinst.maxtempstrings = newmax;
if (prinst.tempstrings)
progfuncs->funcs.parms->memfree(prinst.tempstrings);
prinst.tempstrings = ntable;
}
#ifdef QCGC
if (prinst.nexttempstring >= 0x10000000)
return 0;
do
for (i = prinst.nexttempstring; i < prinst.maxtempstrings && prinst.tempstrings[i]; i++)
;
if (i == prinst.maxtempstrings)
{
i = prinst.nexttempstring++;
} while(prinst.tempstrings[i] != NULL);
if (i == prinst.numtempstrings)
prinst.numtempstrings++;
#else
i = prinst.numtempstrings;
if (i == 0x10000000)
return 0;
prinst.numtempstrings++;
#endif
for (i = 0; i < prinst.nexttempstring && prinst.tempstrings[i]; i++)
;
if (i == prinst.nexttempstring)
return 0; //panic!
}
prinst.nexttempstring = i;
prinst.livetemps++;
prinst.tempstrings[i] = progfuncs->funcs.parms->memalloc(sizeof(tempstr_t) - sizeof(((tempstr_t*)NULL)->value) + len);
prinst.tempstrings[i]->size = len;
@ -1158,69 +1261,98 @@ static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str,
return (string_t)((unsigned int)i | STRING_TEMP);
}
string_t PDECL PR_AllocTempString (pubprogfuncs_t *ppf, const char *str)
pbool PR_RunGC (progfuncs_t *progfuncs)
{
#ifdef QCGC
char *out;
string_t res;
size_t len;
if (!str)
return 0;
len = strlen(str)+1;
res = PR_AllocTempStringLen(ppf, &out, len);
if (res)
memcpy(out, str, len);
return res;
#else
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
char **ntable;
int newmax;
int i;
if (!str)
return 0;
if (prinst.numtempstrings == prinst.maxtempstrings)
if (!prinst.gccontext)
{
newmax = prinst.maxtempstrings += 1024;
prinst.maxtempstrings += 1024;
ntable = progfuncs->funcs.parms->memalloc(sizeof(char*) * newmax);
memcpy(ntable, prinst.tempstrings, sizeof(char*) * prinst.numtempstrings);
prinst.maxtempstrings = newmax;
if (prinst.tempstrings)
progfuncs->funcs.parms->memfree(prinst.tempstrings);
prinst.tempstrings = ntable;
}
i = prinst.numtempstrings;
if (i == 0x10000000)
return 0;
prinst.numtempstrings++;
prinst.tempstrings[i] = progfuncs->funcs.parms->memalloc(strlen(str)+1);
strcpy(prinst.tempstrings[i], str);
return (string_t)((unsigned int)i | STRING_TEMP);
if (prinst.livetemps < prinst.maxtempstrings/2 || prinst.nexttempstring < prinst.maxtempstrings/2)
{ //don't bother yet
return false;
}
else
{
#ifdef GCTIMINGS
double starttime = Sys_DoubleTime(), endtime;
#endif
struct qcgccontext_s *gc = prinst.gccontext = malloc(sizeof(*gc) - sizeof(gc->amem) + prinst.addressableused + sizeof(*gc->tempstrings)*prinst.maxtempstrings);
gc->done = false;
gc->clearedtemps = 0;
gc->progfuncs = progfuncs;
gc->memsize = prinst.addressableused;
memcpy(gc->amem, prinst.addressablehunk, prinst.addressableused);
gc->numtemps = prinst.maxtempstrings;
gc->tempstrings = (void*)((char*)gc->amem+prinst.addressableused);
memcpy(gc->tempstrings, prinst.tempstrings, sizeof(*gc->tempstrings)*gc->numtemps);
COM_InsertWork(WG_LOADER, PR_QCGC_Thread, gc, NULL, 0, 0);
#ifdef GCTIMINGS
endtime = Sys_DoubleTime();
gc->externs->Printf("preparetime=%f\n", (endtime - starttime));
#endif
}
}
else if (prinst.gccontext->done)
{
prinst.livetemps -= prinst.gccontext->clearedtemps;
free(prinst.gccontext);
prinst.gccontext = NULL;
//if over half the (max)strings are still live, just increase the max so we are not spamming collections
if (prinst.livetemps >= prinst.maxtempstrings/2)
{
unsigned int newmax = prinst.maxtempstrings * 2;
tempstr_t **ntable = progfuncs->funcs.parms->memalloc(sizeof(char*) * newmax);
memcpy(ntable, prinst.tempstrings, sizeof(char*) * prinst.maxtempstrings);
memset(ntable+prinst.maxtempstrings, 0, sizeof(char*) * (newmax-prinst.maxtempstrings));
prinst.maxtempstrings = newmax;
if (prinst.tempstrings)
progfuncs->funcs.parms->memfree(prinst.tempstrings);
prinst.tempstrings = ntable;
}
return false;
}
return true; //running...
}
static void PR_FreeAllTemps (progfuncs_t *progfuncs)
{
unsigned int i;
while (prinst.gccontext)
{
COM_WorkerPartialSync(prinst.gccontext, &prinst.gccontext->done, false);
PR_RunGC(progfuncs);
}
for (i = 0; i < prinst.maxtempstrings; i++)
{
externs->memfree(prinst.tempstrings[i]);
prinst.tempstrings[i] = NULL;
}
prinst.maxtempstrings = 0;
prinst.nexttempstring = 0;
}
#elif defined(QCGC)
#include "quakedef.h"
#ifdef QCGC
pbool PR_RunGC (progfuncs_t *progfuncs)
{
unsigned int p;
char *marked;
unsigned int *str;
unsigned int r_l, r_d;
// unsigned long long starttime, markedtime, endtime;
#ifdef GCTIMINGS
double starttime, markedtime, endtime;
#endif
//only run the GC when we've itterated each string at least once.
if (prinst.nexttempstring < (prinst.maxtempstrings>>1) || prinst.nexttempstring < 200)
return false;
// starttime = Sys_GetClock();
#ifdef GCTIMINGS
starttime = Sys_DoubleTime();
#endif
marked = malloc(sizeof(*marked) * prinst.numtempstrings);
memset(marked, 0, sizeof(*marked) * prinst.numtempstrings);
@ -1240,7 +1372,9 @@ pbool PR_RunGC (progfuncs_t *progfuncs)
}
//sweep
// markedtime = Sys_GetClock();
#ifdef GCTIMINGS
markedtime = Sys_DoubleTime();
#endif
r_l = 0;
r_d = 0;
for (p = 0; p < prinst.numtempstrings; p++)
@ -1286,12 +1420,95 @@ pbool PR_RunGC (progfuncs_t *progfuncs)
prinst.tempstrings = ntable;
}
// endtime = Sys_GetClock();
// externs->Printf("live: %u, dead: %u, time: mark=%f, sweep=%f\n", r_l, r_d, (double)(markedtime - starttime) / Sys_GetClockRate(), (double)(endtime - markedtime) / Sys_GetClockRate());
#ifdef GCTIMINGS
endtime = Sys_DoubleTime();
externs->Printf("live: %u, dead: %u, time: mark=%f, sweep=%f, total=%f\n", r_l, r_d, markedtime - starttime, endtime - markedtime, endtime-starttime);
#endif
return true;
}
static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, unsigned int len)
{
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
tempstr_t **ntable;
int newmax;
int i;
if (!str)
return 0;
if (prinst.numtempstrings == prinst.maxtempstrings)
{
newmax = prinst.maxtempstrings + 1024;
ntable = progfuncs->funcs.parms->memalloc(sizeof(char*) * newmax);
memcpy(ntable, prinst.tempstrings, sizeof(char*) * prinst.numtempstrings);
memset(ntable+prinst.maxtempstrings, 0, sizeof(char*) * (newmax-prinst.numtempstrings));
prinst.maxtempstrings = newmax;
if (prinst.tempstrings)
progfuncs->funcs.parms->memfree(prinst.tempstrings);
prinst.tempstrings = ntable;
}
if (prinst.nexttempstring >= 0x10000000)
return 0;
do
{
i = prinst.nexttempstring++;
} while(prinst.tempstrings[i] != NULL);
if (i == prinst.numtempstrings)
prinst.numtempstrings++;
prinst.tempstrings[i] = progfuncs->funcs.parms->memalloc(sizeof(tempstr_t) - sizeof(((tempstr_t*)NULL)->value) + len);
prinst.tempstrings[i]->size = len;
*str = prinst.tempstrings[i]->value;
return (string_t)((unsigned int)i | STRING_TEMP);
}
static void PR_FreeAllTemps (progfuncs_t *progfuncs)
{
unsigned int i;
for (i = 0; i < prinst.numtempstrings; i++)
{
externs->memfree(prinst.tempstrings[i]);
prinst.tempstrings[i] = NULL;
}
prinst.numtempstrings = 0;
prinst.nexttempstring = 0;
}
#else
static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, unsigned int len)
{
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
tempstr_t **ntable;
int newmax;
int i;
if (!str)
return 0;
if (prinst.numtempstrings == prinst.maxtempstrings)
{
newmax = prinst.maxtempstrings + 1024;
ntable = progfuncs->funcs.parms->memalloc(sizeof(char*) * newmax);
memcpy(ntable, prinst.tempstrings, sizeof(char*) * prinst.numtempstrings);
prinst.maxtempstrings = newmax;
if (prinst.tempstrings)
progfuncs->funcs.parms->memfree(prinst.tempstrings);
prinst.tempstrings = ntable;
}
i = prinst.numtempstrings;
if (i == 0x10000000)
return 0;
prinst.numtempstrings++;
prinst.tempstrings[i] = progfuncs->funcs.parms->memalloc(sizeof(tempstr_t) - sizeof(((tempstr_t*)NULL)->value) + len);
prinst.tempstrings[i]->size = len;
*str = prinst.tempstrings[i]->value;
return (string_t)((unsigned int)i | STRING_TEMP);
}
void PR_FreeTemps (progfuncs_t *progfuncs, int depth)
{
int i;
@ -1307,7 +1524,6 @@ void PR_FreeTemps (progfuncs_t *progfuncs, int depth)
prinst.numtempstrings = depth;
}
#endif
static void PR_FreeAllTemps (progfuncs_t *progfuncs)
{
unsigned int i;
@ -1319,6 +1535,21 @@ static void PR_FreeAllTemps (progfuncs_t *progfuncs)
prinst.numtempstrings = 0;
prinst.nexttempstring = 0;
}
#endif
string_t PDECL PR_AllocTempString (pubprogfuncs_t *ppf, const char *str)
{
char *out;
string_t res;
size_t len;
if (!str)
return 0;
len = strlen(str)+1;
res = PR_AllocTempStringLen(ppf, &out, len);
if (res)
memcpy(out, str, len);
return res;
}
static pbool PDECL PR_DumpProfiles (pubprogfuncs_t *ppf, pbool resetprofiles)
{
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
@ -1387,13 +1618,11 @@ static pbool PDECL PR_DumpProfiles (pubprogfuncs_t *ppf, pbool resetprofiles)
return true;
}
static void PDECL PR_CloseProgs(pubprogfuncs_t *ppf);
static void PDECL RegisterBuiltin(pubprogfuncs_t *progfncs, const char *name, builtin_t func);
static void PDECL PR_Shutdown(pubprogfuncs_t *ppf);
static pubprogfuncs_t deffuncs = {
PROGSTRUCT_VERSION,
PR_CloseProgs,
PR_Shutdown,
PR_Configure,
PR_LoadProgs,
PR_InitEnts,
@ -1411,6 +1640,8 @@ static pubprogfuncs_t deffuncs = {
PR_VarString,
NULL, //progstate
0, //numprogs
PR_FindFunc,
#if defined(MINIMAL) || defined(OMIT_QCC)
NULL,
@ -1431,20 +1662,16 @@ static pubprogfuncs_t deffuncs = {
PR_RestoreEnt,
PR_FindGlobal,
ED_NewString,
QC_HunkAlloc,
QC_GetEdictFieldValue,
ProgsToEdict,
EdictToProgs,
PR_EvaluateDebugString,
0,//trace
PR_StackTrace,
PR_ToggleBreakpoint,
0, //numprogs
NULL, //parms
#if 1//defined(MINIMAL) || defined(OMIT_QCC)
NULL, //decompile
@ -1452,7 +1679,6 @@ static pubprogfuncs_t deffuncs = {
QC_Decompile,
#endif
0, //callargc
RegisterBuiltin,
0, //string table(pointer base address)
0, //string table size
@ -1466,19 +1692,18 @@ static pubprogfuncs_t deffuncs = {
QC_RegisterFieldVar,
NULL, //user tempstringbase
0, //user tempstringnum
ED_NewString,
QC_HunkAlloc,
PR_memalloc,
PR_memfree,
PR_AllocTempString,
PR_AllocTempStringLen,
PR_StringToProgs,
PR_StringToNative,
PR_QueryField,
QC_ClearEdict,
QC_FindPrefixedGlobals,
PR_memalloc,
PR_AllocTempStringLen,
PR_memfree,
PR_SetWatchPoint,
QC_AddSharedVar,
@ -1490,7 +1715,9 @@ static pubprogfuncs_t deffuncs = {
PR_UglyValueString,
ED_ParseEval,
PR_SetStringField,
PR_DumpProfiles
PR_DumpProfiles,
0, NULL,
};
static int PDECL qclib_null_printf(const char *s, ...)
{
@ -1565,9 +1792,8 @@ static progexterns_t defexterns = {
#undef maxedicts
#undef sv_num_edicts
static void PDECL PR_CloseProgs(pubprogfuncs_t *ppf)
static void PDECL PR_Shutdown(pubprogfuncs_t *ppf)
{
// extensionbuiltin_t *eb;
void (VARGS *f) (void *);
progfuncs_t *inst = (progfuncs_t*)ppf;
@ -1606,15 +1832,6 @@ static void PDECL PR_CloseProgs(pubprogfuncs_t *ppf)
free(inst->inst.watch_name);
/*
while(inst->prinst.extensionbuiltin)
{
eb = inst->prinst.extensionbuiltin->prev;
f(inst->prinst.extensionbuiltin);
inst->prinst.extensionbuiltin = eb;
}
*/
if (inst->inst.field)
f(inst->inst.field);
if (inst->inst.shares)
@ -1622,18 +1839,6 @@ static void PDECL PR_CloseProgs(pubprogfuncs_t *ppf)
f(inst);
}
static void PDECL RegisterBuiltin(pubprogfuncs_t *progfuncs, const char *name, builtin_t func)
{
/*
extensionbuiltin_t *eb;
eb = memalloc(sizeof(extensionbuiltin_t));
eb->prev = progfuncs->prinst.extensionbuiltin;
progfuncs->prinst.extensionbuiltin = eb;
eb->name = name;
eb->func = func;
*/
}
#ifndef WIN32
#define QCLIBINT //don't use dllspecifications
#endif

View file

@ -48,7 +48,7 @@ edictrun_t *ED_AllocIntoTable (progfuncs_t *progfuncs, int num, pbool object, un
progfuncs->funcs.AddressableFree(&progfuncs->funcs, e->fields);
e->fields = progfuncs->funcs.AddressableAlloc(&progfuncs->funcs, fields_size);
if (!e->fields)
Sys_Error ("ED_Alloc: Unable to allocate more field space");
externs->Sys_Error ("ED_Alloc: Unable to allocate more field space");
e->fieldsize = fields_size;
// e->fields = PRAddressableExtend(progfuncs, NULL, fields_size, 0);
@ -98,7 +98,7 @@ struct edict_s *PDECL ED_Alloc (pubprogfuncs_t *ppf, pbool object, size_t extras
return (struct edict_s *)e;
}
}
Sys_Error ("ED_Alloc: no free edicts (max is %i)", prinst.maxedicts);
externs->Sys_Error ("ED_Alloc: no free edicts (max is %i)", prinst.maxedicts);
}
//define this to wastefully allocate extra ents, to test network capabilities.
@ -146,7 +146,7 @@ struct edict_s *PDECL ED_Alloc (pubprogfuncs_t *ppf, pbool object, size_t extras
char *buf;
buf = PR_SaveEnts(&progfuncs->funcs, NULL, &size, 0, 0);
progfuncs->funcs.parms->WriteFile("edalloc.dump", buf, size);
Sys_Error ("ED_Alloc: no free edicts (max is %i)", prinst.maxedicts);
externs->Sys_Error ("ED_Alloc: no free edicts (max is %i)", prinst.maxedicts);
}
}
@ -358,7 +358,7 @@ unsigned int ED_FindGlobalOfs (progfuncs_t *progfuncs, char *name)
d32 = ED_FindGlobal32(progfuncs, name);
return d32?d32->ofs:0;
}
Sys_Error("ED_FindGlobalOfs - bad struct type");
externs->Sys_Error("ED_FindGlobalOfs - bad struct type");
return 0;
}
@ -457,7 +457,7 @@ unsigned int *ED_FindGlobalOfsFromProgs (progfuncs_t *progfuncs, progstate_t *ps
return NULL;
return &def32->ofs;
}
Sys_Error("ED_FindGlobalOfsFromProgs - bad struct type");
externs->Sys_Error("ED_FindGlobalOfsFromProgs - bad struct type");
return 0;
}
@ -933,7 +933,7 @@ char *PR_GlobalString (progfuncs_t *progfuncs, int ofs)
strcat (line," ");
return line;
}
Sys_Error("Bad struct type in PR_GlobalString");
externs->Sys_Error("Bad struct type in PR_GlobalString");
return "";
}
@ -960,7 +960,7 @@ char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs)
nameofs = def32->s_name;
break;
default:
Sys_Error("Bad struct type in PR_GlobalStringNoContents");
externs->Sys_Error("Bad struct type in PR_GlobalStringNoContents");
}
if (nameofs)
@ -1624,7 +1624,7 @@ add32:
}
break;
default:
Sys_Error("Bad struct type in SaveEnts");
externs->Sys_Error("Bad struct type in SaveEnts");
}
return buf;
@ -2023,7 +2023,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
datastart = file;
file = QCC_COM_Parse(file);
if (qcc_token[0] != '{')
Sys_Error("Progs loading found %s, not '{'", qcc_token);
externs->Sys_Error("Progs loading found %s, not '{'", qcc_token);
if (!resethunk)
ed = (edictrun_t *)ED_Alloc(&progfuncs->funcs, false, 0);
else
@ -2032,7 +2032,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
if (!ed)
{
Sys_Error("Edict was not allocated\n");
externs->Sys_Error("Edict was not allocated\n");
ed = ED_AllocIntoTable(progfuncs, num, false, prinst.fields_size);
}
}
@ -2050,7 +2050,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
num = atoi(qcc_token);
file = QCC_COM_Parse(file);
if (qcc_token[0] != '{')
Sys_Error("Progs loading found %s, not '{'", qcc_token);
externs->Sys_Error("Progs loading found %s, not '{'", qcc_token);
filename[0] = '\0';
@ -2060,7 +2060,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
{
file = QCC_COM_Parse(file); //read the key
if (!file)
Sys_Error("EOF in progs block");
externs->Sys_Error("EOF in progs block");
if (!strcmp("filename", qcc_token)) //check key get and save values
{file = QCC_COM_Parse(file); strcpy(filename, qcc_token);}
@ -2071,7 +2071,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
else if (qcc_token[0] == '}') //end of block
break;
else
Sys_Error("Bad key \"%s\" in progs block", qcc_token);
externs->Sys_Error("Bad key \"%s\" in progs block", qcc_token);
}
PR_ReallyLoadProgs(progfuncs, filename, &pr_progstate[num], true);
@ -2113,7 +2113,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
file = QCC_COM_Parse(file);
if (qcc_token[0] != '{')
Sys_Error("Globals loading found \'%s\', not '{'", qcc_token);
externs->Sys_Error("Globals loading found \'%s\', not '{'", qcc_token);
PR_SwitchProgs(progfuncs, num);
while (1)
@ -2122,7 +2122,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
if (qcc_token[0] == '}')
break;
else if (!qcc_token[0] || !file)
Sys_Error("EOF when parsing global values");
externs->Sys_Error("EOF when parsing global values");
switch(current_progstate->structtype)
{
@ -2153,7 +2153,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
}
break;
default:
Sys_Error("Bad struct type in LoadEnts");
externs->Sys_Error("Bad struct type in LoadEnts");
}
}
PR_SwitchProgs(progfuncs, 0);
@ -2171,13 +2171,13 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
file = QCC_COM_Parse(file);
if (qcc_token[0] != '{')
Sys_Error("Progs loading found %s, not '{'", qcc_token);
externs->Sys_Error("Progs loading found %s, not '{'", qcc_token);
while(1)
{
file = QCC_COM_Parse(file); //read the key
if (!file)
Sys_Error("EOF in general block");
externs->Sys_Error("EOF in general block");
if (!strcmp("maxprogs", qcc_token)) //check key get and save values
{file = QCC_COM_Parse(file); prinst.maxprogs = atoi(qcc_token);}
@ -2192,7 +2192,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
else if (qcc_token[0] == '}') //end of block
break;
else
Sys_Error("Bad key \"%s\" in general block", qcc_token);
externs->Sys_Error("Bad key \"%s\" in general block", qcc_token);
}
if (oldglobals)
@ -2237,7 +2237,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
if (qcc_token[0] == '}')
break;
else if (!qcc_token[0] || !file)
Sys_Error("EOF when parsing global values");
externs->Sys_Error("EOF when parsing global values");
switch(current_progstate->structtype)
{
@ -2268,7 +2268,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
}
break;
default:
Sys_Error("Bad struct type in LoadEnts");
externs->Sys_Error("Bad struct type in LoadEnts");
}
}
}
@ -2318,7 +2318,7 @@ int PDECL PR_LoadEnts(pubprogfuncs_t *ppf, const char *file, void *ctx, void (PD
else if (extendedterm && extendedterm(ppf, ctx, &datastart))
file = datastart;
else
Sys_Error("Bad entity lump: '%s' not recognised (last ent was %i)", qcc_token, ed?ed->entnum:0);
externs->Sys_Error("Bad entity lump: '%s' not recognised (last ent was %i)", qcc_token, ed?ed->entnum:0);
}
if (resethunk)
{
@ -2434,7 +2434,7 @@ struct edict_s *PDECL PR_RestoreEnt (pubprogfuncs_t *ppf, const char *buf, size_
return NULL;
if (strcmp(qcc_token, "{"))
Sys_Error("Restore Ent with no opening brace");
externs->Sys_Error("Restore Ent with no opening brace");
if (!ed)
ent = (edictrun_t *)ED_Alloc(&progfuncs->funcs, false, 0);
@ -2589,7 +2589,6 @@ PR_LoadProgs
int PR_ReallyLoadProgs (progfuncs_t *progfuncs, const char *filename, progstate_t *progstate, pbool complain)
{
unsigned int i, j, type;
// extensionbuiltin_t *eb;
// float fl;
// int len;
// int num;
@ -2789,7 +2788,7 @@ retry:
len=sizeof(dstatement32_t)*pr_progs->numstatements;
break;
default:
Sys_Error("Bad struct type");
externs->Sys_Error("Bad struct type");
len = 0;
}
s = PRHunkAlloc(progfuncs, len, "dstatements");
@ -2808,7 +2807,7 @@ retry:
len=sizeof(ddef32_t)*pr_progs->numglobaldefs;
break;
default:
Sys_Error("Bad struct type");
externs->Sys_Error("Bad struct type");
len = 0;
}
s = PRHunkAlloc(progfuncs, len, "dglobaldefs");
@ -2827,7 +2826,7 @@ retry:
len=sizeof(ddef32_t)*pr_progs->numglobaldefs;
break;
default:
Sys_Error("Bad struct type");
externs->Sys_Error("Bad struct type");
len = 0;
}
s = PRHunkAlloc(progfuncs, len, "progfieldtable");
@ -2937,7 +2936,7 @@ retry:
current_progstate->edict_size = pr_progs->entityfields * 4 + externs->edictsize;
if (sizeof(mfunction_t) > sizeof(qtest_function_t))
Sys_Error("assumption no longer works");
externs->Sys_Error("assumption no longer works");
// byte swap the lumps
switch(current_progstate->structtype)
@ -2983,7 +2982,7 @@ retry:
}
break;
default:
Sys_Error("Bad struct type");
externs->Sys_Error("Bad struct type");
}
//actual global values
@ -3147,7 +3146,7 @@ retry:
}
break;
default:
Sys_Error("Bad struct type");
externs->Sys_Error("Bad struct type");
}
//ifstring fixes arn't performed anymore.
@ -3217,7 +3216,7 @@ retry:
{
isfriked = true;
if (current_progstate->structtype != PST_DEFAULT)
Sys_Error("Decompiling a bigprogs");
externs->Sys_Error("Decompiling a bigprogs");
return true;
}
*/
@ -3397,9 +3396,9 @@ retry:
d32 = ED_FindGlobal32(progfuncs, s);
d2 = ED_FindGlobalOfsFromProgs(progfuncs, &pr_progstate[0], s, ev_function);
if (!d2)
Sys_Error("Runtime-linked function %s was not found in existing progs", s);
externs->Sys_Error("Runtime-linked function %s was not found in existing progs", s);
if (!d32)
Sys_Error("Couldn't find def for \"%s\"", s);
externs->Sys_Error("Couldn't find def for \"%s\"", s);
((int *)glob)[d32->ofs] = (*(func_t *)&pr_progstate[0].globals[*d2]);
s+=strlen(s)+1;
@ -3407,7 +3406,7 @@ retry:
}
break;
default:
Sys_Error("Bad struct type");
externs->Sys_Error("Bad struct type");
}
if ((isfriked && prinst.pr_typecurrent)) //friked progs only allow one file.
@ -3502,7 +3501,7 @@ struct edict_s *PDECL QC_EDICT_NUM(pubprogfuncs_t *ppf, unsigned int n)
{
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
if (n >= prinst.maxedicts)
Sys_Error ("QCLIB: EDICT_NUM: bad number %i", n);
externs->Sys_Error ("QCLIB: EDICT_NUM: bad number %i", n);
return (struct edict_s*)prinst.edicttable[n];
}
@ -3512,6 +3511,6 @@ unsigned int PDECL QC_NUM_FOR_EDICT(pubprogfuncs_t *ppf, struct edict_s *e)
progfuncs_t *progfuncs = (progfuncs_t*)ppf;
edictrun_t *er = (edictrun_t*)e;
if (!er || er->entnum >= prinst.maxedicts)
Sys_Error ("QCLIB: NUM_FOR_EDICT: bad pointer (%p)", e);
externs->Sys_Error ("QCLIB: NUM_FOR_EDICT: bad pointer (%p)", e);
return er->entnum;
}

View file

@ -563,7 +563,7 @@ int ASMCALL PR_LeaveFunction (progfuncs_t *progfuncs)
prstack_t *st;
if (pr_depth <= 0)
Sys_Error ("prog stack underflow");
externs->Sys_Error ("prog stack underflow");
// up stack
st = &pr_stack[--pr_depth];
@ -666,7 +666,7 @@ ddef32_t *ED_FindLocalOrGlobal(progfuncs_t *progfuncs, const char *name, eval_t
return NULL;
break;
default:
Sys_Error("Bad struct type in ED_FindLocalOrGlobal");
externs->Sys_Error("Bad struct type in ED_FindLocalOrGlobal");
def32 = NULL;
}
@ -1102,7 +1102,7 @@ void SetExecutionToLine(progfuncs_t *progfuncs, int linenum)
}
break;
default:
Sys_Error("Bad struct type");
externs->Sys_Error("Bad struct type");
snum = 0;
}
prinst.debugstatement = snum;
@ -1195,7 +1195,7 @@ int PDECL PR_ToggleBreakpoint(pubprogfuncs_t *ppf, const char *filename, int lin
op = ((dstatement32_t*)cp->statements + i)->op;
break;
default:
Sys_Error("Bad structtype");
externs->Sys_Error("Bad structtype");
op = 0;
}
switch (flag)
@ -1237,7 +1237,7 @@ int PDECL PR_ToggleBreakpoint(pubprogfuncs_t *ppf, const char *filename, int lin
((dstatement32_t*)cp->statements + i)->op = op;
break;
default:
Sys_Error("Bad structtype");
externs->Sys_Error("Bad structtype");
op = 0;
}
if (ret) //if its set, only set one breakpoint statement, not all of them.
@ -1269,7 +1269,7 @@ int PDECL PR_ToggleBreakpoint(pubprogfuncs_t *ppf, const char *filename, int lin
op = ((dstatement32_t*)cp->statements + i)->op;
break;
default:
Sys_Error("Bad structtype");
externs->Sys_Error("Bad structtype");
}
switch (flag)
{
@ -1310,7 +1310,7 @@ int PDECL PR_ToggleBreakpoint(pubprogfuncs_t *ppf, const char *filename, int lin
((dstatement32_t*)cp->statements + i)->op = op;
break;
default:
Sys_Error("Bad structtype");
externs->Sys_Error("Bad structtype");
}
break;
}
@ -1782,7 +1782,7 @@ static void PR_ExecuteCode (progfuncs_t *progfuncs, int s)
return;
continue;
default:
Sys_Error("PR_ExecuteProgram - bad structtype");
externs->Sys_Error("PR_ExecuteProgram - bad structtype");
}
}
}

View file

@ -276,7 +276,7 @@ int PDECL QC_RegisterFieldVar(pubprogfuncs_t *ppf, unsigned int type, const char
}
if (!progfuncs->funcs.fieldadjust && engineofs>=0)
if ((unsigned)engineofs/4 != prinst.field[i].ofs)
Sys_Error("Field %s at wrong offset", name);
externs->Sys_Error("Field %s at wrong offset", name);
if (prinst.field[i].progsofs == -1)
prinst.field[i].progsofs = progsofs;
@ -324,7 +324,7 @@ int PDECL QC_RegisterFieldVar(pubprogfuncs_t *ppf, unsigned int type, const char
}
#endif
if (engineofs&3)
Sys_Error("field %s is %i&3", name, (int)engineofs);
externs->Sys_Error("field %s is %i&3", name, (int)engineofs);
prinst.field[fnum].ofs = ofs = engineofs/4;
}
else
@ -372,7 +372,7 @@ int PDECL QC_RegisterFieldVar(pubprogfuncs_t *ppf, unsigned int type, const char
prinst.fields_size = (ofs+type_size[type])*4;
if (prinst.max_fields_size && prinst.fields_size > prinst.max_fields_size)
Sys_Error("Allocated too many additional fields after ents were inited.");
externs->Sys_Error("Allocated too many additional fields after ents were inited.");
#ifdef MAPPING_DEBUG
externs->Printf("Field %s %i -> %i\n", name, prinst.field[fnum].progsofs,prinst.field[fnum].ofs);
@ -499,14 +499,14 @@ void PDECL QC_AddSharedFieldVar(pubprogfuncs_t *ppf, int num, char *stringtable)
//oh well, must be a parameter.
if (*(int *)&pr_globals[gd[num].ofs])
Sys_Error("QCLIB: Global field var with no matching field \"%s\", from offset %i", gd[num].s_name+stringtable, *(int *)&pr_globals[gd[num].ofs]);
externs->Sys_Error("QCLIB: Global field var with no matching field \"%s\", from offset %i", gd[num].s_name+stringtable, *(int *)&pr_globals[gd[num].ofs]);
}
return;
default:
Sys_Error("Bad bits");
externs->Sys_Error("Bad bits");
break;
}
Sys_Error("Should be unreachable");
externs->Sys_Error("Should be unreachable");
}
void QC_AddFieldGlobal(pubprogfuncs_t *ppf, int *globdata)

View file

@ -92,16 +92,25 @@ typedef struct
char value[4];
} tempstr_t;
#if defined(QCGC) && defined(MULTITHREAD)
// #define THREADEDGC
#endif
//FIXME: the defines hidden inside this structure are evil.
typedef struct prinst_s
{
//temp strings are GCed, and can be created by engine, builtins, or just by ent parsing code.
tempstr_t **tempstrings;
unsigned int maxtempstrings;
#ifdef THREADEDGC
unsigned int nexttempstring;
unsigned int livetemps; //increased on alloc, decremented after sweep
struct qcgccontext_s *gccontext;
#elif defined(QCGC)
unsigned int numtempstrings;
#ifdef QCGC
unsigned int nexttempstring;
#else
unsigned int numtempstrings;
unsigned int numtempstringsstack;
#endif
@ -235,8 +244,8 @@ extern QCC_opcode_t pr_opcodes[]; // sized by initialization
#define sv_edicts (*externs->sv_edicts)
#define PR_DPrintf externs->DPrintf
#define printf syntax error
#define Sys_Error externs->Sys_Error
//#define printf syntax error
//#define Sys_Error externs->Sys_Error
int PRHunkMark(progfuncs_t *progfuncs);
void PRHunkFree(progfuncs_t *progfuncs, int mark);
@ -365,12 +374,6 @@ typedef struct progstate_s
#endif
} progstate_t;
typedef struct extensionbuiltin_s {
char *name;
builtin_t func;
struct extensionbuiltin_s *prev;
} extensionbuiltin_t;
//============================================================================

View file

@ -2,6 +2,7 @@
#ifndef PROGSLIB_H
#define PROGSLIB_H
#include "progtype.h"
#include <stdlib.h>
#ifdef _MSC_VER
#define VARGS __cdecl
@ -90,7 +91,7 @@ struct pubprogfuncs_s
{
int progsversion; //PROGSTRUCT_VERSION
void (PDECL *CloseProgs) (pubprogfuncs_t *inst);
void (PDECL *Shutdown) (pubprogfuncs_t *inst);
void (PDECL *Configure) (pubprogfuncs_t *prinst, size_t addressablesize, int max_progs, pbool enableprofiling); //configure buffers and memory. Used to reset and must be called first. Flushes a running VM.
progsnum_t (PDECL *LoadProgs) (pubprogfuncs_t *prinst, const char *s); //load a progs
@ -111,6 +112,7 @@ struct pubprogfuncs_s
char *(PDECL *VarString) (pubprogfuncs_t *prinst, int first); //returns a string made up of multiple arguments
struct progstate_s **progstate; //internal to the library.
int numprogs;
func_t (PDECL *FindFunction) (pubprogfuncs_t *prinst, const char *funcname, progsnum_t num);
@ -131,29 +133,21 @@ struct pubprogfuncs_s
struct edict_s *(PDECL *restoreent) (pubprogfuncs_t *prinst, const char *buf, size_t *size, struct edict_s *ed); //will restore the entity that had it's values saved (can use NULL for ed)
union eval_s *(PDECL *FindGlobal) (pubprogfuncs_t *prinst, const char *name, progsnum_t num, etype_t *type); //find a pointer to the globals value
char *(PDECL *AddString) (pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup); //dump a string into the progs memory (for setting globals and whatnot)
void *(PDECL *Tempmem) (pubprogfuncs_t *prinst, int ammount, char *whatfor); //grab some mem for as long as the progs stays loaded
union eval_s *(PDECL *GetEdictFieldValue)(pubprogfuncs_t *prinst, struct edict_s *ent, const char *name, etype_t type, evalc_t *s); //get an entityvar (cache it) and return the possible values
struct edict_s *(PDECL *ProgsToEdict) (pubprogfuncs_t *prinst, int progs); //edicts are stored as ints and need to be adjusted
int (PDECL *EdictToProgs) (pubprogfuncs_t *prinst, struct edict_s *ed); //edicts are stored as ints and need to be adjusted
char *(PDECL *EvaluateDebugString) (pubprogfuncs_t *prinst, const char *key); //evaluate a string and return it's value (according to current progs) (expands edict vars)
int debug_trace; //start calling the editor for each line executed
void (PDECL *StackTrace) (pubprogfuncs_t *prinst, int showlocals);
int (PDECL *ToggleBreak) (pubprogfuncs_t *prinst, const char *filename, int linenum, int mode);
int numprogs;
struct progexterns_s *parms; //these are the initial parms, they may be changed
pbool (PDECL *Decompile) (pubprogfuncs_t *prinst, const char *fname);
int callargc; //number of args of built-in call
void (PDECL *RegisterBuiltin) (pubprogfuncs_t *prinst, const char *, builtin_t);
char *stringtable; //qc strings are all relative. add to a qc string. this is required for support of frikqcc progs that strip string immediates.
int stringtablesize;
@ -168,23 +162,20 @@ struct pubprogfuncs_s
int (PDECL *RegisterFieldVar) (pubprogfuncs_t *prinst, unsigned int type, const char *name, signed long requestedpos, signed long originalofs);
char *tempstringbase; //for engine's use. Store your base tempstring pointer here.
int tempstringnum; //for engine's use.
char *(PDECL *AddString) (pubprogfuncs_t *prinst, const char *val, int minlength, pbool demarkup); //dump a string into the progs memory (for setting globals and whatnot)
void *(PDECL *Tempmem) (pubprogfuncs_t *prinst, int ammount, char *whatfor); //grab some mem for as long as the progs stays loaded
void *(PDECL *AddressableAlloc) (pubprogfuncs_t *progfuncs, unsigned int ammount); /*returns memory within the qc block, use stringtoprogs to get a usable qc pointer/string*/
void (PDECL *AddressableFree) (pubprogfuncs_t *progfuncs, void *mem); /*frees a block of addressable memory*/
string_t (PDECL *TempString) (pubprogfuncs_t *prinst, const char *str);
string_t (PDECL *AllocTempString) (pubprogfuncs_t *prinst, char **str, unsigned int len);
string_t (PDECL *StringToProgs) (pubprogfuncs_t *prinst, const char *str); //commonly makes a semi-permanent mapping from some table to the string value. mapping can be removed via RemoveProgsString
const char *(ASMCALL *StringToNative) (pubprogfuncs_t *prinst, string_t str);
const char *(ASMCALL *StringToNative) (pubprogfuncs_t *prinst, string_t str);
int (PDECL *QueryField) (pubprogfuncs_t *prinst, unsigned int fieldoffset, etype_t *type, char const**name, evalc_t *fieldcache); //find info on a field definition at an offset
void (PDECL *EntClear) (pubprogfuncs_t *progfuncs, struct edict_s *e);
void (PDECL *FindPrefixGlobals) (pubprogfuncs_t *progfuncs, int prnum, char *prefix, void (PDECL *found) (pubprogfuncs_t *progfuncs, char *name, union eval_s *val, etype_t type, void *ctx), void *ctx); //calls the callback for each named global found
void *(PDECL *AddressableAlloc) (pubprogfuncs_t *progfuncs, unsigned int ammount); /*returns memory within the qc block, use stringtoprogs to get a usable qc pointer/string*/
string_t (PDECL *AllocTempString) (pubprogfuncs_t *prinst, char **str, unsigned int len);
void (PDECL *AddressableFree) (pubprogfuncs_t *progfuncs, void *mem); /*frees a block of addressable memory*/
pbool (PDECL *SetWatchPoint) (pubprogfuncs_t *prinst, const char *key);
void (PDECL *AddSharedVar) (pubprogfuncs_t *progfuncs, int start, int size);
@ -200,6 +191,13 @@ struct pubprogfuncs_s
unsigned int edicttable_length;
struct edict_s **edicttable;
//stuff not used by the qclib at all, but provided for lazy user storage.
struct
{
char *tempstringbase; //for engine's use. Store your base tempstring pointer here.
int tempstringnum; //for engine's use.
} user;
};
typedef struct progexterns_s {
@ -317,8 +315,6 @@ typedef union eval_s
#define PROG_TO_EDICT(pf, ed) (*pf->ProgsToEdict) (pf, ed)
#define EDICT_TO_PROG(pf, ed) (*pf->EdictToProgs) (pf, (struct edict_s*)ed)
#define PR_RegisterBuiltin(pf, name, func) (*pf->RegisterBuiltin) (pf, name, func)
#define PR_GetString(pf,s) (*pf->StringToNative) (pf, s)
#define PR_GetStringOfs(pf,o) (*pf->StringToNative) (pf, G_INT(o))
#define PR_SetString(pf, s) (*pf->StringToProgs) (pf, s)

View file

@ -1876,7 +1876,7 @@ void QCC_PR_LexString (void)
QCC_PR_ParseWarning(WARN_MACROINSTRING, "Macro expansion in string");
if (len+strlen(cnst) >= sizeof(pr_token)-1)
QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1);
QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %u", (unsigned)sizeof(pr_token)-1);
strcpy(pr_token+len, cnst);
len+=strlen(cnst);
@ -1941,7 +1941,7 @@ forceutf8:
//error if needed
if (len+bytecount >= sizeof(pr_token))
QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1);
QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %u", (unsigned)sizeof(pr_token)-1);
//output it.
if (bytecount == 1)
@ -1974,7 +1974,7 @@ forcequake:
forcebyte:
if (len >= sizeof(pr_token)-1)
QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_token)-1);
QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %u", (unsigned)sizeof(pr_token)-1);
pr_token[len] = c;
len++;
}
@ -1982,7 +1982,7 @@ forcebyte:
}
if (len > sizeof(pr_immediate_string)-1)
QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %i", sizeof(pr_immediate_string)-1);
QCC_Error(ERR_INVALIDSTRINGIMMEDIATE, "String length exceeds %u", (unsigned)sizeof(pr_immediate_string)-1);
pr_token[len] = 0;
pr_token_type = tt_immediate;

View file

@ -3810,7 +3810,7 @@ DWORD WINAPI threadwrapper(void *args)
MessageBox(mainwindow, "Access Denied", "Cannot Start Engine", 0);
break;
default:
MessageBox(mainwindow, qcva("gla: %x", hr), "Cannot Start Engine", 0);
MessageBox(mainwindow, qcva("gla: %x", (unsigned)hr), "Cannot Start Engine", 0);
break;
}
hadstatus = true; //don't warn about other stuff

View file

@ -1527,7 +1527,7 @@ static pbool QCC_WriteData (int crc)
outputsttype = PST_QTEST;
break;
default:
Sys_Error("invalid progs type chosen!");
externs->Sys_Error("invalid progs type chosen!");
}
@ -1670,7 +1670,7 @@ static pbool QCC_WriteData (int crc)
}
break;
default:
Sys_Error("structtype error");
externs->Sys_Error("structtype error");
funcdata = NULL;
funcdatasize = 0;
}
@ -2144,7 +2144,7 @@ strofs = (strofs+3)&~3;
}
break;
default:
Sys_Error("structtype error");
externs->Sys_Error("structtype error");
}
progs.ofs_functions = SafeSeek (h, 0, SEEK_CUR);
@ -2292,7 +2292,7 @@ strofs = (strofs+3)&~3;
SafeWrite (h, fields16, numfielddefs*sizeof(QCC_ddef16_t));
break;
default:
Sys_Error("structtype error");
externs->Sys_Error("structtype error");
}
progs.ofs_globals = SafeSeek (h, 0, SEEK_CUR);

View file

@ -46,12 +46,12 @@ char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const
int i;
if (method == 0) //copy
{
if (complen != len) Sys_Error("lengths do not match");
if (complen != len) externs->Sys_Error("lengths do not match");
memcpy(buffer, info, len);
}
else if (method == 1) //xor encryption
{
if (complen != len) Sys_Error("lengths do not match");
if (complen != len) externs->Sys_Error("lengths do not match");
for (i = 0; i < len; i++)
buffer[i] = ((const char*)info)[i] ^ 0xA5;
}
@ -84,13 +84,13 @@ char *QC_decode(progfuncs_t *progfuncs, int complen, int len, int method, const
else
inflateInit(&strm);
if (Z_STREAM_END != inflate(&strm, Z_FINISH)) //decompress it in one go.
Sys_Error("Failed block decompression\n");
externs->Sys_Error("Failed block decompression\n");
inflateEnd(&strm);
}
#endif
//add your decryption/decompression routine here.
else
Sys_Error("Bad file encryption routine\n");
externs->Sys_Error("Bad file encryption routine\n");
return buffer;
@ -165,13 +165,13 @@ int QC_encode(progfuncs_t *progfuncs, int len, int method, const char *in, int h
deflateEnd(&strm);
return i;
#endif
Sys_Error("ZLIB compression not supported in this build");
externs->Sys_Error("ZLIB compression not supported in this build");
return 0;
}
//add your compression/decryption routine here.
else
{
Sys_Error("Wierd method");
externs->Sys_Error("Wierd method");
return 0;
}
}

View file

@ -855,8 +855,8 @@ void PR_Deinit(void)
PR_Common_Shutdown(svprogfuncs, false);
World_Destroy(&sv.world);
if (svprogfuncs->CloseProgs)
svprogfuncs->CloseProgs(svprogfuncs);
if (svprogfuncs->Shutdown)
svprogfuncs->Shutdown(svprogfuncs);
sv.world.progs = NULL;
svprogfuncs=NULL;

View file

@ -130,7 +130,7 @@ qboolean PR_QCChat(char *text, int say_type);
void PR_ClientUserInfoChanged(char *name, char *oldivalue, char *newvalue);
void PR_LocalInfoChanged(char *name, char *oldivalue, char *newvalue);
void PF_InitTempStrings(pubprogfuncs_t *prinst);
void PF_InitTempStrings(pubprogfuncs_t *inst);
#ifdef VM_LUA
qboolean PR_LoadLua(void);

View file

@ -1541,7 +1541,7 @@ void SV_Savegame (const char *savename, qboolean mapchange)
VFS_CLOSE(f);
#ifndef SERVERONLY
#ifdef HAVE_CLIENT
//try to save screenshots automagically.
Q_snprintfz(comment, sizeof(comment), "saves/%s/screeny.%s", savename, "tga");//scr_sshot_type.string);
savefilename = comment;
@ -1549,53 +1549,27 @@ void SV_Savegame (const char *savename, qboolean mapchange)
if (cls.state == ca_active && qrenderer > QR_NONE && qrenderer != QR_VULKAN/*FIXME*/)
{
int stride;
int width;
int height;
void *rgbbuffer;
int width = vid.pixelwidth;
int height = vid.pixelheight;
image_t *img;
//poke the various modes into redrawing the screen (without huds), to avoid any menus or console drawn over the top of the current backbuffer.
//FIXME: clear-to-black first
qboolean okay = false;
#ifdef VM_CG
if (!okay && CG_Refresh())
okay = true;
#endif
#ifdef CSQC_DAT
if (!okay && CSQC_DrawView())
okay = true;
#endif
if (!okay && r_worldentity.model)
uploadfmt_t format;
void *rgbbuffer = SCR_ScreenShot_Capture(width, height, &stride, &format, false);
if (rgbbuffer)
{
V_RenderView (false);
okay = true;
}
if (R2D_Flush)
R2D_Flush();
// extern cvar_t scr_sshot_type;
SCR_ScreenShot(savefilename, FS_GAMEONLY, &rgbbuffer, 1, stride, width, height, format, false);
BZ_Free(rgbbuffer);
//okay, we drew something, we're good to save a screeny.
if (okay)
{
enum uploadfmt fmt;
rgbbuffer = VID_GetRGBInfo(&stride, &width, &height, &fmt);
if (rgbbuffer)
//if any menu code has the shader loaded, we want to avoid them using a cache.
//hopefully the menu code will unload as it goes, because these screenshots could be truely massive, as they're taken at screen resolution.
//should probably use a smaller fbo or something, but whatever.
img = Image_FindTexture(va("saves/%s/screeny.%s", savename, "tga"), NULL, 0);
if (img)
{
// extern cvar_t scr_sshot_type;
SCR_ScreenShot(savefilename, FS_GAMEONLY, &rgbbuffer, 1, stride, width, height, fmt, false);
BZ_Free(rgbbuffer);
//if any menu code has the shader loaded, we want to avoid them using a cache.
//hopefully the menu code will unload as it goes, because these screenshots could be truely massive, as they're taken at screen resolution.
//should probably use a smaller fbo or something, but whatever.
img = Image_FindTexture(va("saves/%s/screeny.%s", savename, "tga"), NULL, 0);
if (img)
if (Image_UnloadTexture(img))
{
if (Image_UnloadTexture(img))
{
//and then reload it so that any shaders using it don't get confused
Image_GetTexture(va("saves/%s/screeny.%s", savename, "tga"), NULL, 0, NULL, NULL, 0, 0, TF_INVALID);
}
//and then reload it so that any shaders using it don't get confused
Image_GetTexture(va("saves/%s/screeny.%s", savename, "tga"), NULL, 0, NULL, NULL, 0, 0, TF_INVALID);
}
}
}

View file

@ -3212,6 +3212,8 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli
state->u.q1.pmovetype = ent->v->movetype;
if (state->u.q1.pmovetype && ((int)ent->v->flags & FL_ONGROUND) && (client->zquake_extensions&Z_EXT_PF_ONGROUND))
state->u.q1.pmovetype |= 0x80;
if (state->u.q1.pmovetype && ((int)cl->jump_held) && (client->zquake_extensions&Z_EXT_PM_TYPE))
state->u.q1.pmovetype |= 0x40;
if (cl != client && client)
{ /*only generate movement values if the client doesn't already know them...*/
state->u.q1.movement[0] = ent->xv->movement[0];

View file

@ -1107,7 +1107,7 @@ void SV_SpawnServer (const char *server, const char *startspot, qboolean noents,
{
if (svprogfuncs) //we don't want the q1 stuff anymore.
{
svprogfuncs->CloseProgs(svprogfuncs);
svprogfuncs->Shutdown(svprogfuncs);
sv.world.progs = svprogfuncs = NULL;
}
}

View file

@ -77,6 +77,7 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char
char tempname[256];
char tempvert[256];
char tempfrag[256];
char incpath[256], *sl;
int inheader = 1;
int i;
unsigned short constid = 256; //first few are reserved.
@ -94,9 +95,19 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char
NULL
};
snprintf(tempname, sizeof(tempname), "vulkan/temp.tmp");
snprintf(tempvert, sizeof(tempvert), "vulkan/temp.vert");
snprintf(tempfrag, sizeof(tempfrag), "vulkan/temp.frag");
const char *tmppath = "/tmp/";
char customsamplerlines[16][256];
snprintf(tempname, sizeof(tempname), "%stemp.tmp", tmppath);
snprintf(tempvert, sizeof(tempvert), "%stemp.vert", tmppath);
snprintf(tempfrag, sizeof(tempfrag), "%stemp.frag", tmppath);
memcpy(incpath, glslname, sizeof(incpath));
if ((sl = strrchr(incpath, '/')))
sl[1] = 0;
else if ((sl = strrchr(incpath, '\\'))) //in case someone is on crappy windows.
sl[1] = 0;
memcpy(blob->blobmagic, "\xffSPV", 4);
blob->blobversion = 1;
@ -243,10 +254,34 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char
blob->defaulttextures |= 1u<<13 | 1u<<17 | 1u<<18 | 1u<<19;
//shader pass
else if (atoi(arg))
blob->numtextures = atoi(arg);
else if ((i=atoi(arg)))
{ //legacy
if (blob->numtextures < i)
blob->numtextures = i;
}
else
printf("Unknown texture: \"%s\"\n", arg);
{
char *eq = strrchr(arg, '=');
if (eq)
{
char *type = strrchr(arg, ':');
int pass = atoi(eq+1);
*eq = 0;
if (type)
*type++ = 0;
else
type = "2D";
if (pass < 16)
{
snprintf(customsamplerlines[pass], sizeof(customsamplerlines[pass]), "uniform sampler%s s_%s;\n", type, arg);
if (blob->numtextures < ++pass)
blob->numtextures = pass;
}
else printf("sampler binding too high: %s:%s=%i\n", arg, type, pass);
}
else
printf("Unknown texture: \"%s\"\n", arg);
}
} while((arg = strtok(NULL, " ,\r\n")));
}
continue;
@ -295,7 +330,11 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char
}
for (i = 0; i < blob->numtextures; i++)
{
fprintf(temp, "layout(set=0, binding=%u) uniform sampler2D s_t%u;\n", binding++, i);
fprintf(temp, "layout(set=0, binding=%u) ", binding++);
if (*customsamplerlines[i])
fprintf(temp, "%s", customsamplerlines[i]);
else
fprintf(temp, "uniform sampler2D s_t%u;\n", i);
}
fprintf(temp, "#endif\n");
@ -415,7 +454,7 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char
#else
"echo \"#version 450 core\" > %s && "
#endif
"cpp %s -DVULKAN -DVERTEX_SHADER -P >> %s && "
"cpp %s -I%s -DVULKAN -DVERTEX_SHADER -P >> %s && "
/*preprocess the fragment shader*/
#ifdef _WIN32
@ -423,7 +462,7 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char
#else
"echo \"#version 450 core\" > %s && "
#endif
"cpp %s -DVULKAN -DFRAGMENT_SHADER -P >> %s && "
"cpp %s -I%s -DVULKAN -DFRAGMENT_SHADER -P >> %s && "
/*convert to spir-v (annoyingly we have no control over the output file names*/
"glslangValidator -V -l -d %s %s"
@ -431,13 +470,15 @@ int generatevulkanblobs(struct blobheader *blob, size_t maxblobsize, const char
/*strip stuff out, so drivers don't glitch out from stuff that we don't use*/
// " && spirv-remap -i vert.spv frag.spv -o vulkan/remap"
,tempvert, tempname, tempvert, tempfrag, tempname, tempfrag, tempvert, tempfrag);
,tempvert, tempname, incpath, tempvert //vertex shader args
,tempfrag, tempname, incpath, tempfrag //fragment shader args
,tempvert, tempfrag); //compile/link args.
system(command);
remove(tempname);
remove(tempvert);
remove(tempfrag);
// remove(tempname);
// remove(tempvert);
// remove(tempfrag);
return 1;
}
@ -451,6 +492,12 @@ int main(int argc, const char **argv)
char line[256];
int r = 1;
if (argc == 1)
{
printf("%s input.glsl output.fvb\n");
return 1;
}
if (!generatevulkanblobs((struct blobheader*)proto, sizeof(proto), inname))
return 1;
//should have generated two files

View file

@ -1479,6 +1479,9 @@ vk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t lay
case PTI_RGB8: format = VK_FORMAT_R8G8B8_UNORM; break;
case PTI_BGR8: format = VK_FORMAT_B8G8R8_UNORM; break;
case PTI_RGB8_SRGB: format = VK_FORMAT_R8G8B8_SRGB; break;
case PTI_BGR8_SRGB: format = VK_FORMAT_B8G8R8_SRGB; break;
//unsupported 'formats'
case PTI_MAX:
#ifdef FTE_TARGET_WEB
@ -1518,7 +1521,20 @@ vk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t lay
viewInfo.flags = 0;
viewInfo.image = ret.image;
viewInfo.viewType = (ret.type==PTI_CUBEMAP)?VK_IMAGE_VIEW_TYPE_CUBE:VK_IMAGE_VIEW_TYPE_2D;
switch(ret.type)
{
default:
return ret;
case PTI_CUBEMAP:
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
break;
case PTI_2D:
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
break;
case PTI_2D_ARRAY:
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
break;
}
viewInfo.format = format;
switch(encoding)
{
@ -1783,12 +1799,27 @@ qboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips)
uint32_t blockbytes;
uint32_t layers;
uint32_t mipcount = mips->mipcount;
if (mips->type != PTI_2D && mips->type != PTI_CUBEMAP)// && mips->type != PTI_2D_ARRAY)
return false;
if (!mipcount || mips->mip[0].width == 0 || mips->mip[0].height == 0)
switch(mips->type)
{
case PTI_2D:
if (!mipcount || mips->mip[0].width == 0 || mips->mip[0].height == 0 || mips->mip[0].depth != 1)
return false;
layers = 1;
break;
case PTI_2D_ARRAY:
if (!mipcount || mips->mip[0].width == 0 || mips->mip[0].height == 0 || mips->mip[0].depth == 0)
return false;
layers = 1;
break;
case PTI_CUBEMAP: //unfortunately, these use separate layers (yay gl compat)
if (!mipcount || mips->mip[0].width == 0 || mips->mip[0].height == 0 || mips->mip[0].depth != 1)
return false;
layers = 6;
break;
default:
return false;
}
layers = (mips->type == PTI_CUBEMAP)?6:1;
layers *= mips->mip[0].depth;
if (layers == 1 && mipcount > 1)
@ -1895,8 +1926,8 @@ qboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips)
{
uint32_t blockswidth = (mips->mip[i].width+blockwidth-1) / blockwidth;
uint32_t blocksheight = (mips->mip[i].height+blockheight-1) / blockheight;
bci.size += blockswidth*blocksheight*blockbytes;
uint32_t blocksdepth = (mips->mip[i].depth+1-1) / 1;
bci.size += blockswidth*blocksheight*blocksdepth*blockbytes;
}
bci.flags = 0;
bci.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
@ -1931,26 +1962,36 @@ qboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips)
//for compressed formats (ie: s3tc/dxt) we need to round up to deal with npot.
uint32_t blockswidth = (mips->mip[i].width+blockwidth-1) / blockwidth;
uint32_t blocksheight = (mips->mip[i].height+blockheight-1) / blockheight;
uint32_t blocksdepth = (mips->mip[i].depth+1-1) / 1;
if (mips->mip[i].data)
memcpy((char*)mapdata + bci.size, (char*)mips->mip[i].data, blockswidth*blockbytes*blocksheight);
memcpy((char*)mapdata + bci.size, (char*)mips->mip[i].data, blockswidth*blockbytes*blocksheight*blocksdepth);
else
memset((char*)mapdata + bci.size, 0, blockswidth*blockbytes*blocksheight);
memset((char*)mapdata + bci.size, 0, blockswidth*blockbytes*blocksheight*blocksdepth);
//queue up a buffer->image copy for this mip
region.bufferOffset = bci.size;
region.bufferRowLength = blockswidth*blockwidth;
region.bufferImageHeight = blocksheight*blockheight;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = i%(mipcount/layers);
region.imageSubresource.baseArrayLayer = i/(mipcount/layers);
region.imageSubresource.layerCount = 1;
if (mips->type == PTI_CUBEMAP)
{
region.imageSubresource.mipLevel = i%(mipcount/layers);
region.imageSubresource.baseArrayLayer = i/(mipcount/layers);
region.imageSubresource.layerCount = mips->mip[i].depth;
}
else
{
region.imageSubresource.mipLevel = i;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = mips->mip[i].depth;
}
region.imageOffset.x = 0;
region.imageOffset.y = 0;
region.imageOffset.z = 0;
region.imageExtent.width = mips->mip[i].width;//blockswidth*blockwidth;
region.imageExtent.height = mips->mip[i].height;//blocksheight*blockheight;
region.imageExtent.depth = 1;
region.imageExtent.width = mips->mip[i].width;
region.imageExtent.height = mips->mip[i].height;
region.imageExtent.depth = mips->mip[i].depth;
vkCmdCopyBufferToImage(vkloadcmd, fence->stagingbuffer, target.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
@ -4826,9 +4867,9 @@ qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*cre
}
sh_config.progpath = NULL;
sh_config.progpath = "vulkan/%s.fvb";
sh_config.blobpath = "spirv";
sh_config.shadernamefmt = NULL;//".spv";
sh_config.shadernamefmt = NULL;//"_vulkan";
if (vk.nv_glsl_shader)
{