Support connecting subnodes to servers over tcp (instead of depending on fork).

Fixed up the -netquake / -spasm / -fitz args slightly, should actually be usable now.
sv_mintic 0 is now treated as 0.013 when using nqplayerphysics, to try to make it smoother for nq clients.
Preparing for astc's volume formats. Mostly for completeness, I was bored. Disabled for now because nothing supports them anyway.
Fix broken mousewheel in SDL2 builds.
Fix configs not getting loaded following initial downloads in the web port/etc.
Make the near-cloud layer of q1 scrolling sky fully opaque by default (like vanilla).
Sky fog now ignores depth, treating it as an infinite distance.
Fix turbs not responding to fog.
r_fullbright no longer needs vid_reload to take effect (and more efficient now).
Tweaked the audio code to use an format enum instead of byte width, just with the same values still, primarily to clean up loaders that deal with S32 vs F32, or U8 vs S8.
Added a cvar to control whether to use threads for the qcgc. Still disabled by default but no longer requires engine recompiles to enable!



git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5683 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2020-04-29 10:43:22 +00:00
parent 7e66608b36
commit 4c2066601a
74 changed files with 2355 additions and 1294 deletions

View File

@ -299,7 +299,7 @@ ELSEIF(${UNIX}) #linux(ish)
NAMES ossaudio
)
IF(OSSAUDIO_LIBRARY)
SET(FTE_LIBS ${FTE_LIBS} ${OSSAUDIO_LIBRARY})
SET(FTE_LIBS ${FTE_LIBS} ${OSSAUDIO_LIBRARY})
ENDIF()
#on linux, use wayland.
@ -307,13 +307,21 @@ ELSEIF(${UNIX}) #linux(ish)
WAYLAND_CLIENT_LIBRARY
NAMES wayland-client libwayland-client
)
FIND_LIBRARY(
HAVE_XKBCOMMON
NAMES xkbcommon
)
IF(NOT HAVE_XKBCOMMON)
MESSAGE(WARNING "xkbcommon library not found, needed for wayland to be usable.")
UNSET(WAYLAND_CLIENT_LIBRARY)
ENDIF()
IF(WAYLAND_CLIENT_LIBRARY)
SET(FTE_DEFINES ${FTE_DEFINES};WAYLANDQUAKE;USE_EGL)
SET(FTE_ARCH_FILES ${FTE_ARCH_FILES}
engine/gl/gl_vidwayland.c
)
ELSE()
MESSAGE(WARNING "Wayland library NOT available")
MESSAGE(WARNING "Wayland library NOT available. X11 will live forever anyway.")
IF(NOT X11_FOUND)
MESSAGE(WARNING "No renderers supported!")
SET(FTE_NO_RENDERERS 1)

View File

@ -3012,23 +3012,9 @@ void CL_QTVPlay_f (void)
connrequest = strchrrev(connrequest, '@');
if (connrequest)
host = connrequest+1;
#ifdef HAVE_SSL
if (!strncmp(host, "tls://", 6))
{
char *colon;
Q_strncpyz(qtvhostname, host+6, sizeof(qtvhostname));
colon = strchr(qtvhostname, ':');
newf = FS_OpenTCP(qtvhostname, 27599);
if (colon) *colon = 0;
newf = FS_OpenSSL(qtvhostname, newf, false);
if (colon) *colon = ':';
}
else
#endif
{
Q_strncpyz(qtvhostname, host, sizeof(qtvhostname));
newf = FS_OpenTCP(qtvhostname, 27599);
}
Q_strncpyz(qtvhostname, host, sizeof(qtvhostname));
newf = FS_OpenTCP(qtvhostname, 27599, false);
if (!newf)
{
@ -3142,7 +3128,7 @@ void CL_QTVList_f (void)
{
char *connrequest;
vfsfile_t *newf;
newf = FS_OpenTCP(qtvhostname, 27599);
newf = FS_OpenTCP(qtvhostname, 27599, false);
if (!newf)
{
@ -3176,7 +3162,7 @@ void CL_QTVDemos_f (void)
{
char *connrequest;
vfsfile_t *newf;
newf = FS_OpenTCP(Cmd_Argv(1), 27599);
newf = FS_OpenTCP(Cmd_Argv(1), 27599, false);
if (!newf)
{

View File

@ -3828,7 +3828,7 @@ void CL_TransitionEntities (void)
frac = (servertime-packold->servertime)/(packnew->servertime-packold->servertime);
// if (!cl.paused)
// Con_Printf("%f %f %f (%f) (%i) %f %f %f\n", packold->servertime, servertime, packnew->servertime, frac, newff, cl.oldgametime, servertime, cl.gametime);
// Con_DPrintf("%f %s%f^7 %f (%f) (%i) %f %s%f^7 %f\n", packold->servertime, (servertime<packold->servertime||packnew->servertime<servertime)?"^1":"",servertime, packnew->servertime, frac, newff, cl.oldgametime, (servertime<cl.oldgametime||cl.gametime<servertime)?"^3":"", servertime, cl.gametime);
CL_TransitionPacketEntities(newff, packnew, packold, frac, servertime);

View File

@ -894,7 +894,7 @@ void CL_CheckForResend (void)
connectinfo.subprotocol = CPNQ_DP7;
}
else if (!strcmp(lbp, "qss") ||
(progstype != PROG_QW && progstype != PROG_H2)) //h2 depends on various extensions and doesn't really match either protocol, but we go for qw because that gives us all sorts of extensions.
(progstype != PROG_QW && progstype != PROG_H2 && sv.state!=ss_clustermode)) //h2 depends on various extensions and doesn't really match either protocol, but we go for qw because that gives us all sorts of extensions.
{
connectinfo.protocol = CP_NETQUAKE;
connectinfo.subprotocol = CPNQ_FITZ666;
@ -5545,6 +5545,7 @@ done:
man->updateurl = Z_StrDup(f->fname);
// if (f->flags & HRF_DOWNLOADED)
man->blockupdate = true;
//man->security = MANIFEST_SECURITY_DEFAULT;
BZ_Free(fdata);
FS_ChangeGame(man, true, true);
}
@ -6386,18 +6387,18 @@ void CL_StartCinematicOrMenu(void)
if (!sv_state && !cls.demoinfile && !cls.state && !*cls.servername)
{
if (qrenderer > QR_NONE && !Key_Dest_Has(kdm_menu))
if (qrenderer > QR_NONE && !Key_Dest_Has(~kdm_game))
{
#ifndef NOBUILTINMENUS
if (!cls.state && !Key_Dest_Has(kdm_menu) && !*FS_GetGamedir(false))
if (!cls.state && !Key_Dest_Has(~kdm_game) && !*FS_GetGamedir(false))
M_Menu_Mods_f();
#endif
if (!cls.state && !Key_Dest_Has(kdm_menu) && cl_demoreel.ival)
if (!cls.state && !Key_Dest_Has(~kdm_game) && cl_demoreel.ival)
{
cls.demonum = 0;
CL_NextDemo();
}
if (!cls.state && !Key_Dest_Has(kdm_menu))
if (!cls.state && !Key_Dest_Has(~kdm_game))
//if we're (now) meant to be using csqc for menus, make sure that its running.
if (!CSQC_UnconnectedInit())
M_ToggleMenu_f();

View File

@ -5671,19 +5671,23 @@ static void CL_SetStatString (int pnum, int stat, char *value)
if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV)
{
/* extern int cls_lastto;
cl.players[cls_lastto].statsstr[stat]=value;
extern int cls_lastto;
//Z_Free(cl.players[cls_lastto].statsstr[stat]);
//cl.players[cls_lastto].statsstr[stat]=Z_StrDup(value);
for (pnum = 0; pnum < cl.splitclients; pnum++)
if (spec_track[pnum] == cls_lastto)
cl.statsstr[pnum][stat] = value;*/
if (cl.playerview[pnum].cam_spec_track == cls_lastto && cl.playerview[pnum].cam_state != CAM_FREECAM)
{
if (cl.playerview[pnum].statsstr[stat])
Z_Free(cl.playerview[pnum].statsstr[stat]);
cl.playerview[pnum].statsstr[stat] = Z_StrDup(value);
}
}
else
{
if (cl.playerview[pnum].statsstr[stat])
Z_Free(cl.playerview[pnum].statsstr[stat]);
cl.playerview[pnum].statsstr[stat] = Z_Malloc(strlen(value)+1);
strcpy(cl.playerview[pnum].statsstr[stat], value);
cl.playerview[pnum].statsstr[stat] = Z_StrDup(value);
}
}
/*

View File

@ -621,11 +621,12 @@ void CL_CalcClientTime(void)
extern float olddemotime;
cl.servertime = olddemotime;
}
//q2 has no drifting.
//q3 always drifts.
//nq+qw code can drift
//q2 has no drifting (our code can't cope with picking anything beyond old/new snapshots, and frankly its 10fps which is horrendous enough as it is).
//q3 always drifts (gamecode does snapshot selection).
//qw code can drift (but oh noes! my latency!)
//FIXME: nq code should be able to drift, but is apparently buggy somewhere and ends up uncomfortably stuttery right now.
//default is to drift in demos+SP but not live (oh noes! added latency!)
if (cls.protocol == CP_QUAKE2 || (cls.protocol != CP_QUAKE3 && (!cl_lerp_smooth.ival || (cl_lerp_smooth.ival == 2 && !(cls.demoplayback || cl.allocated_client_slots == 1 || cl.playerview[0].spectator))) && cls.demoplayback != DPB_MVD))
if (cls.protocol == CP_QUAKE2 || cls.protocol==CP_NETQUAKE/*FIXME*/ || (cls.protocol != CP_QUAKE3 && (!cl_lerp_smooth.ival || (cl_lerp_smooth.ival == 2 && !(cls.demoplayback || cl.allocated_client_slots == 1 || cl.playerview[0].spectator))) && cls.demoplayback != DPB_MVD))
{ //no drift logic
float f;
f = cl.gametime - cl.oldgametime;
@ -671,6 +672,8 @@ void CL_CalcClientTime(void)
else
{
cl.servertime -= 0.02*(max - cl.servertime);
if (cl.servertime < cl.time)
cl.servertime = cl.time;
}
}
if (cl.servertime < min)

View File

@ -661,6 +661,7 @@ int SCR_DrawCenterString (vrect_t *rect, cprint_t *p, struct font_s *font)
int remaining;
shader_t *pic;
int ch;
int mousex,mousey;
conchar_t *line_start[MAX_CPRINT_LINES];
conchar_t *line_end[MAX_CPRINT_LINES];
@ -726,6 +727,7 @@ int SCR_DrawCenterString (vrect_t *rect, cprint_t *p, struct font_s *font)
}
}
Font_BeginString(font, mousecursor_x, mousecursor_y, &mousex, &mousey);
Font_BeginString(font, rect->x, y, &left, &top);
Font_BeginString(font, rect->x+rect->width, rect->y+rect->height, &right, &bottom);
linecount = Font_LineBreaks(p->string, p->string + p->charcount, (p->flags & CPRINT_NOWRAP)?0x7fffffff:(right - left), MAX_CPRINT_LINES, line_start, line_end);
@ -776,9 +778,9 @@ int SCR_DrawCenterString (vrect_t *rect, cprint_t *p, struct font_s *font)
else
x = left + (right - left - Font_LineWidth(line_start[l], line_end[l]))/2;
if (mousecursor_y >= y && mousecursor_y < y+ch)
if (mousey >= y && mousey < y+ch)
{
p->cursorchar = Font_CharAt(mousecursor_x - x, line_start[l], line_end[l]);
p->cursorchar = Font_CharAt(mousex - x, line_start[l], line_end[l]);
}
remaining -= line_end[l]-line_start[l];
@ -2869,7 +2871,7 @@ void SCR_ScreenShot_Cubemap_f(void)
{{-90, 0, 0}, "_up"}
};
const char *ext;
unsigned int bb, bw, bh;
unsigned int bb, bw, bh, bd;
if (!cls.state || !cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)
{
@ -2917,8 +2919,8 @@ void SCR_ScreenShot_Cubemap_f(void)
break;
if (!bb)
{
Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh);
if (!bb || bw != 1 || bh != 1 || fbwidth != fbheight)
Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh, &bd);
if (!bb || bw != 1 || bh != 1 || bd != 1 || fbwidth != fbheight)
{ //erk, no block compression here...
BZ_Free(facedata);
break; //zomgwtfbbq
@ -2999,7 +3001,7 @@ void SCR_ScreenShot_Cubemap_f(void)
buffer = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride, &fmt, true, false);
if (buffer)
{
Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh);
Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh, &bd);
if (sides[i].horizontalflip)
{
int y, x, p;

View File

@ -206,6 +206,9 @@ static image_t *imagelist;
#ifdef DECOMPRESS_ASTC
#define ASTC_PUBLIC
#ifdef ASTC3D
#define ASTC_WITH_3D
#endif
#include "image_astc.h"
#endif
@ -1707,7 +1710,7 @@ int Image_WritePNG (const char *filename, enum fs_relative fsroot, int compressi
qbyte stereochunk = 0; //cross-eyed
png_unknown_chunk unknowns = {"sTER", &stereochunk, sizeof(stereochunk), PNG_HAVE_PLTE};
int bw,bh,chanbits;
int bw,bh,bd,chanbits;
qboolean havepad, bgr;
int colourtype;
@ -1776,7 +1779,7 @@ int Image_WritePNG (const char *filename, enum fs_relative fsroot, int compressi
default:
return false;
}
Image_BlockSizeForEncoding(fmt, &pxsize, &bw, &bh);
Image_BlockSizeForEncoding(fmt, &pxsize, &bw, &bh, &bd);
if (!FS_NativePath(filename, fsroot, name, sizeof(name)))
return false;
@ -4311,7 +4314,7 @@ static qbyte *ReadXCFFile(const qbyte *filedata, size_t len, const char *fname,
{
size_t offs;
struct xcf_s ctx;
unsigned int bb,bw,bh;
unsigned int bb,bw,bh,bd;
if (len < 14 || strncmp(filedata, "gimp xcf ", 9) || filedata[13])
return NULL;
memset(&ctx, 0, sizeof(ctx));
@ -4361,7 +4364,7 @@ static qbyte *ReadXCFFile(const qbyte *filedata, size_t len, const char *fname,
//channels
//without any layers, its fully transparent
Image_BlockSizeForEncoding(ctx.outformat, &bb,&bw,&bh); //just for the bb...
Image_BlockSizeForEncoding(ctx.outformat, &bb,&bw,&bh,&bd); //just for the bb...
ctx.flat = Z_Malloc(ctx.width*ctx.height*bb);
*format = ctx.outformat;
*width = ctx.width;
@ -4752,7 +4755,7 @@ typedef struct
} ktxheader_t;
qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struct pendingtextureinfo *mips)
{
unsigned int bb,bw,bh;
unsigned int bb,bw,bh,bd;
vfsfile_t *file;
ktxheader_t header = {{0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A}, 0x04030201/*endianness*/,
0/*type*/, 1/*typesize*/, 0/*format*/, 0/*internalformat*/,
@ -4798,7 +4801,7 @@ qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struc
return false;
}
Image_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh);
Image_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh, &bd);
switch(mips->encoding)
{
@ -4871,6 +4874,39 @@ qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struc
case PTI_ASTC_10X10_SRGB: header.glinternalformat = 0x93DB/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR*/; break;
case PTI_ASTC_12X10_SRGB: header.glinternalformat = 0x93DC/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR*/; break;
case PTI_ASTC_12X12_SRGB: header.glinternalformat = 0x93DD/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR*/; break;
#ifdef ASTC3D
case PTI_ASTC_3X3X3_HDR:
case PTI_ASTC_3X3X3_LDR: header.glinternalformat = 0x93C0/*GL_COMPRESSED_RGBA_ASTC_3x3x3_OES*/; break;
case PTI_ASTC_4X3X3_HDR:
case PTI_ASTC_4X3X3_LDR: header.glinternalformat = 0x93C1/*GL_COMPRESSED_RGBA_ASTC_4x3x3_OES*/; break;
case PTI_ASTC_4X4X3_HDR:
case PTI_ASTC_4X4X3_LDR: header.glinternalformat = 0x93C2/*GL_COMPRESSED_RGBA_ASTC_4x4x3_OES*/; break;
case PTI_ASTC_4X4X4_HDR:
case PTI_ASTC_4X4X4_LDR: header.glinternalformat = 0x93C3/*GL_COMPRESSED_RGBA_ASTC_4x4x5_OES*/; break;
case PTI_ASTC_5X4X4_HDR:
case PTI_ASTC_5X4X4_LDR: header.glinternalformat = 0x93C4/*GL_COMPRESSED_RGBA_ASTC_5x4x4_OES*/; break;
case PTI_ASTC_5X5X4_HDR:
case PTI_ASTC_5X5X4_LDR: header.glinternalformat = 0x93C5/*GL_COMPRESSED_RGBA_ASTC_5x5x4_OES*/; break;
case PTI_ASTC_5X5X5_HDR:
case PTI_ASTC_5X5X5_LDR: header.glinternalformat = 0x93C6/*GL_COMPRESSED_RGBA_ASTC_5x5x5_OES*/; break;
case PTI_ASTC_6X5X5_HDR:
case PTI_ASTC_6X5X5_LDR: header.glinternalformat = 0x93C7/*GL_COMPRESSED_RGBA_ASTC_6x5x5_OES*/; break;
case PTI_ASTC_6X6X5_HDR:
case PTI_ASTC_6X6X5_LDR: header.glinternalformat = 0x93C8/*GL_COMPRESSED_RGBA_ASTC_6x6x5_OES*/; break;
case PTI_ASTC_6X6X6_HDR:
case PTI_ASTC_6X6X6_LDR: header.glinternalformat = 0x93C9/*GL_COMPRESSED_RGBA_ASTC_6x6x6_OES*/; break;
case PTI_ASTC_3X3X3_SRGB: header.glinternalformat = 0x93E0/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES*/; break;
case PTI_ASTC_4X3X3_SRGB: header.glinternalformat = 0x93E1/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES*/; break;
case PTI_ASTC_4X4X3_SRGB: header.glinternalformat = 0x93E2/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES*/; break;
case PTI_ASTC_4X4X4_SRGB: header.glinternalformat = 0x93E3/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES*/; break;
case PTI_ASTC_5X4X4_SRGB: header.glinternalformat = 0x93E4/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES*/; break;
case PTI_ASTC_5X5X4_SRGB: header.glinternalformat = 0x93E5/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES*/; break;
case PTI_ASTC_5X5X5_SRGB: header.glinternalformat = 0x93E6/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES*/; break;
case PTI_ASTC_6X5X5_SRGB: header.glinternalformat = 0x93E7/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES*/; break;
case PTI_ASTC_6X6X5_SRGB: header.glinternalformat = 0x93E8/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES*/; break;
case PTI_ASTC_6X6X6_SRGB: header.glinternalformat = 0x93E9/*GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES*/; break;
#endif
case PTI_BGRA8: header.glinternalformat = 0x8058/*GL_RGBA8*/; header.glbaseinternalformat = 0x1908/*GL_RGBA*/; header.glformat = 0x80E1/*GL_BGRA*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break;
case PTI_RGBA8: header.glinternalformat = 0x8058/*GL_RGBA8*/; header.glbaseinternalformat = 0x1908/*GL_RGBA*/; header.glformat = 0x1908/*GL_RGBA*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break;
case PTI_BGRA8_SRGB: header.glinternalformat = 0x8C43/*GL_SRGB8_ALPHA8*/; header.glbaseinternalformat = 0x1908/*GL_RGBA*/; header.glformat = 0x80E1/*GL_BGRA*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break;
@ -4938,7 +4974,7 @@ qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struc
unsigned int browbytes = bb * ((mips->mip[mipnum].width+bw-1)/bh);
unsigned int padbytes = (browbytes&3)?4-(browbytes&3):0;
unsigned int brows = (mips->mip[mipnum].height+bh-1)/bh;
unsigned int blayers = (mips->mip[mipnum].depth+1-1)/1;
unsigned int blayers = (mips->mip[mipnum].depth+bd-1)/bd;
if (mips->mip[mipnum].datasize != browbytes*brows*blayers)
{ //should probably be a sys_error
Con_Printf("WriteKTX mip %u missized\n", (unsigned)mipnum);
@ -4994,7 +5030,7 @@ static struct pendingtextureinfo *Image_ReadKTXFile(unsigned int flags, const ch
int encoding = TF_INVALID;
const qbyte *fileend = filedata + filesize;
unsigned int blockwidth, blockheight, blockbytes;
unsigned int blockwidth, blockheight, blockdepth, blockbytes;
if (filesize < sizeof(ktxheader_t) || memcmp(filedata, magic, sizeof(magic)))
return NULL; //not a ktx file
@ -5204,7 +5240,7 @@ static struct pendingtextureinfo *Image_ReadKTXFile(unsigned int flags, const ch
if (nummips * header.numberoffaces > countof(mips->mip))
nummips = countof(mips->mip) / header.numberoffaces;
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight);
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);
w = header.pixelwidth;
h = max(1, header.pixelheight);
@ -5222,7 +5258,8 @@ static struct pendingtextureinfo *Image_ReadKTXFile(unsigned int flags, const ch
browbytes = blockbytes * ((w+blockwidth-1)/blockwidth);
padbytes = (browbytes & 3)?4-(browbytes&3):0;
rows = ((h+blockheight-1)/blockheight)*d;
rows = ((h+blockheight-1)/blockheight)*
((d+blockdepth-1)/blockdepth);
if (datasize != (browbytes+padbytes) * rows)
{
Con_Printf("%s: mip %i does not match expected size (%u, required %u)\n", fname, mipnum, datasize, (browbytes+padbytes) * rows);
@ -5289,7 +5326,7 @@ static struct pendingtextureinfo *Image_ReadKTXFile(unsigned int flags, const ch
}
#ifdef ASTC_WITH_HDRTEST
if (encoding >= PTI_ASTC_4X4_LDR && encoding <= PTI_ASTC_12X12_LDR)
if (encoding >= PTI_ASTC_4X4_LDR && encoding < PTI_ASTC_4X4_SRGB)
{
int face;
for (face = 0; face < header.numberoffaces; face++)
@ -5311,7 +5348,7 @@ static struct pendingtextureinfo *Image_ReadKTXFile(unsigned int flags, const ch
static struct pendingtextureinfo *Image_ReadASTCFile(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize)
{
struct pendingtextureinfo *mips;
int encoding = PTI_INVALID, blockbytes, blockwidth, blockheight;
int encoding = PTI_INVALID, blockbytes, blockwidth, blockheight, blockdepth;
static const struct {
int w, h, d;
int fmt;
@ -5331,6 +5368,18 @@ static struct pendingtextureinfo *Image_ReadASTCFile(unsigned int flags, const c
{10,10,1,PTI_ASTC_10X10_LDR},
{12,10,1,PTI_ASTC_12X10_LDR},
{12,12,1,PTI_ASTC_12X12_LDR},
#ifdef ASTC3D
{3,3,3,PTI_ASTC_3X3X3_LDR},
{4,3,3,PTI_ASTC_4X3X3_LDR},
{4,4,3,PTI_ASTC_4X4X3_LDR},
{4,4,4,PTI_ASTC_4X4X4_LDR},
{5,4,4,PTI_ASTC_5X4X4_LDR},
{5,5,4,PTI_ASTC_5X5X4_LDR},
{5,5,5,PTI_ASTC_5X5X5_LDR},
{6,5,5,PTI_ASTC_6X5X5_LDR},
{6,6,5,PTI_ASTC_6X6X5_LDR},
{6,6,6,PTI_ASTC_6X6X6_LDR},
#endif
};
int i;
int size[3] = {
@ -5347,8 +5396,8 @@ static struct pendingtextureinfo *Image_ReadASTCFile(unsigned int flags, const c
}
if (!encoding)
return NULL; //block size not known
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight);
if (16+((size[0]+blockwidth-1)/blockwidth)*((size[1]+blockheight-1)/blockheight)*blockbytes != filesize)
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);
if (16+blockbytes*((size[0]+blockwidth-1)/blockwidth)*((size[1]+blockheight-1)/blockheight)*((size[2]+blockdepth-1)/blockdepth) != filesize)
return NULL; //err, not the right size!
mips = Z_Malloc(sizeof(*mips));
@ -5377,7 +5426,7 @@ static struct pendingtextureinfo *Image_ReadASTCFile(unsigned int flags, const c
static struct pendingtextureinfo *Image_ReadPKMFile(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize)
{
struct pendingtextureinfo *mips;
unsigned int encoding, blockbytes, blockwidth, blockheight;
unsigned int encoding, blockbytes, blockwidth, blockheight, blockdepth;
unsigned short ver, dfmt;
unsigned short datawidth, dataheight;
unsigned short imgwidth, imgheight;
@ -5424,7 +5473,7 @@ static struct pendingtextureinfo *Image_ReadPKMFile(unsigned int flags, const ch
else
return NULL;
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight);
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);
if (16+((datawidth+blockwidth-1)/blockwidth)*((dataheight+blockheight-1)/blockheight)*blockbytes != filesize)
return NULL; //err, not the right size!
@ -5483,7 +5532,7 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch
int mipnum;
int datasize;
unsigned int w, h, d;
unsigned int blockwidth, blockheight, blockbytes;
unsigned int blockwidth, blockheight, blockdepth, blockbytes;
struct pendingtextureinfo *mips;
int encoding;
int layers = 1, layer;
@ -5491,6 +5540,7 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch
ddsheader_t fmtheader;
dds10header_t fmt10header;
qbyte *fileend = filedata + filesize;
if (filesize < sizeof(fmtheader) || *(int*)filedata != (('D'<<0)|('D'<<8)|('S'<<16)|(' '<<24)))
return NULL;
@ -5764,10 +5814,31 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch
if ((fmtheader.ddsCaps[1] & 0x200) && (fmtheader.ddsCaps[1] & 0xfc00) != 0xfc00)
return NULL; //cubemap without all 6 faces defined.
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight);
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);
if (!blockbytes)
return NULL; //werid/unsupported
if (fmtheader.dwFlags & 8)
{ //explicit pitch flag. we don't support any padding, so this check exists just to be sure none is required.
w = max(1, fmtheader.dwWidth);
if (fmtheader.dwPitchOrLinearSize != blockbytes*(w+blockwidth-1)/blockwidth)
return NULL;
}
if (fmtheader.dwFlags & 0x80000)
{ //linear size flag. we don't support any padding, so this check exists just to be sure none is required.
//linear-size of the top-level mip.
size_t linearsize;
w = max(1, fmtheader.dwWidth);
h = max(1, fmtheader.dwHeight);
d = max(1, fmtheader.dwDepth);
linearsize = ((w+blockwidth-1)/blockwidth)*
((h+blockheight-1)/blockheight)*
((d+blockdepth-1)/blockdepth)*
blockbytes;
if (fmtheader.dwPitchOrLinearSize != linearsize)
return NULL;
}
if (fmtheader.ddsCaps[1] & 0x200)
{
if (fmt10header.arraysize % 6) //weird number of faces.
@ -5815,7 +5886,7 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch
{ //can just use the data without copying.
for (mipnum = 0; mipnum < nummips; mipnum++)
{
datasize = ((w+blockwidth-1)/blockwidth) * ((h+blockheight-1)/blockheight) * (d) * blockbytes;
datasize = ((w+blockwidth-1)/blockwidth) * ((h+blockheight-1)/blockheight) * ((d+blockdepth-1)/blockdepth) * blockbytes;
mips->mip[mipnum].data = filedata;
mips->mip[mipnum].datasize = datasize;
@ -5829,13 +5900,19 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch
d = max(1, d>>1);
}
mips->mipcount = mipnum;
if (filedata > fileend)
{ //overflow... corrupt dds?
Z_Free(mips);
return NULL;
}
}
else
{ //we need to copy stuff in order to pack it properly. :(
//allocate space and calc mip sizes
for (mipnum = 0; mipnum < nummips; mipnum++)
{
datasize = ((w+blockwidth-1)/blockwidth) * ((h+blockheight-1)/blockheight) * (layers*d) * blockbytes;
datasize = ((w+blockwidth-1)/blockwidth) * ((h+blockheight-1)/blockheight) * (layers*((d+blockdepth-1)/blockdepth)) * blockbytes;
mips->mip[mipnum].data = BZ_Malloc(datasize);
mips->mip[mipnum].datasize = datasize;
mips->mip[mipnum].width = w;
@ -5853,6 +5930,13 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch
for (mipnum = 0; mipnum < nummips; mipnum++)
{
datasize = mips->mip[mipnum].datasize/layers;
if (filedata+datasize > fileend)
{ //overflow... corrupt dds?
for (mipnum = 0; mipnum < nummips; mipnum++)
Z_Free(mips->mip[mipnum].data);
Z_Free(mips);
return NULL;
}
memcpy((qbyte*)mips->mip[mipnum].data+datasize*layer, filedata, datasize);
filedata += datasize;
}
@ -5874,10 +5958,10 @@ qboolean Image_WriteDDSFile(const char *filename, enum fs_relative fsroot, struc
ddsheader_t h9={0};
int *endian;
unsigned int blockbytes, blockwidth, blockheight;
unsigned int blockbytes, blockwidth, blockheight, blockdepth;
unsigned int arraysize;
Image_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight);
Image_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);
h9.dwSize = sizeof(h9);
h9.ddpfPixelFormat.dwSize = sizeof(h9.ddpfPixelFormat);
@ -5889,7 +5973,10 @@ qboolean Image_WriteDDSFile(const char *filename, enum fs_relative fsroot, struc
if (blockwidth != 1 || blockheight != 1)
{
h9.dwFlags |= 0x80000; //LINEARSIZE
h9.dwPitchOrLinearSize = ((mips->mip[0].width+blockwidth-1)/blockwidth)*((mips->mip[0].height+blockheight-1)/blockheight)*blockbytes;
h9.dwPitchOrLinearSize = ((mips->mip[0].width+blockwidth-1)/blockwidth)*
((mips->mip[0].height+blockheight-1)/blockheight)*
(mips->type==PTI_3D?((mips->mip[0].depth+blockdepth-1)/blockdepth):1)*
blockbytes;
}
else
{
@ -6135,6 +6222,38 @@ qboolean Image_WriteDDSFile(const char *filename, enum fs_relative fsroot, struc
case PTI_ASTC_12X12_HDR: //hdr allows more endpoint modes.
case PTI_ASTC_12X12_LDR: h10.dxgiformat = 0xba/*DXGI_FORMAT_ASTC_12X12_UNORM*/; break;
case PTI_ASTC_12X12_SRGB: h10.dxgiformat = 0xbb/*DXGI_FORMAT_ASTC_12X12_SRGB*/; break;
#ifdef ASTC3D
case PTI_ASTC_3X3X3_HDR:
case PTI_ASTC_4X3X3_HDR:
case PTI_ASTC_4X4X3_HDR:
case PTI_ASTC_4X4X4_HDR:
case PTI_ASTC_5X4X4_HDR:
case PTI_ASTC_5X5X4_HDR:
case PTI_ASTC_5X5X5_HDR:
case PTI_ASTC_6X5X5_HDR:
case PTI_ASTC_6X6X5_HDR:
case PTI_ASTC_6X6X6_HDR:
case PTI_ASTC_3X3X3_LDR:
case PTI_ASTC_4X3X3_LDR:
case PTI_ASTC_4X4X3_LDR:
case PTI_ASTC_4X4X4_LDR:
case PTI_ASTC_5X4X4_LDR:
case PTI_ASTC_5X5X4_LDR:
case PTI_ASTC_5X5X5_LDR:
case PTI_ASTC_6X5X5_LDR:
case PTI_ASTC_6X6X5_LDR:
case PTI_ASTC_6X6X6_LDR:
case PTI_ASTC_3X3X3_SRGB:
case PTI_ASTC_4X3X3_SRGB:
case PTI_ASTC_4X4X3_SRGB:
case PTI_ASTC_4X4X4_SRGB:
case PTI_ASTC_5X4X4_SRGB:
case PTI_ASTC_5X5X4_SRGB:
case PTI_ASTC_5X5X5_SRGB:
case PTI_ASTC_6X5X5_SRGB:
case PTI_ASTC_6X6X5_SRGB:
case PTI_ASTC_6X6X6_SRGB: return false; //no dxgi format assigned that we know of
#endif
case PTI_RGBX8: DX9FMT(32,0x000000ff,0x0000ff00,0x00ff0000,0x00000000,DX9RGB); break; //WARNING: buggy in gimp (ends up alpha=0)
case PTI_RGB8: DX9FMT(24,0x000000ff,0x0000ff00,0x00ff0000,0x00000000,DX9RGB); break;
@ -10260,17 +10379,17 @@ static void Image_Decode_BC7_Block(qbyte *fte_restrict in, pixel32_t *fte_restri
#ifdef ASTC_WITH_LDR
static void Image_Decode_ASTC_LDR_U8_Block(qbyte *fte_restrict in, pixel32_t *fte_restrict out, int stride, uploadfmt_t fmt)
{
int bw, bh, blockbytes;
Image_BlockSizeForEncoding(fmt, &blockbytes, &bw, &bh);
ASTC_Decode_LDR8(in, out->v, stride, bw, bh);
int bw, bh, bd, blockbytes;
Image_BlockSizeForEncoding(fmt, &blockbytes, &bw, &bh, &bd);
ASTC_Decode_LDR8(in, out->v, stride, 0/*w*h*/, bw, bh, bd);
}
#endif
#ifdef ASTC_WITH_HDR
static void Image_Decode_ASTC_HDR_HF_Block(qbyte *fte_restrict in, pixel64_t *fte_restrict out, int stride, uploadfmt_t fmt)
{
int bw, bh, blockbytes;
Image_BlockSizeForEncoding(fmt, &blockbytes, &bw, &bh);
ASTC_Decode_HDR(in, out->v, stride, bw, bh);
int bw, bh, bd, blockbytes;
Image_BlockSizeForEncoding(fmt, &blockbytes, &bw, &bh, &bd);
ASTC_Decode_HDR(in, out->v, stride, 0/*w*h*/, bw, bh, bd);
}
/*static unsigned int RGB16F_to_E5BGR9(unsigned short Cr, unsigned short Cg, unsigned short Cb)
@ -10354,9 +10473,9 @@ static void Image_Decode_L8_Block(qbyte *fte_restrict in, pixel32_t *fte_restric
Vector4Set(out->v, in[0], in[0], in[0], 0xff);
}
void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes, unsigned int *blockwidth, unsigned int *blockheight)
void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes, unsigned int *blockwidth, unsigned int *blockheight, unsigned int *blockdepth)
{
unsigned int b = 0, w = 1, h = 1;
unsigned int b = 0, w = 1, h = 1, d = 1;
switch(encoding)
{
case PTI_RGB565:
@ -10513,6 +10632,39 @@ void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes,
case PTI_ASTC_12X12_SRGB:
case PTI_ASTC_12X12_LDR: w = 12; h = 12; b = 16; break;
#ifdef ASTC3D
case PTI_ASTC_3X3X3_HDR:
case PTI_ASTC_3X3X3_SRGB:
case PTI_ASTC_3X3X3_LDR: w = 3; h = 3; d = 3; b = 16; break;
case PTI_ASTC_4X3X3_HDR:
case PTI_ASTC_4X3X3_SRGB:
case PTI_ASTC_4X3X3_LDR: w = 4; h = 3; d = 3; b = 16; break;
case PTI_ASTC_4X4X3_HDR:
case PTI_ASTC_4X4X3_SRGB:
case PTI_ASTC_4X4X3_LDR: w = 4; h = 4; d = 3; b = 16; break;
case PTI_ASTC_4X4X4_HDR:
case PTI_ASTC_4X4X4_SRGB:
case PTI_ASTC_4X4X4_LDR: w = 4; h = 4; d = 4; b = 16; break;
case PTI_ASTC_5X4X4_HDR:
case PTI_ASTC_5X4X4_SRGB:
case PTI_ASTC_5X4X4_LDR: w = 5; h = 4; d = 4; b = 16; break;
case PTI_ASTC_5X5X4_HDR:
case PTI_ASTC_5X5X4_SRGB:
case PTI_ASTC_5X5X4_LDR: w = 5; h = 5; d = 4; b = 16; break;
case PTI_ASTC_5X5X5_HDR:
case PTI_ASTC_5X5X5_SRGB:
case PTI_ASTC_5X5X5_LDR: w = 5; h = 5; d = 5; b = 16; break;
case PTI_ASTC_6X5X5_HDR:
case PTI_ASTC_6X5X5_SRGB:
case PTI_ASTC_6X5X5_LDR: w = 6; h = 5; d = 5; b = 16; break;
case PTI_ASTC_6X6X5_HDR:
case PTI_ASTC_6X6X5_SRGB:
case PTI_ASTC_6X6X5_LDR: w = 6; h = 6; d = 5; b = 16; break;
case PTI_ASTC_6X6X6_HDR:
case PTI_ASTC_6X6X6_SRGB:
case PTI_ASTC_6X6X6_LDR: w = 6; h = 6; d = 6; b = 16; break;
#endif
case PTI_EMULATED:
#ifdef FTE_TARGET_WEB
case PTI_WHOLEFILE: //UNKNOWN!
@ -10524,6 +10676,7 @@ void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes,
*blockbytes = b;
*blockwidth = w;
*blockheight = h;
*blockdepth = d;
}
qboolean Image_FormatHasAlpha(uploadfmt_t encoding)
@ -10641,6 +10794,38 @@ qboolean Image_FormatHasAlpha(uploadfmt_t encoding)
case PTI_ASTC_12X12_HDR:
case PTI_ASTC_12X12_SRGB:
case PTI_ASTC_12X12_LDR:
#ifdef ASTC3D
case PTI_ASTC_3X3X3_HDR:
case PTI_ASTC_3X3X3_SRGB:
case PTI_ASTC_3X3X3_LDR:
case PTI_ASTC_4X3X3_HDR:
case PTI_ASTC_4X3X3_SRGB:
case PTI_ASTC_4X3X3_LDR:
case PTI_ASTC_4X4X3_HDR:
case PTI_ASTC_4X4X3_SRGB:
case PTI_ASTC_4X4X3_LDR:
case PTI_ASTC_4X4X4_HDR:
case PTI_ASTC_4X4X4_SRGB:
case PTI_ASTC_4X4X4_LDR:
case PTI_ASTC_5X4X4_HDR:
case PTI_ASTC_5X4X4_SRGB:
case PTI_ASTC_5X4X4_LDR:
case PTI_ASTC_5X5X4_HDR:
case PTI_ASTC_5X5X4_SRGB:
case PTI_ASTC_5X5X4_LDR:
case PTI_ASTC_5X5X5_HDR:
case PTI_ASTC_5X5X5_SRGB:
case PTI_ASTC_5X5X5_LDR:
case PTI_ASTC_6X5X5_HDR:
case PTI_ASTC_6X5X5_SRGB:
case PTI_ASTC_6X5X5_LDR:
case PTI_ASTC_6X6X5_HDR:
case PTI_ASTC_6X6X5_SRGB:
case PTI_ASTC_6X6X5_LDR:
case PTI_ASTC_6X6X6_HDR:
case PTI_ASTC_6X6X6_SRGB:
case PTI_ASTC_6X6X6_LDR:
#endif
return true;
case PTI_EMULATED:
@ -10766,6 +10951,38 @@ const char *Image_FormatName(uploadfmt_t fmt)
case PTI_ASTC_12X12_HDR: return "ASTC_12X12_HDR";
case PTI_ASTC_12X12_SRGB: return "ASTC_12X12_SRGB";
case PTI_ASTC_12X12_LDR: return "ASTC_12X12_LDR";
#ifdef ASTC3D
case PTI_ASTC_3X3X3_HDR: return "ASTC_3X3X3_HDR";
case PTI_ASTC_3X3X3_SRGB: return "ASTC_3X3X3_SRGB";
case PTI_ASTC_3X3X3_LDR: return "ASTC_3X3X3_LDR";
case PTI_ASTC_4X3X3_HDR: return "ASTC_4X3X3_HDR";
case PTI_ASTC_4X3X3_SRGB: return "ASTC_4X3X3_SRGB";
case PTI_ASTC_4X3X3_LDR: return "ASTC_4X3X3_LDR";
case PTI_ASTC_4X4X3_HDR: return "ASTC_4X4X3_HDR";
case PTI_ASTC_4X4X3_SRGB: return "ASTC_4X4X3_SRGB";
case PTI_ASTC_4X4X3_LDR: return "ASTC_4X4X3_LDR";
case PTI_ASTC_4X4X4_HDR: return "ASTC_4X4X4_HDR";
case PTI_ASTC_4X4X4_SRGB: return "ASTC_4X4X4_SRGB";
case PTI_ASTC_4X4X4_LDR: return "ASTC_4X4X4_LDR";
case PTI_ASTC_5X4X4_HDR: return "ASTC_5X4X4_HDR";
case PTI_ASTC_5X4X4_SRGB: return "ASTC_5X4X4_SRGB";
case PTI_ASTC_5X4X4_LDR: return "ASTC_5X4X4_LDR";
case PTI_ASTC_5X5X4_HDR: return "ASTC_5X5X4_HDR";
case PTI_ASTC_5X5X4_SRGB: return "ASTC_5X5X4_SRGB";
case PTI_ASTC_5X5X4_LDR: return "ASTC_5X5X4_LDR";
case PTI_ASTC_5X5X5_HDR: return "ASTC_5X5X5_HDR";
case PTI_ASTC_5X5X5_SRGB: return "ASTC_5X5X5_SRGB";
case PTI_ASTC_5X5X5_LDR: return "ASTC_5X5X5_LDR";
case PTI_ASTC_6X5X5_HDR: return "ASTC_6X5X5_HDR";
case PTI_ASTC_6X5X5_SRGB: return "ASTC_6X5X5_SRGB";
case PTI_ASTC_6X5X5_LDR: return "ASTC_6X5X5_LDR";
case PTI_ASTC_6X6X5_HDR: return "ASTC_6X6X5_HDR";
case PTI_ASTC_6X6X5_SRGB: return "ASTC_6X6X5_SRGB";
case PTI_ASTC_6X6X5_LDR: return "ASTC_6X6X5_LDR";
case PTI_ASTC_6X6X6_HDR: return "ASTC_6X6X6_HDR";
case PTI_ASTC_6X6X6_SRGB: return "ASTC_6X6X6_SRGB";
case PTI_ASTC_6X6X6_LDR: return "ASTC_6X6X6_LDR";
#endif
#ifdef FTE_TARGET_WEB
case PTI_WHOLEFILE: return "Whole File";
@ -10803,10 +11020,10 @@ static pixel32_t *Image_Block_Decode(qbyte *fte_restrict in, size_t insize, int
int sizediff;
int rows, columns, layers;
unsigned int blockbytes, blockwidth, blockheight;
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight);
unsigned int blockbytes, blockwidth, blockheight, blockdepth;
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);
if (blockwidth > TMPBLOCKSIZE || blockheight > TMPBLOCKSIZE)
if (blockwidth > TMPBLOCKSIZE || blockheight > TMPBLOCKSIZE || blockdepth != 1)
Sys_Error("Image_Block_Decode only supports up to %u*%u blocks.\n", TMPBLOCKSIZE,TMPBLOCKSIZE);
sizediff = insize - blockbytes*((w+blockwidth-1)/blockwidth)*((h+blockheight-1)/blockheight)*d;
@ -10872,13 +11089,13 @@ static pixel64_t *Image_Block_Decode64(qbyte *fte_restrict in, size_t insize, in
int sizediff;
int rows, columns, layers;
unsigned int blockbytes, blockwidth, blockheight;
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight);
unsigned int blockbytes, blockwidth, blockheight, blockdepth;
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);
if (blockwidth > TMPBLOCKSIZE || blockheight > TMPBLOCKSIZE)
Sys_Error("Image_Block_Decode only supports up to %u*%u blocks.\n", TMPBLOCKSIZE,TMPBLOCKSIZE);
if (blockwidth > TMPBLOCKSIZE || blockheight > TMPBLOCKSIZE || blockdepth != 1)
Sys_Error("Image_Block_Decode only supports up to %u*%u*%u blocks.\n", TMPBLOCKSIZE,TMPBLOCKSIZE,1);
sizediff = insize - blockbytes*((w+blockwidth-1)/blockwidth)*((h+blockheight-1)/blockheight)*d;
sizediff = insize - blockbytes*((w+blockwidth-1)/blockwidth)*((h+blockheight-1)/blockheight)*((d+blockdepth-1)/blockdepth);
if (sizediff)
{
Con_Printf("Image_Block_Decode: %s data size is %u, expected %u\n\n", Image_FormatName(encoding), (unsigned int)insize, (unsigned int)(insize-sizediff));
@ -10892,8 +11109,9 @@ static pixel64_t *Image_Block_Decode64(qbyte *fte_restrict in, size_t insize, in
rows *= blockheight;
columns = w/blockwidth;
columns *= blockwidth;
layers = d;
for (z = 0; z < layers; z++)
layers = d/blockdepth;
layers *= blockdepth;
for (z = 0; z < layers; z+=blockdepth)
{
for (y = 0; y < rows; y+=blockheight, out += w*(blockheight-1))
{
@ -11317,9 +11535,9 @@ void Image_ChangeFormat(struct pendingtextureinfo *mips, qboolean *allowedformat
{ //direct3d is annoying, and will reject any block-compressed format with a base mip size that is not a multiple of the block size.
//its fine with weirdly sized mips though. I have no idea why there's this restriction, but whatever.
//we need to manually decompress in order to correctly handle such images
int blockbytes, blockwidth, blockheight;
Image_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight);
if (!(mips->mip[0].width % blockwidth) && !(mips->mip[0].height % blockheight))
int blockbytes, blockwidth, blockheight, blockdepth;
Image_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);
if (!(mips->mip[0].width % blockwidth) && !(mips->mip[0].height % blockheight) && !(mips->mip[0].depth % blockdepth))
return;
//else encoding isn't supported for this size. fall through.
}
@ -11499,7 +11717,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
unsigned int *rgbadata = rawdata;
int i;
qboolean valid;
unsigned int bb, bw, bh;
unsigned int bb, bw, bh, bd;
mips->mip[0].width = imgwidth;
mips->mip[0].height = imgheight;
@ -11516,13 +11734,13 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
if (mips->mip[0].width == imgwidth && mips->mip[0].height == imgheight) //make sure its okay
{
size_t sz = 0;
Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh);
Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh, &bd);
for (i = 0; i < countof(mips->mip) && (imgwidth || imgheight); i++, imgwidth>>=1, imgheight>>=1)
{
mips->mip[i].width = max(1,imgwidth);
mips->mip[i].height = max(1,imgheight);
mips->mip[i].depth = 1;
mips->mip[i].datasize = bb * ((mips->mip[i].width+bw-1)/bw) * ((mips->mip[i].height+bh-1)/bh);
mips->mip[i].datasize = bb * ((mips->mip[i].width+bw-1)/bw) * ((mips->mip[i].height+bh-1)/bh) * ((mips->mip[i].depth+bd-1)/bd);
mips->mip[i].needfree = false;
sz += mips->mip[i].datasize;
}
@ -12041,6 +12259,38 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
case PTI_ASTC_12X12_LDR:
case PTI_ASTC_12X12_SRGB:
case PTI_ASTC_12X12_HDR:
#ifdef ASTC3D
case PTI_ASTC_3X3X3_HDR:
case PTI_ASTC_3X3X3_SRGB:
case PTI_ASTC_3X3X3_LDR:
case PTI_ASTC_4X3X3_HDR:
case PTI_ASTC_4X3X3_SRGB:
case PTI_ASTC_4X3X3_LDR:
case PTI_ASTC_4X4X3_HDR:
case PTI_ASTC_4X4X3_SRGB:
case PTI_ASTC_4X4X3_LDR:
case PTI_ASTC_4X4X4_HDR:
case PTI_ASTC_4X4X4_SRGB:
case PTI_ASTC_4X4X4_LDR:
case PTI_ASTC_5X4X4_HDR:
case PTI_ASTC_5X4X4_SRGB:
case PTI_ASTC_5X4X4_LDR:
case PTI_ASTC_5X5X4_HDR:
case PTI_ASTC_5X5X4_SRGB:
case PTI_ASTC_5X5X4_LDR:
case PTI_ASTC_5X5X5_HDR:
case PTI_ASTC_5X5X5_SRGB:
case PTI_ASTC_5X5X5_LDR:
case PTI_ASTC_6X5X5_HDR:
case PTI_ASTC_6X5X5_SRGB:
case PTI_ASTC_6X5X5_LDR:
case PTI_ASTC_6X6X5_HDR:
case PTI_ASTC_6X6X5_SRGB:
case PTI_ASTC_6X6X5_LDR:
case PTI_ASTC_6X6X6_HDR:
case PTI_ASTC_6X6X6_SRGB:
case PTI_ASTC_6X6X6_LDR:
#endif
#ifdef FTE_TARGET_WEB
case PTI_WHOLEFILE:
#endif
@ -12238,8 +12488,8 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
}
else
mips->mip[0].data = NULL;
Image_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh);
mips->mip[0].datasize = ((mips->mip[0].width+bw-1)/bw) * ((mips->mip[0].height+bh-1)/bh) * bb;
Image_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh, &bd);
mips->mip[0].datasize = ((mips->mip[0].width+bw-1)/bw) * ((mips->mip[0].height+bh-1)/bh) * ((mips->mip[0].depth+bd-1)/bd) * bb;
if (mips->type == PTI_3D)
{
@ -12247,7 +12497,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
mips->mip[0].data = NULL;
/*our 2d input image is interlaced as y0z0,y0z1,y1z0,y1z1
however, hardware uses the more logical y0z0,y1z0,y0z1,y1z1 ordering (xis ordered properly already)*/
if (mips->mip[0].height*mips->mip[0].height == mips->mip[0].width && mips->mip[0].depth == 1 && (mips->encoding == PTI_RGBA8 || mips->encoding == PTI_RGBX8 || mips->encoding == PTI_BGRA8 || mips->encoding == PTI_BGRX8))
if (mips->mip[0].height*mips->mip[0].height == mips->mip[0].width && mips->mip[0].depth == 1 && (bb==4&&bw==1&&bh==1&&bd==1))
{
int d, r;
int size = mips->mip[0].height;
@ -12695,9 +12945,9 @@ static struct pendingtextureinfo *Image_LoadCubemapTextureData(const char *nicen
if ((data = ReadRawImageFile(buf, filesize, &width, &height, &format, true, fname)))
{
extern cvar_t vid_hardwaregamma;
int bb,bw,bh;
Image_BlockSizeForEncoding(format, &bb, &bw, &bh);
if (needsflipping && (bw!=1 || bh!=1))
int bb,bw,bh, bd;
Image_BlockSizeForEncoding(format, &bb, &bw, &bh, &bd);
if (needsflipping && (bw!=1 || bh!=1 || bd!=1))
/*can't do it*/;
else if (width == height && (!mips || width == mips->mip[0].width)) //cubemaps must be square and all the same size (npot is fine though)
{ //(skies have a fallback for invalid sizes, but it'll run a bit slower)
@ -13443,11 +13693,11 @@ image_t *QDECL Image_GetTexture(const char *identifier, const char *subpath, uns
break;
default:
{
unsigned int bb, bw, bh;
unsigned int bb, bw, bh, bd;
unsigned int lev;
Image_BlockSizeForEncoding(fallbackfmt&~PTI_FULLMIPCHAIN, &bb, &bw, &bh);
Image_BlockSizeForEncoding(fallbackfmt&~PTI_FULLMIPCHAIN, &bb, &bw, &bh, &bd);
for (b=0, lev = 0; fallbackwidth>>lev||fallbackheight>>lev; lev++)
b += bb * (max(1,fallbackwidth>>lev)+bw-1)/bw * (max(1,fallbackheight>>lev)+bh-1)/bh;
b += bb * (max(1,fallbackwidth>>lev)+bw-1)/bw * (max(1,fallbackheight>>lev)+bh-1)/bh;// * (max(1,fallbackdepth>>lev)+bd-1)/bd;
}
break;
}
@ -13727,9 +13977,9 @@ void Image_List_f(void)
if (tex->status == TEX_LOADED)
{
char *type;
unsigned int blockbytes, blockwidth, blockheight;
Image_BlockSizeForEncoding(tex->format, &blockbytes, &blockwidth, &blockheight);
imgmem = blockbytes * (tex->width+blockwidth-1)/blockwidth * (tex->height+blockheight-1)/blockheight * tex->depth;
unsigned int blockbytes, blockwidth, blockheight, blockdepth;
Image_BlockSizeForEncoding(tex->format, &blockbytes, &blockwidth, &blockheight, &blockdepth);
imgmem = blockbytes * (tex->width+blockwidth-1)/blockwidth * (tex->height+blockheight-1)/blockheight * (tex->depth+blockdepth-1)/blockdepth;
switch((tex->flags & IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT)
{
case PTI_2D: type = ""; break;
@ -13777,7 +14027,7 @@ void Image_Formats_f(void)
{
size_t i;
float bpp;
int blockbytes, blockwidth, blockheight;
int blockbytes, blockwidth, blockheight, blockdepth;
#ifdef GLQUAKE
if (qrenderer == QR_OPENGL)
@ -13822,6 +14072,8 @@ void Image_Formats_f(void)
Con_Printf( " Non-Power-Of-Two: %s%s\n", sh_config.texture_non_power_of_two?S_COLOR_GREEN"Supported":(sh_config.texture_non_power_of_two_pic?S_COLOR_YELLOW"Limited":S_COLOR_RED"Unsupported"), sh_config.npot_rounddown?" (rounded down)":"");
Con_Printf( " Block Size Padding: %s\n", sh_config.texture_allow_block_padding?S_COLOR_GREEN"Supported":S_COLOR_RED"Unsupported");
Con_Printf( " Mipcap: %s\n", sh_config.can_mipcap?S_COLOR_GREEN"Supported":S_COLOR_RED"Unsupported");
Con_Printf( "\n Driver Support:\n");
for (i = 0; i < PTI_MAX; i++)
{
switch(i)
@ -13831,9 +14083,9 @@ void Image_Formats_f(void)
default:
break;
}
Image_BlockSizeForEncoding(i, &blockbytes, &blockwidth, &blockheight);
bpp = blockbytes*8.0/(blockwidth*blockheight);
Con_Printf("%20s: %s"S_COLOR_GRAY" (%.3g-bpp)\n", Image_FormatName(i), sh_config.texfmt[i]?S_COLOR_GREEN"Enabled":S_COLOR_RED"Disabled", bpp);
Image_BlockSizeForEncoding(i, &blockbytes, &blockwidth, &blockheight, &blockdepth);
bpp = blockbytes*8.0/(blockwidth*blockheight*blockdepth);
Con_Printf("%20s: %s"S_COLOR_GRAY" (%s%.3g-bpp)\n", Image_FormatName(i), sh_config.texfmt[i]?S_COLOR_GREEN"Enabled":S_COLOR_RED"Disabled", (blockdepth!=1)?"3d, ":"", bpp);
}
}

View File

@ -12,16 +12,17 @@
#define ASTC_WITH_LDR //comment out this line to disable pure-LDR decoding (the hdr code can still be used).
#define ASTC_WITH_HDR //comment out this line to disable HDR decoding.
#define ASTC_WITH_HDRTEST //comment out this line to disable HDR decoding.
#define ASTC_WITH_HDRTEST //comment out this line to disable checking for which profile is needed.
//#define ASTC_WITH_3D
#ifdef ASTC_WITH_LDR
ASTC_PUBLIC void ASTC_Decode_LDR8(unsigned char *in, unsigned char *out, int pixstride, int bw,int bh); //generates RGBA8 data (gives error colour for hdr blocks!)
ASTC_PUBLIC void ASTC_Decode_LDR8(unsigned char *in, unsigned char *out, int pixstride/*outwidth*/, int layerstride/*outwidth*outheight*/, int bw,int bh,int bd); //generates RGBA8 data (gives error colour for hdr blocks!)
#endif
#ifdef ASTC_WITH_HDR
ASTC_PUBLIC void ASTC_Decode_HDR(unsigned char *in, unsigned short *out, int pixstride, int bw,int bh); //generates RGBA16F data.
ASTC_PUBLIC void ASTC_Decode_HDR(unsigned char *in, unsigned short *out, int pixstride/*outwidth*/, int layerstride/*outwidth*outheight*/, int bw,int bh,int bd); //generates RGBA16F data.
#endif
#ifdef ASTC_WITH_HDRTEST
ASTC_PUBLIC int ASTC_BlocksAreHDR(unsigned char *in, size_t datasize, int bw, int bh, int bd); //returns true if n consecutive blocks require the HDR profile.
ASTC_PUBLIC int ASTC_BlocksAreHDR(unsigned char *in, size_t datasize, int bw, int bh, int bd); //returns true if n consecutive blocks require the HDR profile (ie: detects when you need to soft-decode for drivers with partial support, as opposed to just always decompressing).
#endif
@ -38,21 +39,24 @@
#if defined(ASTC_WITH_LDR) || defined(ASTC_WITH_HDR)
#define ASTC_WITH_DECODE
#endif
enum
enum astc_status_e
{
ASTC_OKAY,
ASTC_ERROR, //validation errors
ASTC_UNSUPPORTED_FULL, //volume textures... Note: non-hdr profile errors are per-partition, so not an actual block error.
ASTC_RESERVED, //reserved bits. basically an error but might not be in the future.
//valid blocks
ASTC_OKAY, //we can decode at least part of this normally (hdr endpoints may still result in per-endpoint errors).
ASTC_VOID_LDR, //not an error - the block is a single LDR colour, with an RGBA16 colour in the last 8 bytes.
ASTC_VOID_HDR //not an error - the block is a single HDR colour, with an RGBA16F colour in the last 8 bytes.
ASTC_VOID_HDR, //not an error - the block is a single HDR colour, with an RGBA16F colour in the last 8 bytes.
//invalid blocks
ASTC_ERROR, //validation errors
ASTC_UNSUPPORTED, //basically just volume textures
ASTC_RESERVED, //reserved bits. basically an error but might not be in the future.
};
struct astc_block_info
{
unsigned char *in; //the 16 bytes of the block
char blocksize[3];
unsigned char blocksize[3]; //block width, height, depth(1 for 2d).
char status; //0=regular block, -1=error, etc
enum astc_status_e status; //block status/type.
unsigned char dualplane; //two sets of weights instead of one.
unsigned char ccs; //second set applies to this component
@ -68,9 +72,9 @@ struct astc_block_info
unsigned short partindex; //used for deciding which partition each pixel belongs in
struct astc_part
{
char mode; //endpoint modes
unsigned char mode; //endpoint modes
#ifdef ASTC_WITH_HDR
char hdr; //endpoint colour mode - &1=rgb, &2=alpha
unsigned char hdr; //endpoint colour mode - &1=rgb, &2=alpha
#endif
int ep[2][4];
} part[4];
@ -164,8 +168,42 @@ static void ASTC_ReadBlockMode(struct astc_block_info *b)
b->precision = (s>>(9-3))&(1<<3);//P
b->precision |= (s>>4)&1; //p0
if (b->blocksize[2] != 1)
{ //3d blocks have a different layout
b->status = ASTC_UNSUPPORTED_FULL;
{ //3d blocks have a different header layout
#ifdef ASTC_WITH_3D
if (s&3)
{
b->precision|=(s&3)<<1; //p2, p1
b->wcount[0] = ((s>>5)&3)+2, b->wcount[1] = ((s>>7)&3)+2, b->wcount[2] = ((s>>2)&3)+2;
}
else
{
b->precision|=(s&0xc)>>1; //p2, p1
if ((s&0x180)!=0x180)
{
b->dualplane = 0; //always single plane.
b->precision &= 7; //clear the high precision bit (reused for 'b')
if (!(s&0x180))
b->wcount[0] = 6, b->wcount[1] = ((s>>9)&3)+2, b->wcount[2] = ((s>>5)&3)+2;
else if (!(s&0x80))
b->wcount[0] = ((s>>5)&3)+2, b->wcount[1] = 6, b->wcount[2] = ((s>>9)&3)+2;
else
b->wcount[0] = ((s>>5)&3)+2, b->wcount[1] = ((s>>9)&3)+2, b->wcount[2] = 6;
}
else if ((s&0x60)!=0x60)
{
if (!(s&0x60))
b->wcount[0] = 6, b->wcount[1] = 2, b->wcount[2] = 2;
else if (!(s&0x20))
b->wcount[0] = 2, b->wcount[1] = 6, b->wcount[2] = 2;
else //40
b->wcount[0] = 2, b->wcount[1] = 2, b->wcount[2] = 6;
}
else
b->status = ASTC_RESERVED; //reserved (or void extent, but those were handled above)
}
#else
b->status = ASTC_UNSUPPORTED;
#endif
}
else
{
@ -1314,16 +1352,22 @@ static int ASTC_ChoosePartition(int seed, int x, int y, int z, int partitions, i
#ifdef ASTC_WITH_LDR
//Spits out 8-bit RGBA data for a single block. Any HDR blocks will result in the error colour.
//sRGB can be applied by the caller, if needed.
ASTC_PUBLIC void ASTC_Decode_LDR8(unsigned char *in, unsigned char *out, int pixstride, int bw, int bh)
ASTC_PUBLIC void ASTC_Decode_LDR8(unsigned char *in, unsigned char *out, int pixstride, int layerstride, int bw, int bh, int bd)
{
struct astc_block_info b;
int x, y;
int stride = pixstride*4;
#ifdef ASTC_WITH_3D
int z;
layerstride = layerstride*4-(stride*bh);
#else
if (bd != 1)
return; //error!
#endif
b.in = in;
b.blocksize[0] = bw;
b.blocksize[1] = bh;
b.blocksize[2] = 1;
b.blocksize[2] = bd;
ASTC_ReadBlockMode(&b);
if (b.status == ASTC_VOID_LDR)
@ -1347,13 +1391,19 @@ ASTC_PUBLIC void ASTC_Decode_LDR8(unsigned char *in, unsigned char *out, int pix
if (b.status == ASTC_OKAY)
{
#define N b.wcount[0]
#define M b.wcount[1]
int s1=1<<b.dualplane,s2=N<<b.dualplane; //values for 2d blocks (3d blocks will override)
int s3=((bd!=1?N*M:0)+N+1)<<b.dualplane; //small variation for 3d blocks.
int smallblock = (b.blocksize[0]*b.blocksize[1]*b.blocksize[2])<31;
int ds = (1024+b.blocksize[0]/2)/(b.blocksize[0]-1);
int dt = (1024+b.blocksize[1]/2)/(b.blocksize[1]-1);
int planes = 1<<b.dualplane, wstride = b.wcount[0]*planes;
int s, t, v0, w, w00,w01,w10,w11;
int fs, s, ds = (1024+b.blocksize[0]/2)/(b.blocksize[0]-1);
int ft, t, dt = (1024+b.blocksize[1]/2)/(b.blocksize[1]-1);
#ifdef ASTC_WITH_3D
int fr, r, dr = (1024+b.blocksize[2]/2)/(b.blocksize[2]-1);
#endif
int v0, w, w00,w01,w10,w11;
struct astc_part *p;
//int dr = (1024+b.bd/2)/(b.bd-1);
#ifdef ASTC_WITH_HDR
for (x = 0; x < b.partitions; x++)
@ -1367,26 +1417,73 @@ ASTC_PUBLIC void ASTC_Decode_LDR8(unsigned char *in, unsigned char *out, int pix
}
#endif
//for (z = 0; z < bd; z++, out += layerstride-stride*bh)
#ifdef ASTC_WITH_3D
for (z = 0; z < bd; z++, out += layerstride-stride*bh)
#endif
{
//r = ((dr*z)*(b.nweights[2]-1)+32)>>6;
#ifdef ASTC_WITH_3D
r = ((dr*z)*(b.wcount[2]-1)+32)>>6;
fr=r&0xf;
#endif
for (y = 0; y < bh; y++, out += stride)
{
t = ((dt*y)*(b.wcount[1]-1)+32)>>6;
ft=t&0xf;
for (x = 0; x < bw; x++)
{
p = &b.part[ASTC_ChoosePartition(b.partindex, x,y,0, b.partitions, smallblock)];
s = ((ds*x)*(b.wcount[0]-1)+32)>>6;
w11 = ((s&0xf)*(t&0xf)+8) >> 4;
w10 = (t&0xf) - w11;
w01 = (s&0xf) - w11;
w00 = 16 - (s&0xf) - (t&0xf) + w11;
fs=s&0xf;
#ifdef ASTC_WITH_3D
if (bd != 1)
{ //3d blocks use simplex interpolation instead of 8-way interpolation. its easier for hardware but more cycles for us.
if (fs>fr)
{ //figure out which weights/factors to use.
if (ft>fr)
{
if (fs>ft)
s1=1, s2=N, w00=16-fs, w01=fs-ft, w10=ft-fr, w11=fr;
else
s1=N, s2=1, w00=16-ft, w01=ft-fs, w10=fs-fr, w11=fr;
}
else
s1=1, s2=N*M, w00=16-fs, w01=fs-fr, w10=fr-ft, w11=ft;
}
else
{
if (fs>ft)
s1=N*M, s2=1, w00=16-fr, w01=fr-fs, w10=fs-ft, w11=ft;
else
{
if (ft>fr)
s1=N, s2=N*M, w00=16-ft, w01=ft-fr, w10=fr-fs, w11=fs;
else
s1=N*M, s2=N, w00=16-fr, w01=fr-ft, w10=ft-fs, w11=fs;
}
}
v0 = (((s>>4))<<b.dualplane)+(((t>>4))*wstride);
s1 <<= b.dualplane;
s2 <<= b.dualplane;
s2+=s1;
//s3 = (N*M+N+1)<<b.dualplane;
v0 = ((s>>4)+(t>>4)*N+(r>>4)*N*M) << b.dualplane;
}
else
#endif
{
//s1 = 1<<b.dualplane;
//s2 = (N)<<b.dualplane;
//s3 = (N+1)<<b.dualplane;
w11 = (fs*ft+8) >> 4;
w10 = ft - w11;
w01 = fs - w11;
w00 = 16 - fs - ft + w11;
v0 = ((s>>4)+(t>>4)*N) << b.dualplane;
}
w = ( w00*b.weights[v0] +
w01*b.weights[v0+planes] +
w10*b.weights[v0+wstride] +
w11*b.weights[v0+planes+wstride] + 8) >> 4;
w01*b.weights[v0+s1] +
w10*b.weights[v0+s2] +
w11*b.weights[v0+s3] + 8) >> 4;
out[(x<<2)+0] = ((64-w)*p->ep[0][0] + w*p->ep[1][0])>>6;
out[(x<<2)+1] = ((64-w)*p->ep[0][1] + w*p->ep[1][1])>>6;
out[(x<<2)+2] = ((64-w)*p->ep[0][2] + w*p->ep[1][2])>>6;
@ -1396,9 +1493,9 @@ ASTC_PUBLIC void ASTC_Decode_LDR8(unsigned char *in, unsigned char *out, int pix
{ //dual planes has a second set of weights that override a single channel
v0++;
w = ( w00*b.weights[v0] +
w01*b.weights[v0+planes] +
w10*b.weights[v0+wstride] +
w11*b.weights[v0+planes+wstride] + 8) >> 4;
w01*b.weights[v0+s1] +
w10*b.weights[v0+s2] +
w11*b.weights[v0+s3] + 8) >> 4;
out[(x<<2)+b.ccs] = ((64-w)*p->ep[0][b.ccs] + w*p->ep[1][b.ccs])>>6;
}
}
@ -1406,15 +1503,18 @@ ASTC_PUBLIC void ASTC_Decode_LDR8(unsigned char *in, unsigned char *out, int pix
}
}
else
{
for (y = 0; y < bh; y++, out += stride)
for (x = 0; x < bw; x++)
{
out[(x<<2)+0] = 0xff;
out[(x<<2)+1] = 0;
out[(x<<2)+2] = 0xff;
out[(x<<2)+3] = 0xff;
}
{ //error colour == magenta
#ifdef ASTC_WITH_3D
for (z = 0; z < bd; z++, out += layerstride)
#endif
for (y = 0; y < bh; y++, out += stride)
for (x = 0; x < bw; x++)
{
out[(x<<2)+0] = 0xff;
out[(x<<2)+1] = 0;
out[(x<<2)+2] = 0xff;
out[(x<<2)+3] = 0xff;
}
}
}
#endif
@ -1457,15 +1557,22 @@ static unsigned short ASTC_GenHalffloat(int hdr, int rawval)
}
//Spits out half-float RGBA data for a single block.
ASTC_PUBLIC void ASTC_Decode_HDR(unsigned char *in, unsigned short *out, int pixstride, int bw, int bh)
ASTC_PUBLIC void ASTC_Decode_HDR(unsigned char *in, unsigned short *out, int pixstride, int layerstride, int bw, int bh, int bd)
{
int x, y;
int stride = pixstride*4;
struct astc_block_info b;
#ifdef ASTC_WITH_3D
int z;
layerstride = layerstride*4-(stride*bh);
#else
if (bd != 1)
return; //error!
#endif
b.in = in;
b.blocksize[0] = bw;
b.blocksize[1] = bh;
b.blocksize[2] = 1;
b.blocksize[2] = bd;
ASTC_ReadBlockMode(&b);
@ -1503,13 +1610,19 @@ ASTC_PUBLIC void ASTC_Decode_HDR(unsigned char *in, unsigned short *out, int pix
if (b.status == ASTC_OKAY)
{
#define N b.wcount[0]
#define M b.wcount[1]
int s1=1<<b.dualplane,s2=N<<b.dualplane; //values for 2d blocks (3d blocks will override)
int s3=((bd!=1?N*M:0)+N+1)<<b.dualplane; //small variation for 3d blocks.
int smallblock = (b.blocksize[0]*b.blocksize[1]*b.blocksize[2])<31;
int ds = (1024+b.blocksize[0]/2)/(b.blocksize[0]-1);
int dt = (1024+b.blocksize[1]/2)/(b.blocksize[1]-1);
int planes = 1<<b.dualplane, wstride = b.wcount[0]*planes;
int s, t, v0, w, w00,w01,w10,w11;
int fs, s, ds = (1024+b.blocksize[0]/2)/(b.blocksize[0]-1);
int ft, t, dt = (1024+b.blocksize[1]/2)/(b.blocksize[1]-1);
#ifdef ASTC_WITH_3D
int fr, r, dr = (1024+b.blocksize[2]/2)/(b.blocksize[2]-1);
#endif
int v0, w, w00,w01,w10,w11;
struct astc_part *p;
//int dr = (1024+b.bd/2)/(b.bd-1);
for (x = 0; x < b.partitions; x++)
{ //we need to do a little extra processing here
@ -1528,26 +1641,74 @@ ASTC_PUBLIC void ASTC_Decode_HDR(unsigned char *in, unsigned short *out, int pix
}
}
//for (z = 0; z < bd; z++, out += layerstride-stride*bh)
#ifdef ASTC_WITH_3D
for (z = 0; z < bd; z++, out += layerstride)
#endif
{
//r = ((dr*z)*(b.nweights[2]-1)+32)>>6;
#ifdef ASTC_WITH_3D
r = ((dr*z)*(b.wcount[2]-1)+32)>>6;
fr=s&0xf;
#endif
for (y = 0; y < bh; y++, out += stride)
{
t = ((dt*y)*(b.wcount[1]-1)+32)>>6;
ft=s&0xf;
for (x = 0; x < bw; x++)
{
p = &b.part[ASTC_ChoosePartition(b.partindex, x,y,0, b.partitions, smallblock)];
s = ((ds*x)*(b.wcount[0]-1)+32)>>6;
w11 = ((s&0xf)*(t&0xf)+8) >> 4;
w10 = (t&0xf) - w11;
w01 = (s&0xf) - w11;
w00 = 16 - (s&0xf) - (t&0xf) + w11;
fs=s&0xf;
#ifdef ASTC_WITH_3D
if (bd != 1)
{ //3d blocks use simplex interpolation instead of 8-way interpolation. its easier for hardware but more cycles for us.
if (fs>fr)
{ //figure out which weights/factors to use.
if (ft>fr)
{
if (fs>ft)
s1=1, s2=N, w00=16-fs, w01=fs-ft, w10=ft-fr, w11=fr;
else
s1=N, s2=1, w00=16-ft, w01=ft-fs, w10=fs-fr, w11=fr;
}
else
s1=1, s2=N*M, w00=16-fs, w01=fs-fr, w10=fr-ft, w11=ft;
}
else
{
if (fs>ft)
s1=N*M, s2=1, w00=16-fr, w01=fr-fs, w10=fs-ft, w11=ft;
else
{
if (ft>fr)
s1=N, s2=N*M, w00=16-ft, w01=ft-fr, w10=fr-fs, w11=fs;
else
s1=N*M, s2=N, w00=16-fr, w01=fr-ft, w10=ft-fs, w11=fs;
}
}
v0 = (((s>>4))<<b.dualplane)+(((t>>4))*wstride);
s1 <<= b.dualplane;
s2 <<= b.dualplane;
s2+=s1;
//s3 = (N*M+N+1)<<b.dualplane;
v0 = (((s>>4))+((t>>4)*N)+(r>>4)*N*M) << b.dualplane;
}
else
#endif
{
//s1 = 1<<b.dualplane;
//s2 = (N)<<b.dualplane;
//s3 = (N+1)<<b.dualplane;
w11 = (fs*ft+8) >> 4;
w10 = ft - w11;
w01 = fs - w11;
w00 = 16 - fs - ft + w11;
v0 = (((s>>4))+(t>>4)*N) << b.dualplane;
}
w = ( w00*b.weights[v0] +
w01*b.weights[v0+planes] +
w10*b.weights[v0+wstride] +
w11*b.weights[v0+planes+wstride] + 8) >> 4;
w01*b.weights[v0+s1] +
w10*b.weights[v0+s2] +
w11*b.weights[v0+s3] + 8) >> 4;
out[(x<<2)+0] = ASTC_GenHalffloat(p->hdr&1, ((64-w)*p->ep[0][0] + w*p->ep[1][0])>>6);
out[(x<<2)+1] = ASTC_GenHalffloat(p->hdr&1, ((64-w)*p->ep[0][1] + w*p->ep[1][1])>>6);
out[(x<<2)+2] = ASTC_GenHalffloat(p->hdr&1, ((64-w)*p->ep[0][2] + w*p->ep[1][2])>>6);
@ -1557,9 +1718,9 @@ ASTC_PUBLIC void ASTC_Decode_HDR(unsigned char *in, unsigned short *out, int pix
{ //dual planes has a second set of weights that override a single channel
v0++;
w = ( w00*b.weights[v0] +
w01*b.weights[v0+planes] +
w10*b.weights[v0+wstride] +
w11*b.weights[v0+planes+wstride] + 8) >> 4;
w01*b.weights[v0+s1] +
w10*b.weights[v0+s2] +
w11*b.weights[v0+s3] + 8) >> 4;
out[(x<<2)+b.ccs] = ASTC_GenHalffloat(p->hdr&(1<<b.ccs), ((64-w)*p->ep[0][b.ccs] + w*p->ep[1][b.ccs])>>6);
}
}
@ -1567,15 +1728,18 @@ ASTC_PUBLIC void ASTC_Decode_HDR(unsigned char *in, unsigned short *out, int pix
}
}
else
{
for (y = 0; y < bh; y++, out += stride)
for (x = 0; x < bw; x++)
{
out[(x<<2)+0] = 0;//0xf<<10;
out[(x<<2)+1] = 0;
out[(x<<2)+2] = 0;//0xf<<10;
out[(x<<2)+3] = 0xf<<10;
}
{ //error colour == magenta
#ifdef ASTC_WITH_3D
for (z = 0; z < bd; z++, out += layerstride)
#endif
for (y = 0; y < bh; y++, out += stride)
for (x = 0; x < bw; x++)
{
out[(x<<2)+0] = 0xf<<10;
out[(x<<2)+1] = 0;
out[(x<<2)+2] = 0xf<<10;
out[(x<<2)+3] = 0xf<<10;
}
}
}
#endif

View File

@ -330,7 +330,9 @@ static void J_KillAll(void)
#endif
#if SDL_MAJOR_VERSION >= 2
unsigned int MySDL_MapKey(unsigned int sdlkey)
//FIXME: switch to scancodes rather than keysyms
//use SDL_GetKeyName(SDL_GetKeyFromScancode(quaketosdl[qkey])) for keybinds menu
unsigned int MySDL_MapKey(SDL_Keycode sdlkey)
{
switch(sdlkey)
{
@ -740,8 +742,10 @@ static unsigned int tbl_sdltoquakemouse[] =
K_MOUSE1,
K_MOUSE3,
K_MOUSE2,
#if SDL_MAJOR_VERSION < 2
K_MWHEELUP,
K_MWHEELDOWN,
#endif
K_MOUSE4,
K_MOUSE5,
K_MOUSE6,
@ -915,11 +919,38 @@ void Sys_SendKeyEvents(void)
IN_MouseMove(event.motion.which, false, event.motion.xrel, event.motion.yrel, 0, 0);
break;
#if SDL_MAJOR_VERSION >= 2
case SDL_MOUSEWHEEL:
if (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED)
event.wheel.y *= -1;
for (; event.wheel.y > 0; event.wheel.y--)
{
IN_KeyEvent(event.button.which, true, K_MWHEELUP, 0);
IN_KeyEvent(event.button.which, false, K_MWHEELUP, 0);
}
for (; event.wheel.y < 0; event.wheel.y++)
{
IN_KeyEvent(event.button.which, true, K_MWHEELDOWN, 0);
IN_KeyEvent(event.button.which, false, K_MWHEELDOWN, 0);
}
/* for (; event.wheel.x > 0; event.wheel.x--)
{
IN_KeyEvent(event.button.which, true, K_MWHEELRIGHT, 0);
IN_KeyEvent(event.button.which, false, K_MWHEELRIGHT, 0);
}
for (; event.wheel.x < 0; event.wheel.x++)
{
IN_KeyEvent(event.button.which, true, K_MWHEELLEFT, 0);
IN_KeyEvent(event.button.which, false, K_MWHEELLEFT, 0);
}*/
break;
#endif
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
#if SDL_MAJOR_VERSION >= 2
if (event.button.which == SDL_TOUCH_MOUSEID)
break; //ignore legacy touch events.
break; //ignore legacy touch events. SDL_FINGER* events above will handle it (for multitouch)
#endif
//Hmm. SDL allows for 255 buttons, but only defines 5...
if (event.button.button > sizeof(tbl_sdltoquakemouse)/sizeof(tbl_sdltoquakemouse[0]))

View File

@ -60,10 +60,7 @@ qboolean keydown[K_MAX];
char *releasecommand[K_MAX][MAX_INDEVS]; //this is the console command to be invoked when the key is released. should free it.
qbyte releasecommandlevel[K_MAX][MAX_INDEVS]; //and this is the cbuf level it is to be run at.
static void QDECL Con_Selectioncolour_Callback(struct cvar_s *var, char *oldvalue);
extern cvar_t con_displaypossibilities;
cvar_t con_selectioncolour = CVARFC("con_selectioncolour", "0", CVAR_RENDERERCALLBACK, Con_Selectioncolour_Callback);
cvar_t con_echochat = CVAR("con_echochat", "0");
extern cvar_t cl_chatmode;
@ -659,14 +656,6 @@ int Con_ExecuteLine(console_t *con, const char *line)
return true;
}
vec3_t sccolor;
static void QDECL Con_Selectioncolour_Callback(struct cvar_s *var, char *oldvalue)
{
if (qrenderer != QR_NONE)
SCR_StringToRGB(var->string, sccolor, 1);
}
qboolean Key_GetConsoleSelectionBox(console_t *con, int *sx, int *sy, int *ex, int *ey)
{
*sx = *sy = *ex = *ey = 0;
@ -2866,13 +2855,12 @@ void Key_Init (void)
//
// register our functions
//
Cmd_AddCommandAD ("bind",Key_Bind_f, Key_Bind_c, NULL);
Cmd_AddCommandAD ("bind",Key_Bind_f, Key_Bind_c, "Changes the action associated with each keyboard button. Use eg \"bind ctrl+shift+alt+k kill\" for special modifiers (should be used only after more basic modifiers).");
Cmd_AddCommand ("in_bind",Key_Bind_f);
Cmd_AddCommand ("bindlevel",Key_Bind_f);
Cmd_AddCommandAD ("unbind",Key_Unbind_f, Key_Bind_c, NULL);
Cmd_AddCommand ("unbindall",Key_Unbindall_f);
Cmd_AddCommandD ("unbindall",Key_Unbindall_f, "A dangerous command that forgets ALL your key settings. For use only in default.cfg.");
Cvar_Register (&con_selectioncolour, "Console variables");
Cvar_Register (&con_echochat, "Console variables");
}

View File

@ -207,6 +207,7 @@ static qboolean pkg_updating; //when flagged, further changes are blocked until
#else
static const qboolean pkg_updating = false;
#endif
static qboolean pm_packagesinstalled;
//FIXME: these are allocated for the life of the exe. changing basedir should purge the list.
static int numdownloadablelists = 0;
@ -2459,7 +2460,15 @@ static void PM_PackageEnabled(package_t *p)
continue;
COM_FileExtension(dep->name, ext, sizeof(ext));
if (!stricmp(ext, "pak") || !stricmp(ext, "pk3"))
FS_ReloadPackFiles();
{
if (pm_packagesinstalled)
{
pm_packagesinstalled = false;
FS_ChangeGame(fs_manifest, true, false);
}
else
FS_ReloadPackFiles();
}
#ifdef PLUGINS
if ((p->flags & DPF_PLUGIN) && !Q_strncasecmp(dep->name, PLUGINPREFIX, strlen(PLUGINPREFIX)))
Cmd_ExecuteString(va("plug_load %s\n", dep->name), RESTRICT_LOCAL);
@ -2533,29 +2542,31 @@ static int QDECL PM_ExtractFiles(const char *fname, qofs_t fsize, time_t mtime,
}
static void PM_StartADownload(void);
//callback from PM_StartADownload
static void PM_Download_Got(struct dl_download *dl)
typedef struct
{
char native[MAX_OSPATH];
qboolean successful = dl->status == DL_FINISHED;
package_t *p;
char *tempname = dl->user_ctx;
const enum fs_relative temproot = dl->user_num;
qboolean successful;
char *tempname; //z_strduped string, so needs freeing.
enum fs_relative temproot;
char localname[256];
char url[256];
} pmdownloadedinfo_t;
//callback from PM_StartADownload
static void PM_Download_Got(int iarg, void *data)
{
pmdownloadedinfo_t *info = data;
char native[MAX_OSPATH];
qboolean successful = info->successful;
package_t *p;
char *tempname = info->tempname;
const enum fs_relative temproot = info->temproot;
for (p = availablepackages; p ; p=p->next)
{
if (p->download == dl)
if (p == info->p)
break;
}
if (dl->file)
{
if (!VFS_CLOSE(dl->file))
successful = false;
dl->file = NULL;
}
else
successful = false;
pm_packagesinstalled=true;
if (p)
{
@ -2566,7 +2577,7 @@ static void PM_Download_Got(struct dl_download *dl)
if (!successful)
{
Con_Printf("Couldn't download %s (from %s)\n", p->name, dl->url);
Con_Printf("Couldn't download %s (from %s)\n", p->name, info->url);
FS_Remove (tempname, temproot);
Z_Free(tempname);
PM_StartADownload();
@ -2732,12 +2743,43 @@ static void PM_Download_Got(struct dl_download *dl)
Con_Printf("menu_download: %s has no filename info\n", p->name);
}
else
Con_Printf("menu_download: Can't figure out where %s came from (url: %s)\n", dl->localname, dl->url);
Con_Printf("menu_download: Can't figure out where %s came from (url: %s)\n", info->localname, info->url);
FS_Remove (tempname, temproot);
Z_Free(tempname);
PM_StartADownload();
}
static void PM_Download_PreliminaryGot(struct dl_download *dl)
{ //this function is annoying.
//we're on the mainthread, but we might still be waiting for some other thread to complete
//there could be loads of stuff on the callstack. lots of stuff that could get annoyed if we're restarting the entire filesystem, for instance.
//so set up a SECOND callback using a different mechanism...
pmdownloadedinfo_t info;
info.tempname = dl->user_ctx;
info.temproot = dl->user_num;
Q_strncpyz(info.url, dl->url, sizeof(info.url));
Q_strncpyz(info.localname, dl->localname, sizeof(info.localname));
for (info.p = availablepackages; info.p ; info.p=info.p->next)
{
if (info.p->download == dl)
break;
}
info.successful = (dl->status == DL_FINISHED);
if (dl->file)
{
if (!VFS_CLOSE(dl->file))
info.successful = false;
dl->file = NULL;
}
else
info.successful = false;
Cmd_AddTimer(0, PM_Download_Got, 0, &info, sizeof(info));
}
static char *PM_GetTempName(package_t *p)
{
@ -2956,6 +2998,16 @@ int PM_IsApplying(qboolean listsonly)
}
#ifdef WEBCLIENT
static void PM_DownloadsCompleted(int iarg, void *data)
{ //if something installed, then make sure everything is reconfigured properly.
if (pm_packagesinstalled)
{
pm_packagesinstalled = false;
FS_ChangeGame(fs_manifest, true, false);
}
}
//looks for the next package that needs downloading, and grabs it
static void PM_StartADownload(void)
{
@ -3070,7 +3122,7 @@ static void PM_StartADownload(void)
if (tmpfile)
{
p->download = HTTP_CL_Get(mirror, NULL, PM_Download_Got);
p->download = HTTP_CL_Get(mirror, NULL, PM_Download_PreliminaryGot);
if (!p->download)
Con_Printf("Unable to download %s\n", p->name);
}
@ -3100,6 +3152,9 @@ static void PM_StartADownload(void)
}
}
if (pkg_updating && !downloading)
Cmd_AddTimer(0, PM_DownloadsCompleted, 0, NULL, 0);
//clear the updating flag once there's no more activity needed
pkg_updating = downloading;
}
@ -3244,8 +3299,12 @@ void PM_ApplyChanges(void)
//and flag any new/updated ones for a download
for (p = availablepackages; p ; p=p->next)
{
if ((p->flags&DPF_ALLMARKED) && !(p->flags&DPF_ENABLED) && !p->download)
p->trymirrors = ~0u;
if (!p->download)
if (((p->flags & DPF_MANIMARKED) && !(p->flags&DPF_PRESENT)) || //satisfying a manifest merely requires that it be present, not actually enabled.
((p->flags&DPF_MARKED) && !(p->flags&DPF_ENABLED))) //actually enabled stuff requires actual enablement
{
p->trymirrors = ~0u;
}
}
PM_StartADownload(); //and try to do those downloads.
#else
@ -4127,6 +4186,7 @@ typedef struct {
char pathprefix[MAX_QPATH];
int downloadablessequence;
char titletext[128];
char applymessage[128]; //so we can change its text to give it focus
qboolean populated;
} dlmenu_t;
@ -4646,6 +4706,13 @@ static void MD_Download_UpdateStatus(struct emenu_s *m)
else
Q_snprintfz(info->titletext, sizeof(info->titletext), "Downloads (+%u -%u)", addpackages, rempackages);
if (pkg_updating)
Q_snprintfz(info->applymessage, sizeof(info->applymessage), "Apply (please wait)");
else if (addpackages || rempackages)
Q_snprintfz(info->applymessage, sizeof(info->applymessage), "%sApply (+%u -%u)", ((int)(realtime*4)&3)?"^a":"", addpackages, rempackages);
else
Q_snprintfz(info->applymessage, sizeof(info->applymessage), "Apply");
if (!info->populated)
{
for (i = 0; i < numdownloadablelists; i++)
@ -4660,7 +4727,7 @@ static void MD_Download_UpdateStatus(struct emenu_s *m)
info->populated = true;
MC_AddFrameStart(m, 48);
y = 48;
b = MC_AddCommand(m, 48, 320-16, y, "Apply", MD_ApplyDownloads);
b = MC_AddCommand(m, 48, 320-16, y, info->applymessage, MD_ApplyDownloads);
b->rightalign = false;
b->common.tooltip = "Enable/Disable/Download/Delete packages to match any changes made (you will be prompted with a list of the changes that will be made).";
y+=8;

View File

@ -697,7 +697,6 @@ void M_Menu_Audio_f (void)
menubulk_t bulk[] = {
MB_REDTEXT("Sound Options", true),
MB_TEXT("^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082", true),
MB_SPACING(8),
MB_CONSOLECMD("Restart Sound", "snd_restart\n", "Restart audio systems and apply set options."),
MB_SPACING(4),
MB_COMBOCVAR("Output Device", snd_device, (const char**)info->outdevdescs, (const char**)info->outdevnames, "Choose which audio driver and device to use."),
@ -927,6 +926,7 @@ const char *presetexec[] =
"seta r_graphics 1;"
"seta r_renderscale 1;"
"seta gl_texture_anisotropic_filtering 0;"
// end '286'
, // fast options (for deathmatch)
"gl_texturemode ln;"
@ -946,8 +946,10 @@ const char *presetexec[] =
"r_lavastyle 1;"
"r_nolightdir 0;"
"seta gl_simpleitems 0;"
// end fast
, //quakespasm-esque options (for singleplayer faithful).
"gl_texturemode2d l.l;"
"r_part_density 1;"
"gl_polyblend 1;"
"r_dynamic 2;"
@ -973,6 +975,7 @@ const char *presetexec[] =
"seta cl_deadbodyfilter 0;"
"gl_texture_anisotropic_filtering 4;"
"cl_fullpitch 1;maxpitch 90;seta minpitch -90;" //QS has cheaty viewpitch range. some maps require it.
// end spasm
, //vanilla-esque options (for purists).
"cl_fullpitch 0;maxpitch \"\";seta minpitch \"\";" //quakespasm is not vanilla
@ -986,6 +989,7 @@ const char *presetexec[] =
"gl_affinemodels 1;"
"r_softwarebanding 1;" //ugly software banding.
"r_part_classic_square 1;" //blocky baby!
// end vanilla
, // normal (faithful) options, but with content replacement thrown in
//#ifdef MINIMAL
@ -1015,6 +1019,7 @@ const char *presetexec[] =
"r_nolerp 0;"
"r_noframegrouplerp 0;"
"cl_fullpitch 1;maxpitch 90;seta minpitch -90;"
//end normal
, // nice options
// "r_stains 0.75;"
@ -1033,6 +1038,7 @@ const char *presetexec[] =
// "gl_detail 1;"
"r_lightstylesmooth 1;"
"r_deluxemapping 2;"
//end 'nice'
, // realtime options
"r_bloom 1;"
@ -1043,6 +1049,7 @@ const char *presetexec[] =
"r_shadow_realtime_world 1;"
"gl_texture_anisotropic_filtering 16;"
"vid_hardwaregamma 4;" //scene gamma
//end 'realtime'
};
typedef struct fpsmenuinfo_s
@ -1050,18 +1057,21 @@ typedef struct fpsmenuinfo_s
menucombo_t *preset;
} fpsmenuinfo_t;
static void ApplyPreset (int presetnum)
static void ApplyPreset (int presetnum, qboolean doreload)
{
int i;
//this function is written backwards, to ensure things work properly in configs etc.
// TODO: work backwards and only set cvars once
Cbuf_InsertText("\nfs_restart\nvid_reload\n", RESTRICT_LOCAL, true);
if (doreload)
{
forcesaveprompt = true;
Cbuf_InsertText("\nfs_restart\nvid_reload\n", RESTRICT_LOCAL, true);
}
for (i = presetnum; i >= 0; i--)
{
Cbuf_InsertText(presetexec[i], RESTRICT_LOCAL, true);
}
forcesaveprompt = true;
}
void M_Menu_Preset_f (void)
@ -1124,6 +1134,7 @@ void FPS_Preset_f (void)
char *presetfname;
char *arg = Cmd_Argv(1);
int i;
qboolean doreload = true;
if (!*arg)
{
@ -1131,13 +1142,21 @@ void FPS_Preset_f (void)
return;
}
presetfname = va("configs/preset_%s.cfg", arg);
if (COM_FCheckExists(presetfname))
if (!strncmp(arg, "builtin_", 8))
{
char buffer[MAX_OSPATH];
COM_QuotedString(presetfname, buffer, sizeof(buffer), false);
Cbuf_InsertText(va("\nexec %s\nfs_restart\n", buffer), RESTRICT_LOCAL, false);
return;
arg += 8;
doreload = false;
}
else
{
presetfname = va("configs/preset_%s.cfg", arg);
if (COM_FCheckExists(presetfname))
{
char buffer[MAX_OSPATH];
COM_QuotedString(presetfname, buffer, sizeof(buffer), false);
Cbuf_InsertText(va("\nexec %s\nfs_restart\n", buffer), RESTRICT_LOCAL, false);
return;
}
}
if (!stricmp("hdr", arg))
@ -1263,7 +1282,7 @@ void FPS_Preset_f (void)
{
if (!stricmp(presetname[i], arg))
{
ApplyPreset(i);
ApplyPreset(i, doreload);
return;
}
}
@ -3863,7 +3882,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct emenu
}
if (shader->defaulttextures->base)
{
Draw_FunString(0, y, va("%s: %s (%s)", t, shader->defaulttextures->base->ident, shader->defaulttextures->base->subpath));
Draw_FunString(0, y, va("%s: %s (%s)", t, shader->defaulttextures->base->ident, shader->defaulttextures->base->subpath?shader->defaulttextures->base->subpath:""));
y+=8;
R2D_Image(0, y, shader->defaulttextures->base->width, shader->defaulttextures->base->height, 0, 0, 1, 1, shader);
}
@ -4106,14 +4125,14 @@ static void Mods_Draw(int x, int y, struct menucustom_s *c, struct emenu_s *m)
return;
if (mod->manifest)
{
if (mousecursor_y >= y && mousecursor_y < y+8)
if (m->selecteditem == (menuoption_t*)c)
Draw_AltFunString(x, y, mod->manifest->formalname);
else
Draw_FunString(x, y, mod->manifest->formalname);
}
else
{
if (mousecursor_y >= y && mousecursor_y < y+8)
if (m->selecteditem == (menuoption_t*)c)
Draw_AltFunString(x, y, mod->gamedir);
else
Draw_FunString(x, y, mod->gamedir);
@ -4161,8 +4180,8 @@ void M_Menu_Mods_f (void)
for (i = 0; i<1 || Mods_GetMod(i); i++)
{
c = MC_AddCustom(menu, 64, 32+i*8, menu->data, i, NULL);
if (!menu->cursoritem)
menu->cursoritem = (menuoption_t*)c;
// if (!menu->selecteditem)
// menu->selecteditem = (menuoption_t*)c;
c->common.height = 8;
c->draw = Mods_Draw;
c->key = Mods_Key;

View File

@ -1491,7 +1491,7 @@ static void QCBUILTIN PF_R_AddEntityMask(pubprogfuncs_t *prinst, struct globalva
if (csqc_isdarkplaces)
{
//hopelessly inefficient version for compat with DP.
maxe = *prinst->parms->sv_num_edicts;
maxe = *prinst->parms->num_edicts;
for (e=1; e < maxe; e++)
{
ent = (void*)EDICT_NUM_PB(prinst, e);
@ -1522,7 +1522,7 @@ static void QCBUILTIN PF_R_AddEntityMask(pubprogfuncs_t *prinst, struct globalva
}
else
{
maxe = *prinst->parms->sv_num_edicts;
maxe = *prinst->parms->num_edicts;
for (e=1; e < maxe; e++)
{
ent = (void*)EDICT_NUM_PB(prinst, e);
@ -3623,7 +3623,7 @@ void CSQC_ResetTrails(void)
if (!prinst)
return;
for (i = 0; i < *prinst->parms->sv_num_edicts; i++)
for (i = 0; i < *prinst->parms->num_edicts; i++)
{
ent = (csqcedict_t*)EDICT_NUM_PB(prinst, i);
ent->trailstate = NULL;
@ -7917,9 +7917,12 @@ qboolean CSQC_Init (qboolean anycsqc, const char *csprogsname, unsigned int chec
csqcprogparms.autocompile = PR_COMPILEIGNORE;//enum {PR_NOCOMPILE, PR_COMPILENEXIST, PR_COMPILECHANGED, PR_COMPILEALWAYS} autocompile;
csqcprogparms.gametime = &csqctime;
#ifdef MULTITHREAD
csqcprogparms.usethreadedgc = pr_gc_threaded.ival;
#endif
csqcprogparms.sv_edicts = (struct edict_s **)&csqc_world.edicts;
csqcprogparms.sv_num_edicts = &csqc_world.num_edicts;
csqcprogparms.edicts = (struct edict_s **)&csqc_world.edicts;
csqcprogparms.num_edicts = &csqc_world.num_edicts;
csqcprogparms.useeditor = QCEditor;//void (*useeditor) (char *filename, int line, int nump, char **parms);
csqcprogparms.user = &csqc_world;

View File

@ -886,9 +886,9 @@ void QCBUILTIN PF_CL_uploadimage (pubprogfuncs_t *prinst, struct globalvars_s *p
}
else
{
unsigned int blockbytes, blockwidth, blockheight;
unsigned int blockbytes, blockwidth, blockheight, blockdepth;
//get format info
Image_BlockSizeForEncoding(format, &blockbytes, &blockwidth, &blockheight);
Image_BlockSizeForEncoding(format, &blockbytes, &blockwidth, &blockheight, &blockdepth);
//round up as appropriate
blockwidth = ((width+blockwidth-1)/blockwidth)*blockwidth;
blockheight = ((height+blockheight-1)/blockheight)*blockheight;
@ -1718,12 +1718,12 @@ void QCBUILTIN PF_menu_findchain (pubprogfuncs_t *prinst, struct globalvars_s *p
menuedict_t *ent, *chain; //note, all edicts share the common header, but don't use it's fields!
eval_t *val;
chain = (menuedict_t *) *prinst->parms->sv_edicts;
chain = (menuedict_t *) *prinst->parms->edicts;
f = G_INT(OFS_PARM0)+prinst->fieldadjust;
s = PR_GetStringOfs(prinst, OFS_PARM1);
for (i = 1; i < *prinst->parms->sv_num_edicts; i++)
for (i = 1; i < *prinst->parms->num_edicts; i++)
{
ent = (menuedict_t *)EDICT_NUM_PB(prinst, i);
if (ent->ereftype == ER_FREE)
@ -1750,12 +1750,12 @@ void QCBUILTIN PF_menu_findchainfloat (pubprogfuncs_t *prinst, struct globalvars
menuedict_t *ent, *chain; //note, all edicts share the common header, but don't use it's fields!
eval_t *val;
chain = (menuedict_t *) *prinst->parms->sv_edicts;
chain = (menuedict_t *) *prinst->parms->edicts;
f = G_INT(OFS_PARM0)+prinst->fieldadjust;
s = G_FLOAT(OFS_PARM1);
for (i = 1; i < *prinst->parms->sv_num_edicts; i++)
for (i = 1; i < *prinst->parms->num_edicts; i++)
{
ent = (menuedict_t*)EDICT_NUM_PB(prinst, i);
if (ent->ereftype == ER_FREE)
@ -1779,12 +1779,12 @@ void QCBUILTIN PF_menu_findchainflags (pubprogfuncs_t *prinst, struct globalvars
menuedict_t *ent, *chain; //note, all edicts share the common header, but don't use it's fields!
eval_t *val;
chain = (menuedict_t *) *prinst->parms->sv_edicts;
chain = (menuedict_t *) *prinst->parms->edicts;
f = G_INT(OFS_PARM0)+prinst->fieldadjust;
s = G_FLOAT(OFS_PARM1);
for (i = 1; i < *prinst->parms->sv_num_edicts; i++)
for (i = 1; i < *prinst->parms->num_edicts; i++)
{
ent = (menuedict_t*)EDICT_NUM_PB(prinst, i);
if (ent->ereftype == ER_FREE)
@ -2985,9 +2985,12 @@ qboolean MP_Init (void)
menuprogparms.autocompile = PR_COMPILEIGNORE;//PR_COMPILEEXISTANDCHANGED;//enum {PR_NOCOMPILE, PR_COMPILENEXIST, PR_COMPILECHANGED, PR_COMPILEALWAYS} autocompile;
menuprogparms.gametime = &menutime;
#ifdef MULTITHREAD
menuprogparms.usethreadedgc = pr_gc_threaded.ival;
#endif
menuprogparms.sv_edicts = (struct edict_s **)&menu_edicts;
menuprogparms.sv_num_edicts = &num_menu_edicts;
menuprogparms.edicts = (struct edict_s **)&menu_edicts;
menuprogparms.num_edicts = &num_menu_edicts;
menuprogparms.useeditor = QCEditor;//void (*useeditor) (char *filename, int line, int nump, char **parms);
menuprogparms.user = &menu_world;

View File

@ -365,8 +365,8 @@ typedef enum
WG_LOADER = 1,
WG_COUNT = 2 //main and loaders
} wgroup_t;
void COM_AddWork(wgroup_t thread, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b);
void COM_InsertWork(wgroup_t tg, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b);
void COM_AddWork(wgroup_t thread, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b); //low priority
void COM_InsertWork(wgroup_t tg, void(*func)(void *ctx, void *data, size_t a, size_t b), void *ctx, void *data, size_t a, size_t b); //high priority
qboolean COM_HasWork(void);
void COM_WorkerFullSync(void);
void COM_DestroyWorkerThread(void);

View File

@ -500,7 +500,7 @@ apic_t *R2D_LoadAtlasedPic(const char *name)
}
if (!atlas.tex)
atlas.tex = Image_CreateTexture(va("fte_atlas%i", atlasid), NULL, IF_NOMIPMAP);
atlas.tex = Image_CreateTexture(va("fte_atlas%i", atlasid), NULL, IF_NOMIPMAP|IF_NOMIPMAP);
if (!atlas.shader)
{
atlas.shader = R_RegisterShader(va("fte_atlas%i", atlasid), SUF_NONE,
@ -1871,7 +1871,11 @@ texid_t R2D_RT_Configure(const char *id, int width, int height, uploadfmt_t rtfm
if (rtfmt)
{
tid->flags = (tid->flags & ~(IF_NEAREST|IF_LINEAR)) | (imageflags & (IF_NEAREST|IF_LINEAR));
if (tid->flags != ((tid->flags & ~(IF_NEAREST|IF_LINEAR)) | (imageflags & (IF_NEAREST|IF_LINEAR))))
{
tid->flags = ((tid->flags & ~(IF_NEAREST|IF_LINEAR)) | (imageflags & (IF_NEAREST|IF_LINEAR)));
tid->width = -1;
}
Image_Upload(tid, rtfmt, NULL, NULL, width, height, imageflags);
tid->width = width;
tid->height = height;

View File

@ -1429,6 +1429,19 @@ static void Surf_BuildLightMap (model_t *currentmodel, msurface_t *surf, int map
{
for (i=0 ; i<size*3 ; i++)
blocklights[i] = r_fullbright.value*255*256;
if (!surf->samples)
{
surf->cached_light[0] = d_lightstylevalue[0];
surf->cached_colour[0] = cl_lightstyle[0].colourkey;
}
else
{
for (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)
{
surf->cached_light[maps] = d_lightstylevalue[surf->styles[maps]];
surf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;
}
}
}
else if (!currentmodel->lightdata)
{
@ -1441,8 +1454,8 @@ static void Surf_BuildLightMap (model_t *currentmodel, msurface_t *surf, int map
/*no samples, but map is otherwise lit = pure black*/
for (i=0 ; i<size*3 ; i++)
blocklights[i] = 0;
surf->cached_light[0] = 0;
surf->cached_colour[0] = 0;
surf->cached_light[0] = d_lightstylevalue[0];
surf->cached_colour[0] = cl_lightstyle[0].colourkey;
}
else
{
@ -1556,8 +1569,19 @@ static void Surf_BuildLightMap (model_t *currentmodel, msurface_t *surf, int map
{ //r_fullbright is meant to be a scaler.
for (i=0 ; i<size ; i++)
blocklights[i] = r_fullbright.value*255*256;
surf->cached_light[0] = d_lightstylevalue[0];
surf->cached_colour[0] = cl_lightstyle[0].colourkey;
if (!surf->samples)
{
surf->cached_light[0] = d_lightstylevalue[0];
surf->cached_colour[0] = cl_lightstyle[0].colourkey;
}
else
{
for (maps = 0 ; maps < MAXCPULIGHTMAPS && surf->styles[maps] != INVALID_LIGHTSTYLE ; maps++)
{
surf->cached_light[maps] = d_lightstylevalue[surf->styles[maps]];
surf->cached_colour[maps] = cl_lightstyle[surf->styles[maps]].colourkey;
}
}
}
else if (!currentmodel->lightdata)
{ //no scalers here.
@ -1951,8 +1975,8 @@ void Surf_RenderDynamicLightmaps (msurface_t *fa)
// check for lightmap modification
if (!fa->samples)
{
if (fa->cached_light[0] != 0
|| fa->cached_colour[0] != 0)
if (fa->cached_light[0] != d_lightstylevalue[0]
|| fa->cached_colour[0] != cl_lightstyle[0].colourkey)
goto dynamic;
}
else
@ -1994,8 +2018,8 @@ static void Surf_RenderDynamicLightmaps_Worker (model_t *wmodel, msurface_t *fa,
// check for lightmap modification
if (!fa->samples)
{
if (fa->cached_light[0] != 0
|| fa->cached_colour[0] != 0)
if (fa->cached_light[0] != d_lightstylevalue[0]
|| fa->cached_colour[0] != cl_lightstyle[0].colourkey)
goto dynamic;
}
else
@ -3264,7 +3288,7 @@ void R_GenWorldEBO(void *ctx, void *data, size_t a, size_t b)
pvs = es->wmodel->funcs.ClusterPVS(es->wmodel, es->cluster[1], &es->pvs, PVM_MERGE);
}
else
pvs = es->wmodel->funcs.ClusterPVS(es->wmodel, es->cluster[0], &es->pvs, PVM_FAST);
pvs = es->wmodel->funcs.ClusterPVS(es->wmodel, es->cluster[0], &es->pvs, PVM_REPLACE);
#if defined(Q2BSPS) || defined(Q3BSPS)
if (es->wmodel->fromgame == fg_quake2 || es->wmodel->fromgame == fg_quake3)
@ -3361,19 +3385,39 @@ void Surf_DrawWorld (void)
#ifdef Q1BSPS
else if (currentmodel->fromgame == fg_quake || currentmodel->fromgame == fg_halflife)
{
int i = cl_max_lightstyles;
if (webostate && !webogenerating)
for (i = 0; i < cl_max_lightstyles; i++)
if (!webogenerating)
{
qboolean gennew = false;
if (!webostate)
gennew = true; //generate an initial one, if we can.
if (!gennew && webostate)
{
if (webostate->lightstylevalues[i] != d_lightstylevalue[i])
break;
int i = cl_max_lightstyles;
for (i = 0; i < cl_max_lightstyles; i++)
{
if (webostate->lightstylevalues[i] != d_lightstylevalue[i])
{ //a lightstyle changed. something needs to be rebuilt. FIXME: should probably have a bitmask for whether the lightstyle is relevant...
gennew = true;
break;
}
}
}
if (webostate && i == cl_max_lightstyles)
{
}
else
{
if (!webogenerating)
if (!gennew && webostate && (webostate->cluster[0] != r_viewcluster || webostate->cluster[1] != r_viewcluster2))
{
if (webostate->pvs.buffersize != currentmodel->pvsbytes || r_viewcluster2 != -1)
gennew = true; //o.O
else if (memcmp(webostate->pvs.buffer, webostate->wmodel->funcs.ClusterPVS(webostate->wmodel, r_viewcluster, NULL, PVM_FAST), currentmodel->pvsbytes))
gennew = true;
else
{ //okay, so the pvs didn't change despite the clusters changing. this happens when using unvised maps or lots of func_detail
//just hack the cluster numbers so we don't have to do the memcmp above repeatedly for no reason.
webostate->cluster[0] = r_viewcluster;
webostate->cluster[1] = r_viewcluster2;
}
}
if (gennew)
{
int i;
if (!currentmodel->numbatches)
@ -3820,8 +3864,8 @@ int Surf_NewLightmaps(int count, int width, int height, uploadfmt_t fmt, qboolea
int first = numlightmaps;
int i;
unsigned int pixbytes, pixw, pixh;
unsigned int dpixbytes, dpixw, dpixh;
unsigned int pixbytes, pixw, pixh, pixd;
unsigned int dpixbytes, dpixw, dpixh, dpixd;
uploadfmt_t dfmt;
if (!count)
@ -3834,8 +3878,8 @@ int Surf_NewLightmaps(int count, int width, int height, uploadfmt_t fmt, qboolea
Con_Print("WARNING: Deluxemapping with odd number of lightmaps\n");
}
Image_BlockSizeForEncoding(fmt, &pixbytes, &pixw, &pixh);
if (pixw != 1 || pixh != 1)
Image_BlockSizeForEncoding(fmt, &pixbytes, &pixw, &pixh, &pixd);
if (pixw != 1 || pixh != 1 || pixd != 1)
return -1; //compressed formats are unsupported
dfmt = PTI_A2BGR10; //favour this one, because it tends to be slightly faster.
if (!sh_config.texfmt[dfmt])
@ -3844,7 +3888,9 @@ int Surf_NewLightmaps(int count, int width, int height, uploadfmt_t fmt, qboolea
dfmt = PTI_RGBX8;
if (!sh_config.texfmt[dfmt])
dfmt = PTI_RGB8;
Image_BlockSizeForEncoding(dfmt, &dpixbytes, &dpixw, &dpixh);
Image_BlockSizeForEncoding(dfmt, &dpixbytes, &dpixw, &dpixh, &dpixd);
if (dpixw != 1 || dpixh != 1 || dpixd != 1)
return -1; //compressed formats are unsupported
Sys_LockMutex(com_resourcemutex);

View File

@ -478,7 +478,7 @@ 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);
void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes, unsigned int *blockwidth, unsigned int *blockheight, unsigned int *blockdepth);
const char *Image_FormatName(uploadfmt_t encoding);
qboolean Image_FormatHasAlpha(uploadfmt_t encoding);
image_t *Image_LoadTexture (const char *identifier, int width, int height, uploadfmt_t fmt, void *data, unsigned int flags);

View File

@ -162,6 +162,7 @@ cvar_t r_dynamic = CVARFD ("r_dynamic", IFMINIMAL("0","1"),
cvar_t r_temporalscenecache = CVARFD ("r_temporalscenecache", "0", CVAR_ARCHIVE, "Controls whether to generate+reuse a scene cache over multiple frames. This is generated on a separate thread to avoid any associated costs. This can significantly boost framerates on complex maps, but can also stress the gpu more (performance tradeoff that varies per map). An outdated cache may be used if the cache takes too long to build (eg: lightmap animations), which could cause the odd glitch when moving fast (but retain more consistent framerates - another tradeoff).\n0: Tranditional quake rendering.\n1: Generate+Use the scene cache.");
cvar_t r_fastturb = CVARF ("r_fastturb", "0",
CVAR_SHADERSYSTEM);
cvar_t r_skycloudalpha = CVARFD ("r_skycloudalpha", "1", CVAR_RENDERERLATCH, "Controls how opaque the front layer of legacy scrolling skies should be.");
cvar_t r_fastsky = CVARF ("r_fastsky", "0",
CVAR_ARCHIVE);
cvar_t r_fastskycolour = CVARF ("r_fastskycolour", "0",
@ -981,6 +982,7 @@ void Renderer_Init(void)
Cvar_Register (&r_nolightdir, GRAPHICALNICETIES);
Cvar_Register (&r_fastturb, GRAPHICALNICETIES);
Cvar_Register (&r_skycloudalpha, GRAPHICALNICETIES);
Cvar_Register (&r_fastsky, GRAPHICALNICETIES);
Cvar_Register (&r_fastskycolour, GRAPHICALNICETIES);
Cvar_Register (&r_wateralpha, GRAPHICALNICETIES);

View File

@ -161,6 +161,7 @@ typedef enum uploadfmt
PTI_EAC_RG11, /*8bpp*/ //useful for normalmaps (calculate blue)
PTI_EAC_RG11_SNORM, /*8bpp*/ //useful for normalmaps (calculate blue)
//astc... zomg.
#define PTI_ASTC_FIRST PTI_ASTC_4X4_LDR
PTI_ASTC_4X4_LDR, /*8bpp*/ //ldr/srgb/hdr formats are technically all the same.
PTI_ASTC_5X4_LDR, /*6.40*/ //srgb formats are different because of an extra srgb lookup step
PTI_ASTC_5X5_LDR, /*5.12*/ //ldr formats are identical to hdr except for the extended colour modes disabled.
@ -175,6 +176,19 @@ typedef enum uploadfmt
PTI_ASTC_10X10_LDR, /*1.28*/
PTI_ASTC_12X10_LDR, /*1.07*/
PTI_ASTC_12X12_LDR, /*0.89*/
// #define ASTC3D
#ifdef ASTC3D
PTI_ASTC_3X3X3_LDR, /*4.74*/ //astc volume ldr textures are worth tracking only to provide hints to cache them as 8bit instead of 16bit (reducing gpu cache needed).
PTI_ASTC_4X3X3_LDR, /*3.56*/
PTI_ASTC_4X4X3_LDR, /*2.67*/
PTI_ASTC_4X4X4_LDR, /*2.00*/
PTI_ASTC_5X4X4_LDR, /*1.60*/
PTI_ASTC_5X5X4_LDR, /*1.28*/
PTI_ASTC_5X5X5_LDR, /*1.02*/
PTI_ASTC_6X5X5_LDR, /*0.85*/
PTI_ASTC_6X6X5_LDR, /*0.71*/
PTI_ASTC_6X6X6_LDR, /*0.59*/
#endif
PTI_ASTC_4X4_SRGB,
PTI_ASTC_5X4_SRGB,
PTI_ASTC_5X5_SRGB,
@ -189,6 +203,18 @@ typedef enum uploadfmt
PTI_ASTC_10X10_SRGB,
PTI_ASTC_12X10_SRGB,
PTI_ASTC_12X12_SRGB,
#ifdef ASTC3D
PTI_ASTC_3X3X3_SRGB,
PTI_ASTC_4X3X3_SRGB,
PTI_ASTC_4X4X3_SRGB,
PTI_ASTC_4X4X4_SRGB,
PTI_ASTC_5X4X4_SRGB,
PTI_ASTC_5X5X4_SRGB,
PTI_ASTC_5X5X5_SRGB,
PTI_ASTC_6X5X5_SRGB,
PTI_ASTC_6X6X5_SRGB,
PTI_ASTC_6X6X6_SRGB,
#endif
PTI_ASTC_4X4_HDR, //these are not strictly necessary, and are likely to be treated identically to the ldr versions, but they may use extra features that the hardware does not support
PTI_ASTC_5X4_HDR,
PTI_ASTC_5X5_HDR,
@ -203,8 +229,21 @@ typedef enum uploadfmt
PTI_ASTC_10X10_HDR,
PTI_ASTC_12X10_HDR,
PTI_ASTC_12X12_HDR,
#define PTI_ASTC_FIRST PTI_ASTC_4X4_LDR
#ifdef ASTC3D
PTI_ASTC_3X3X3_HDR,
PTI_ASTC_4X3X3_HDR,
PTI_ASTC_4X4X3_HDR,
PTI_ASTC_4X4X4_HDR,
PTI_ASTC_5X4X4_HDR,
PTI_ASTC_5X5X4_HDR,
PTI_ASTC_5X5X5_HDR,
PTI_ASTC_6X5X5_HDR,
PTI_ASTC_6X6X5_HDR,
PTI_ASTC_6X6X6_HDR,
#define PTI_ASTC_LAST PTI_ASTC_6X6X6_HDR
#else
#define PTI_ASTC_LAST PTI_ASTC_12X12_HDR
#endif
//depth formats
PTI_DEPTH16,

View File

@ -432,16 +432,16 @@ static qboolean OpenAL_LoadCache(oalinfo_t *oali, unsigned int *bufptr, sfxcache
{
unsigned int fmt;
unsigned int size;
switch(sc->width)
switch(sc->format)
{
#ifdef FTE_TARGET_WEB
case 0:
case QAF_BLOB:
palGenBuffers(1, bufptr);
emscriptenfte_al_loadaudiofile(*bufptr, sc->data, sc->length);
//alIsBuffer will report false until success or failure...
return true; //but we do have a 'proper' reference to the buffer.
#endif
case 1:
case QAF_S8:
if (sc->numchannels == 2)
{
fmt = AL_FORMAT_STEREO8;
@ -453,7 +453,7 @@ static qboolean OpenAL_LoadCache(oalinfo_t *oali, unsigned int *bufptr, sfxcache
size = sc->length*1;
}
break;
case 2:
case QAF_S16:
if (sc->numchannels == 2)
{
fmt = AL_FORMAT_STEREO16;
@ -466,7 +466,7 @@ static qboolean OpenAL_LoadCache(oalinfo_t *oali, unsigned int *bufptr, sfxcache
}
break;
#ifdef MIXER_F32
case 4:
case QAF_F32:
if (!oali->canfloataudio)
return false;
if (sc->numchannels == 2)
@ -497,69 +497,70 @@ static qboolean OpenAL_LoadCache(oalinfo_t *oali, unsigned int *bufptr, sfxcache
}
else if (volume != 1)
{
if (sc->width == 1)
switch(sc->format)
{
unsigned char *tmp = malloc(size);
char *src = sc->data;
int i;
for (i = 0; i < size; i++)
case QAF_S8:
{
tmp[i] = src[i]*volume+128; //signed->unsigned
unsigned char *tmp = malloc(size);
char *src = sc->data;
int i;
for (i = 0; i < size; i++)
tmp[i] = src[i]*volume+128; //signed->unsigned
palBufferData(*bufptr, fmt, tmp, size, sc->speed);
free(tmp);
}
palBufferData(*bufptr, fmt, tmp, size, sc->speed);
free(tmp);
}
else if (sc->width == 2)
{
short *tmp = malloc(size);
short *src = (short*)sc->data;
int i;
for (i = 0; i < (size>>1); i++)
break;
case QAF_S16:
{
tmp[i] = bound(-32767, src[i]*volume, 32767); //signed.
short *tmp = malloc(size);
short *src = (short*)sc->data;
int i;
for (i = 0; i < (size>>1); i++)
tmp[i] = bound(-32767, src[i]*volume, 32767); //signed.
palBufferData(*bufptr, fmt, tmp, size, sc->speed);
free(tmp);
}
palBufferData(*bufptr, fmt, tmp, size, sc->speed);
free(tmp);
}
break;
#ifdef MIXER_F32
else if (sc->width == 4)
{
float *tmp = malloc(size);
float *src = (float*)sc->data;
int i;
for (i = 0; i < (size>>2); i++)
case QAF_F32:
{
tmp[i] = src[i]*volume; //signed. oversaturation isn't my problem
float *tmp = malloc(size);
float *src = (float*)sc->data;
int i;
for (i = 0; i < (size>>2); i++)
tmp[i] = src[i]*volume; //signed. oversaturation isn't my problem
palBufferData(*bufptr, fmt, tmp, size, sc->speed);
free(tmp);
}
palBufferData(*bufptr, fmt, tmp, size, sc->speed);
free(tmp);
}
break;
#endif
}
}
else
{
if (sc->width == 1)
switch(sc->format)
{
unsigned char *tmp = malloc(size);
char *src = sc->data;
int i;
for (i = 0; i < size; i++)
case QAF_S8:
{
tmp[i] = src[i]+128;
unsigned char *tmp = malloc(size);
char *src = sc->data;
int i;
for (i = 0; i < size; i++)
{
tmp[i] = src[i]+128;
}
palBufferData(*bufptr, fmt, tmp, size, sc->speed);
free(tmp);
}
palBufferData(*bufptr, fmt, tmp, size, sc->speed);
free(tmp);
}
else if (sc->width == 2 || sc->width == 4)
{
#if 0
short *tmp = malloc(size);
memcpy(tmp, sc->data, size);
palBufferData(*bufptr, fmt, tmp, size, sc->speed);
free(tmp);
#else
palBufferData(*bufptr, fmt, sc->data, size, sc->speed);
break;
//case QAF_U8:
case QAF_S16:
//case QAF_S32:
#ifdef MIXER_F32
case QAF_F32:
#endif
palBufferData(*bufptr, fmt, sc->data, size, sc->speed);
break;
}
}
@ -833,7 +834,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, chanupdat
else
{
offset = pos - sbuf.soundoffset;
sbuf.data += offset * sc->width*sc->numchannels;
sbuf.data += offset * QAF_BYTES(sc->format)*sc->numchannels;
sbuf.length -= offset;
}
sbuf.soundoffset = 0;
@ -852,7 +853,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, chanupdat
{ //decoder isn't ready yet, but didn't signal an error/eof. queue a little silence, because that's better than constant micro stutters
sfxcache_t silence;
silence.speed = snd_speed;
silence.width = 2;
silence.format = QAF_S16;
silence.numchannels = 1;
silence.data = NULL;
silence.length = 0.1 * silence.speed;
@ -881,7 +882,7 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, chanupdat
{ //queue 0.1 secs if we're starting/resetting a new stream this is to try to cover up discontinuities caused by packetloss or whatever
sfxcache_t silence;
silence.speed = snd_speed;
silence.width = 2;
silence.format = QAF_S16;
silence.numchannels = 1;
silence.data = NULL;
silence.length = 0.1 * silence.speed;

View File

@ -3166,18 +3166,25 @@ float S_GetChannelLevel(int entnum, int entchannel)
{
spos -= scache->soundoffset;
spos *= scache->numchannels;
switch(scache->width)
switch(scache->format)
{
case 1:
case QAF_S8:
for (j = 0; j < scache->numchannels; j++) //average the channels
result += abs(((signed char*)scache->data)[spos+j]);
result /= scache->numchannels*127.0;
break;
case 2:
case QAF_S16:
for (j = 0; j < scache->numchannels; j++) //average the channels
result += abs(((signed short*)scache->data)[spos+j]);
result /= scache->numchannels*32767.0;
break;
#ifdef MIXER_F32
case QAF_F32:
for (j = 0; j < scache->numchannels; j++) //average the channels
result += fabs(((float*)scache->data)[spos+j]);
result /= scache->numchannels;
break;
#endif
}
}
else
@ -4124,14 +4131,14 @@ void S_SoundList_f(void)
Con_Printf("?( ) : %s\n", sfx->name);
continue;
}
size = (sc->soundoffset+sc->length)*sc->width*(sc->numchannels);
size = (sc->soundoffset+sc->length)*QAF_BYTES(sc->format)*(sc->numchannels);
duration = (sc->soundoffset+sc->length) / sc->speed;
total += size;
if (sfx->loopstart >= 0)
Con_Printf ("L");
else
Con_Printf (" ");
Con_Printf("(%2db%2ic) %6i %2is : %s\n",sc->width*8, sc->numchannels, size, duration, sfx->name);
Con_Printf("(%2db%2ic) %6i %2is : %s\n",QAF_BYTES(sc->format)*8, sc->numchannels, size, duration, sfx->name);
}
Con_Printf ("Total resident: %i\n", total);
@ -4179,7 +4186,7 @@ typedef struct {
sfx_t *sfx;
int numchannels;
int width;
qaudiofmt_t format;
int length;
void *data;
} streaming_t;
@ -4202,7 +4209,7 @@ sfxcache_t *QDECL S_Raw_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start,
buf->numchannels = s->numchannels;
buf->soundoffset = 0;
buf->speed = snd_speed;
buf->width = s->width;
buf->format = s->format;
}
if (start >= s->length)
return NULL; //eof...
@ -4226,7 +4233,7 @@ void QDECL S_Raw_Purge(sfx_t *sfx)
}
//streaming audio. //this is useful when there is one source, and the sound is to be played with no attenuation
void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels, int width, float volume)
void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels, qaudiofmt_t format, float volume)
{
soundcardinfo_t *si;
int i;
@ -4292,7 +4299,7 @@ void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels,
s->sfx->loadstate = SLS_LOADED;
s->numchannels = channels;
s->width = width;
s->format = format;
s->data = NULL;
s->length = 0;
@ -4302,9 +4309,9 @@ void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels,
}
S_LockMixer();
if (s->width != width || s->numchannels != channels)
if (s->format != format || s->numchannels != channels)
{
s->width = width;
s->format = format;
s->numchannels = channels;
s->length = 0;
Con_Printf("Restarting raw stream\n");
@ -4352,8 +4359,8 @@ void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels,
}
}
newcache = BZ_Malloc((spare+outsamples) * (s->numchannels) * s->width);
memcpy(newcache, (qbyte*)s->data + prepadl * (s->numchannels) * s->width, spare * (s->numchannels) * s->width);
newcache = BZ_Malloc((spare+outsamples) * (s->numchannels) * QAF_BYTES(s->format));
memcpy(newcache, (qbyte*)s->data + prepadl * (s->numchannels) * QAF_BYTES(s->format), spare * (s->numchannels) * QAF_BYTES(s->format));
BZ_Free(s->data);
s->data = newcache;
@ -4362,15 +4369,15 @@ void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels,
{
extern cvar_t snd_linearresample_stream;
short *outpos = (short *)((char*)s->data + spare * (s->numchannels) * s->width);
short *outpos = (short *)((char*)s->data + spare * (s->numchannels) * QAF_BYTES(s->format));
SND_ResampleStream(data,
speed,
width,
format,
channels,
samples,
outpos,
snd_speed,
s->width,
s->format,
s->numchannels,
snd_linearresample_stream.ival);
}

View File

@ -28,7 +28,7 @@ typedef struct
{
int format;
int rate;
int width;
int bitwidth;
int numchannels;
int loopstart;
int samples;
@ -295,7 +295,7 @@ qbyte *S_Alloc (int size);
// SND_ResampleStream: takes a sound stream and converts with given parameters. Limited to
// 8-16-bit signed conversions and mono-to-mono/stereo-to-stereo conversions.
// Not an in-place algorithm.
void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int insamps, void *out, int outrate, int outwidth, int outchannels, int resampstyle)
void SND_ResampleStream (void *in, int inrate, qaudiofmt_t informat, int inchannels, int insamps, void *out, int outrate, qaudiofmt_t outformat, int outchannels, int resampstyle)
{
double scale;
signed char *in8 = (signed char *)in;
@ -308,17 +308,17 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
if (insamps <= 0)
return;
if (inchannels == outchannels && inwidth == outwidth && inrate == outrate)
if (inchannels == outchannels && informat == outformat && inrate == outrate)
{
memcpy(out, in, inwidth*insamps*inchannels);
memcpy(out, in, informat*insamps*inchannels);
return;
}
if (inchannels == 1 && outchannels == 1)
{
if (inwidth == 1)
if (informat == QAF_S8)
{
if (outwidth == 1)
if (outformat == QAF_S8)
{
if (inrate < outrate) // upsample
{
@ -336,7 +336,7 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
}
return;
}
else
else if (outformat == QAF_S16)
{
if (inrate == outrate) // quick convert
QUICKCONVERT(in8, insamps, out16, 8, 0)
@ -357,9 +357,9 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
return;
}
}
else // 16-bit
else if (informat == QAF_S16) // 16-bit
{
if (outwidth == 2)
if (outformat == QAF_S16)
{
if (inrate < outrate) // upsample
{
@ -377,7 +377,7 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
}
return;
}
else
else if (outformat == QAF_S8)
{
if (inrate == outrate) // quick convert
QUICKCONVERT(in16, insamps, out8, 0, 8)
@ -401,9 +401,9 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
}
else if (outchannels == 2 && inchannels == 2)
{
if (inwidth == 1)
if (informat == QAF_S8)
{
if (outwidth == 1)
if (outformat == QAF_S8)
{
if (inrate < outrate) // upsample
{
@ -419,6 +419,7 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
else
STANDARDRESCALESTEREO(in8, inrate, insamps, out8, outrate, 0, 0)
}
return;
}
else
{
@ -443,9 +444,9 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
}
}
}
else // 16-bit
else if (informat == QAF_S16) // 16-bit
{
if (outwidth == 2)
if (outformat == QAF_S16)
{
if (inrate < outrate) // upsample
{
@ -462,7 +463,7 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
STANDARDRESCALESTEREO(in16, inrate, insamps, out16, outrate, 0, 0)
}
}
else
else if (outformat == QAF_S8)
{
if (inrate == outrate) // quick convert
{
@ -489,9 +490,9 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
#if 0
else if (outchannels == 1 && inchannels == 2)
{
if (inwidth == 1)
if (informat == QAF_S8)
{
if (outwidth == 1)
if (outformat == QAF_S8)
{
if (inrate < outrate) // upsample
{
@ -503,7 +504,7 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
else // downsample
STANDARDRESCALESTEREOTOMONO(in8, inrate, insamps, out8, outrate, 0, 0)
}
else
else if (outformat == QAF_S16)
{
if (inrate == outrate) // quick convert
QUICKCONVERTSTEREOTOMONO(in8, insamps, out16, 8, 0)
@ -518,9 +519,9 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
STANDARDRESCALESTEREOTOMONO(in8, inrate, insamps, out16, outrate, 8, 0)
}
}
else // 16-bit
else if (informat == QAF_S16) // 16-bit
{
if (outwidth == 2)
if (outformat == QAF_S16)
{
if (inrate < outrate) // upsample
{
@ -532,7 +533,7 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
else // downsample
STANDARDRESCALESTEREOTOMONO(in16, inrate, insamps, out16, outrate, 0, 0)
}
else
else if (outformat == QAF_S8)
{
if (inrate == outrate) // quick convert
QUICKCONVERTSTEREOTOMONO(in16, insamps, out8, 0, 8)
@ -556,7 +557,7 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
ResampleSfx
================
*/
static qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, int inwidth, int insamps, int inloopstart, qbyte *data)
static qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, qaudiofmt_t informat, int insamps, int inloopstart, qbyte *data)
{
extern cvar_t snd_linearresample;
extern cvar_t snd_loadasstereo;
@ -564,17 +565,17 @@ static qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, int inwidth
sfxcache_t *sc;
int outsamps;
int len;
int outwidth;
qaudiofmt_t outformat;
scale = snd_speed / (double)inrate;
outsamps = insamps * scale;
if (loadas8bit.ival < 0)
outwidth = 2;
outformat = QAF_S16;
else if (loadas8bit.ival)
outwidth = 1;
outformat = QAF_S8;
else
outwidth = inwidth;
len = outsamps * outwidth * inchannels;
outformat = informat;
len = outsamps * QAF_BYTES(outformat) * inchannels;
sfx->decoder.buf = sc = BZ_Malloc(sizeof(sfxcache_t) + len);
if (!sc)
@ -583,7 +584,7 @@ static qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, int inwidth
}
sc->numchannels = inchannels;
sc->width = outwidth;
sc->format = outformat;
sc->speed = snd_speed;
sc->length = outsamps;
sc->soundoffset = 0;
@ -595,12 +596,12 @@ static qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, int inwidth
SND_ResampleStream (data,
inrate,
inwidth,
informat,
inchannels,
insamps,
sc->data,
sc->speed,
sc->width,
sc->format,
sc->numchannels,
snd_linearresample.ival);
@ -611,12 +612,12 @@ static qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, int inwidth
nc->data = (qbyte*)(nc+1);
SND_ResampleStream (sc->data,
sc->speed,
sc->width,
sc->format,
sc->numchannels,
outsamps,
nc->data,
nc->speed*2,
nc->width,
nc->format,
nc->numchannels,
false);
nc->numchannels *= 2;
@ -738,22 +739,10 @@ static qboolean QDECL S_LoadDoomSound (sfx_t *s, qbyte *data, size_t datalen, in
}
#endif
void S_ShortedLittleFloats(void *p, size_t samples)
{
short *out = p;
float *in = p;
int t;
while(samples --> 0)
{
t = LittleFloat(*in++) * 32767;
t = bound(-32768, t, 32767);
*out++ = t;
}
}
static qboolean QDECL S_LoadWavSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode)
{
wavinfo_t info;
qaudiofmt_t format;
if (datalen < 4 || strncmp(data, "RIFF", 4))
return false;
@ -766,22 +755,62 @@ static qboolean QDECL S_LoadWavSound (sfx_t *s, qbyte *data, size_t datalen, int
return false;
}
if (info.format == 1 && info.width == 1) //unsigned bytes
COM_CharBias(data + info.dataofs, info.samples*info.numchannels);
else if (info.format == 1 && info.width == 2) //signed shorts
COM_SwapLittleShortBlock((short *)(data + info.dataofs), info.samples*info.numchannels);
else if (info.format == 3 && info.width == 4) //signed floats
if (info.format == 1 && info.bitwidth == 8) //unsigned bytes
{
S_ShortedLittleFloats(data + info.dataofs, info.samples*info.numchannels);
info.width = 2;
COM_CharBias(data + info.dataofs, info.samples*info.numchannels);
format = QAF_S8;
}
else if (info.format == 1 && info.bitwidth == 16) //signed shorts
{
COM_SwapLittleShortBlock((short *)(data + info.dataofs), info.samples*info.numchannels);
format = QAF_S16;
}
else if (info.format == 1 && info.bitwidth == 32) //24 or 32bit int audio
{
short *out = (short *)(data + info.dataofs);
int *in = (int *)(data + info.dataofs);
size_t samples = info.samples*info.numchannels;
while(samples --> 0)
{ //in place size conversion, so we need to do it forwards.
*out++ = LittleLong(*in++)>>16; //just drop the least significant bits.
}
format = QAF_S16;
}
#ifdef MIXER_F32
else if (info.format == 3 && info.bitwidth == 32) //signed floats
{
if (bigendian)
{
size_t i = info.samples*info.numchannels;
float *ptr = (float*)(data + info.dataofs);
while(i --> 0)
ptr[i] = LittleFloat(ptr[i]);
}
format = QAF_F32;
}
#else
else if (info.format == 3 && info.bitwidth == 4) //signed floats
{
short *out = (short *)(data + info.dataofs);
float *in = (float *)(data + info.dataofs);
size_t samples = info.samples*info.numchannels;
int t;
while(samples --> 0)
{ //in place size conversion, so we need to do it forwards.
t = LittleFloat(*in++) * 32767;
t = bound(-32768, t, 32767);
*out++ = t;
}
format = QAF_S16;
}
#endif
else
{
s->loadstate = SLS_FAILED;
switch(info.format)
{
case 1/*WAVE_FORMAT_PCM*/:
case 3/*WAVE_FORMAT_IEEE_FLOAT*/: Con_Printf ("%s has an unsupported width (%i bits).\n", s->name, info.width*8); break;
case 3/*WAVE_FORMAT_IEEE_FLOAT*/: Con_Printf ("%s has an unsupported width (%i bits).\n", s->name, info.bitwidth); break;
case 6/*WAVE_FORMAT_ALAW*/: Con_Printf ("%s uses unsupported a-law format.\n", s->name); break;
case 7/*WAVE_FORMAT_MULAW*/: Con_Printf ("%s uses unsupported mu-law format.\n", s->name); break;
case 0xfffe/*WAVE_FORMAT_EXTENSIBLE*/:
@ -790,7 +819,7 @@ static qboolean QDECL S_LoadWavSound (sfx_t *s, qbyte *data, size_t datalen, int
return false;
}
return ResampleSfx (s, info.rate, info.numchannels, info.width, info.samples, info.loopstart, data + info.dataofs);
return ResampleSfx (s, info.rate, info.numchannels, format, info.samples, info.loopstart, data + info.dataofs);
}
qboolean QDECL S_LoadOVSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode);
@ -1174,7 +1203,7 @@ static wavinfo_t GetWavinfo (char *name, qbyte *wav, int wavlength)
info.numchannels = GetLittleShort(&ctx);
info.rate = GetLittleLong(&ctx);
ctx.data_p += 4+2;
info.width = GetLittleShort(&ctx) / 8;
info.bitwidth = GetLittleShort(&ctx);
// get cue chunk
chunklen = FindChunk(&ctx, "cue ");
@ -1209,7 +1238,7 @@ static wavinfo_t GetWavinfo (char *name, qbyte *wav, int wavlength)
}
ctx.data_p += 8;
samples = chunklen / info.width /info.numchannels;
samples = (chunklen<<3) / info.bitwidth / info.numchannels;
if (info.samples)
{

View File

@ -311,8 +311,9 @@ void S_PaintChannels(soundcardinfo_t *sc, int endtime)
continue;
}
if (scache->width == 1)
switch(scache->format)
{
case QAF_S8:
if (scache->numchannels==2)
SND_PaintChannel8_O2I2(ch, scache, ltime-sc->paintedtime, count, rate);
else if (sc->sn.numchannels <= 2)
@ -323,9 +324,8 @@ void S_PaintChannels(soundcardinfo_t *sc, int endtime)
SND_PaintChannel8_O6I1(ch, scache, count, rate);
else
SND_PaintChannel8_O8I1(ch, scache, count, rate);
}
else if (scache->width == 2)
{
break;
case QAF_S16:
if (scache->numchannels==2)
SND_PaintChannel16_O2I2(ch, scache, ltime-sc->paintedtime, count, rate);
else if (sc->sn.numchannels <= 2)
@ -336,10 +336,9 @@ void S_PaintChannels(soundcardinfo_t *sc, int endtime)
SND_PaintChannel16_O6I1(ch, scache, count, rate);
else
SND_PaintChannel16_O8I1(ch, scache, count, rate);
}
break;
#ifdef MIXER_F32
else if (scache->width == 4)
{
case QAF_F32:
if (scache->numchannels==2)
SND_PaintChannel32F_O2I2(ch, scache, ltime-sc->paintedtime, count, rate);
else if (sc->sn.numchannels <= 2)
@ -350,8 +349,9 @@ void S_PaintChannels(soundcardinfo_t *sc, int endtime)
SND_PaintChannel32F_O6I1(ch, scache, count, rate);
else
SND_PaintChannel32F_O8I1(ch, scache, count, rate);
}
break;
#endif
}
ltime += count;
ch->pos += rate * count;
}

View File

@ -99,7 +99,7 @@ static float QDECL OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf, char *nam
buf->length = dec->pcmtotal;
buf->numchannels = dec->srcchannels;
buf->speed = dec->srcspeed;
buf->width = 2;
buf->format = QAF_S16;
}
if (name)
{
@ -276,7 +276,7 @@ static sfxcache_t *QDECL OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf
buf->length = dec->decodedbytecount / (2 * dec->srcchannels);
buf->numchannels = dec->srcchannels;
buf->speed = snd_speed;
buf->width = 2;
buf->format = QAF_S16;
}
return buf;
}

View File

@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef __SOUND__
#define __SOUND__
//#define MIXER_F32
#define MAXSOUNDCHANNELS 8 //on a per device basis
//pitch/rate changes require that we track stuff with subsample precision.
@ -63,12 +64,27 @@ typedef struct sfx_s
int loopstart; //-1 or sample index to begin looping at once the sample ends
} sfx_t;
typedef enum
{
#ifdef FTE_TARGET_WEB
QAF_BLOB=0,
#endif
QAF_S8=1,
//QAF_U8=0x80|1,
QAF_S16=2,
//QAF_S32=4,
#ifdef MIXER_F32
QAF_F32=0x80|4,
#endif
#define QAF_BYTES(v) (v&0x7f) //to make memory allocation easier.
} qaudiofmt_t;
// !!! if this is changed, it much be changed in asm_i386.h too !!!
typedef struct sfxcache_s
{
usamplepos_t length; //sample count
unsigned int speed;
unsigned int width;
qaudiofmt_t format;
unsigned int numchannels;
usamplepos_t soundoffset; //byte index into the sound
qbyte *data; // variable sized
@ -250,7 +266,7 @@ qboolean S_IsPlayingSomewhere(sfx_t *s);
// picks a channel based on priorities, empty slots, number of channels
channel_t *SND_PickChannel(soundcardinfo_t *sc, int entnum, int entchannel);
void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int insamps, void *out, int outrate, int outwidth, int outchannels, int resampstyle);
void SND_ResampleStream (void *in, int inrate, qaudiofmt_t inwidth, int inchannels, int insamps, void *out, int outrate, qaudiofmt_t outwidth, int outchannels, int resampstyle);
// restart entire sound subsystem (doesn't flush old sounds, so make sure that happens)
void S_DoRestart (qboolean onlyifneeded);
@ -258,7 +274,7 @@ void S_DoRestart (qboolean onlyifneeded);
void S_Restart_f (void);
//plays streaming audio
void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels, int width, float volume);
void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels, qaudiofmt_t width, float volume);
void CLVC_Poll (void);

View File

@ -902,14 +902,6 @@ char *Sys_ConsoleInput(void)
static char text[256];
char *nl;
#ifdef SUBSERVERS
if (SSV_IsSubServer())
{
SSV_CheckFromMaster();
return NULL;
}
#endif
if (noconinput)
return NULL;
@ -1110,10 +1102,14 @@ int main (int c, const char **v)
fcntl(STDIN_FILENO, F_SETFL, fcntl (0, F_GETFL, 0) | FNDELAY);
#endif
#ifndef CLIENTONLY
#ifdef HAVE_SERVER
#ifdef SUBSERVERS
if (COM_CheckParm("-clusterslave"))
isDedicated = nostdout = isClusterSlave = true;
{
isDedicated = true;
nostdout = noconinput = true;
SSV_SetupControlPipe(Sys_GetStdInOutStream());
}
#endif
if (COM_CheckParm("-dedicated"))
isDedicated = true;

View File

@ -868,7 +868,8 @@ int QDECL main(int argc, char **argv)
sleeptime = Host_Frame (time);
oldtime = newtime;
Sys_Sleep(sleeptime);
if (sleeptime)
Sys_Sleep(sleeptime);
}
}
@ -1068,17 +1069,14 @@ qboolean Sys_RunInstaller(void)
#endif
#ifdef HAVEAUTOUPDATE
//legacy, so old build can still deal with updates properly
void Sys_SetUpdatedBinary(const char *fname)
//returns true if we could sucessfull overwrite the engine binary.
qboolean Sys_SetUpdatedBinary(const char *fname)
{
return false;
}
//says whether the system code is able to invoke new binaries properly
qboolean Sys_EngineCanUpdate(void)
{
return false; //nope, nothing here
}
//invoke the given system-path binary
qboolean Sys_EngineWasUpdated(char *newbinary)
//says whether the system code is able/allowed to overwrite itself.
//(ie: return false if we don't know the binary name or if its write-protected etc)
qboolean Sys_EngineMayUpdate(void)
{
return false; //sorry
}

View File

@ -58,7 +58,7 @@ cmdalias_t *cmd_alias;
cvar_t cfg_save_all = CVARFD("cfg_save_all", "", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, "If 1, cfg_save ALWAYS saves all cvars. If 0, cfg_save only ever saves archived cvars. If empty, cfg_save saves all cvars only when an explicit filename was given (ie: when not used internally via quit menu options).");
cvar_t cfg_save_auto = CVARFD("cfg_save_auto", "0", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, "If 1, the config will automatically be saved and without prompts. If 0, you'll have to save your config manually (possibly via prompts from the quit menu).");
cvar_t cfg_save_infos = CVARFD("cfg_save_infos", "1", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, "If 1, saves userinfo and serverinfo to configs.");
cvar_t cfg_save_aliases = CVARFD("cfg_save_aliases", "1", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, "If 1, saves userinfo and serverinfo to configs.");
cvar_t cfg_save_aliases = CVARFD("cfg_save_aliases", "1", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, "If 1, saves aliases to configs. Note that aliases sent from servers are assumed to be server-specific and are never saved (and are forgotten on map changes, too).");
cvar_t cfg_save_binds = CVARFD("cfg_save_binds", "1", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, "If 1, saves all key bindings to configs.");
cvar_t cfg_save_buttons = CVARFD("cfg_save_buttons", "0", CVAR_ARCHIVE|CVAR_NOTFROMSERVER, "If 1, saves the state of things such as +mlook or +forward to configs.");

View File

@ -613,7 +613,7 @@ qboolean FS_WriteFile (const char *filename, const void *data, int len, enum fs_
void *FS_MallocFile(const char *filename, enum fs_relative relativeto, qofs_t *filesize);
vfsfile_t *QDECL FS_OpenVFS(const char *filename, const char *mode, enum fs_relative relativeto);
vfsfile_t *FS_OpenTemp(void);
vfsfile_t *FS_OpenTCP(const char *name, int defaultport);
vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls);
vfsfile_t *FS_OpenWithFriends(const char *fname, char *sysname, size_t sysnamesize, int numfriends, ...);
@ -705,6 +705,7 @@ typedef struct
GAMEDIR_READONLY=1u<<2, //don't write here...
GAMEDIR_USEBASEDIR=1u<<3, //packages will be read from the basedir (and homedir), but not other files. path is an empty string.
GAMEDIR_STEAMGAME=1u<<4, //finds the game via steam. must also be private+readonly.
GAMEDIR_QSHACK=1u<<8,
GAMEDIR_SPECIAL=GAMEDIR_USEBASEDIR|GAMEDIR_STEAMGAME, //if one of these flags, then the gamedir cannot be simply concatenated to the basedir/homedir.
} flags;

View File

@ -617,7 +617,10 @@ static qboolean FS_Manifest_ParseTokens(ftemanifest_t *man)
else if (!Q_strcasecmp(cmd, "mainconfig"))
{
Z_Free(man->mainconfig);
man->mainconfig = Z_StrDup(Cmd_Argv(1));
if (strcmp(".cfg", COM_GetFileExtension(Cmd_Argv(1),NULL)))
man->mainconfig = Z_StrDup(va("%s.cfg", Cmd_Argv(1)));
else
man->mainconfig = Z_StrDup(Cmd_Argv(1));
}
else if (!Q_strcasecmp(cmd, "defaultexec"))
{
@ -672,6 +675,11 @@ static qboolean FS_Manifest_ParseTokens(ftemanifest_t *man)
if (!Q_strcasecmp(cmd, "basegame"))
man->gamepath[i].flags |= GAMEDIR_BASEGAME;
if (*newdir == '/')
{
newdir++;
man->gamepath[i].flags |= GAMEDIR_QSHACK;
}
if (*newdir == '*')
{ //*dir makes the dir 'private' and not networked.
newdir++;
@ -1567,7 +1575,7 @@ fail:
else
Con_Printf("Failed\n");
*/
if (found == FF_NOTFOUND || loc->len == -1)
if (found == FF_NOTFOUND || found == FF_DIRECTORY || loc->len == -1)
{
if (lflags & FSLF_DEEPONFAILURE)
return 0x7fffffff; //if we're asking for depth, the file is reported to be so far into the filesystem as to be irrelevant.
@ -2780,6 +2788,8 @@ static searchpathfuncs_t *FS_OpenPackByExtension(vfsfile_t *f, searchpathfuncs_t
searchpathfuncs_t *pak;
int j;
char ext[8];
if (!f)
return NULL;
COM_FileExtension(pakname, ext, sizeof(ext));
for (j = 0; j < sizeof(searchpathformats)/sizeof(searchpathformats[0]); j++)
{
@ -2950,6 +2960,8 @@ static void FS_AddDataFiles(searchpath_t **oldpaths, const char *purepath, const
flocation_t loc;
wildpaks_t wp;
filelist_t list = {0};
qboolean qshack = (pflags&SPF_QSHACK);
pflags &= ~SPF_QSHACK;
Q_strncpyz(logicalpaths, logicalpath, sizeof(logicalpaths));
FS_CleanDir(logicalpaths, sizeof(logicalpaths));
@ -3005,37 +3017,50 @@ static void FS_AddDataFiles(searchpath_t **oldpaths, const char *purepath, const
continue;
if (loadstuff & (1<<j))
{
qboolean okay = true;
const char *extension = searchpathformats[j].extension;
//first load all the numbered pak files
for (i=0 ; ; i++)
for (i=0 ; okay ; i++)
{
snprintf (pakfile, sizeof(pakfile), "pak%i.%s", i, extension);
fs_finds++;
if (!search->handle->FindFile(search->handle, &loc, pakfile, NULL))
break; //not found..
snprintf (logicalfile, sizeof(logicalfile), "%spak%i.%s", logicalpaths, i, extension);
snprintf (purefile, sizeof(purefile), "%s/pak%i.%s", purepath, i, extension);
for (existing = com_searchpaths; existing; existing = existing->next)
if (search->handle->FindFile(search->handle, &loc, pakfile, NULL))
{
if (!Q_strcasecmp(existing->logicalpath, logicalfile)) //assumption: first member of structure is a char array
break; //already loaded (base paths?)
snprintf (logicalfile, sizeof(logicalfile), "%spak%i.%s", logicalpaths, i, extension);
snprintf (purefile, sizeof(purefile), "%s/pak%i.%s", purepath, i, extension);
for (existing = com_searchpaths; existing; existing = existing->next)
{
if (!Q_strcasecmp(existing->logicalpath, logicalfile)) //assumption: first member of structure is a char array
break; //already loaded (base paths?)
}
if (!existing)
{
handle = FS_GetOldPath(oldpaths, logicalfile, &keptflags);
if (!handle)
{
vfs = search->handle->OpenVFS(search->handle, &loc, "rb");
if (!vfs)
break;
handle = searchpathformats[j].OpenNew (vfs, search->handle, pakfile, logicalfile, "");
if (!handle)
break;
}
FS_AddPathHandle(oldpaths, purefile, logicalfile, handle, "", SPF_COPYPROTECTED|pflags|keptflags, (unsigned int)-1);
}
}
if (!existing)
else
okay = false;
if (i == 0 && qshack)
{
snprintf (pakfile, sizeof(pakfile), "quakespasm.%s", extension);
handle = FS_GetOldPath(oldpaths, logicalfile, &keptflags);
if (!handle)
{
vfs = search->handle->OpenVFS(search->handle, &loc, "rb");
if (!vfs)
break;
handle = searchpathformats[j].OpenNew (vfs, search->handle, pakfile, logicalfile, "");
if (!handle)
break;
}
FS_AddPathHandle(oldpaths, purefile, logicalfile, handle, "", SPF_COPYPROTECTED|pflags|keptflags, (unsigned int)-1);
handle = FS_OpenPackByExtension(VFSOS_Open(pakfile, "rb"), NULL, pakfile, pakfile);
if (handle) //logically should have SPF_EXPLICIT set, but that would give it a worse gamedir depth
FS_AddPathHandle(oldpaths, "", pakfile, handle, "", SPF_COPYPROTECTED|SPF_PRIVATE, (unsigned int)-1);
}
}
}
@ -3098,7 +3123,7 @@ static searchpath_t *FS_AddPathHandle(searchpath_t **oldpaths, const char *purep
//temp packages also do not nest
// if (!(flags & SPF_TEMPORARY))
FS_AddDataFiles(oldpaths, purepath, logicalpath, search, flags&(SPF_COPYPROTECTED|SPF_UNTRUSTED|SPF_TEMPORARY|SPF_PRIVATE), loadstuff);
FS_AddDataFiles(oldpaths, purepath, logicalpath, search, flags&(SPF_COPYPROTECTED|SPF_UNTRUSTED|SPF_TEMPORARY|SPF_PRIVATE|SPF_QSHACK), loadstuff);
if (flags & SPF_TEMPORARY)
{
@ -3464,7 +3489,7 @@ void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths)
#define QUAKESPASMSUCKS "set mod_h2holey_bugged 1\n"
#define QCFG "set v_gammainverted 1\nset con_stayhidden 0\nset com_parseutf8 0\nset allow_download_pakcontents 1\nset allow_download_refpackages 0\nset sv_bigcoords \"\"\nmap_autoopenportals 1\n" "sv_port "STRINGIFY(PORT_QWSERVER)" "STRINGIFY(PORT_NQSERVER)"\n" ZFIXHACK EZQUAKECOMPETITIVE QRPCOMPAT QUAKESPASMSUCKS
/*NetQuake reconfiguration, to make certain people feel more at home...*/
#define NQCFG "//-nohome\ncfg_save_auto 1\n" QCFG "sv_nqplayerphysics 1\ncl_loopbackprotocol auto\ncl_sbar 1\nplug_sbar 0\nsv_port "STRINGIFY(PORT_NQSERVER)"\ncl_defaultport "STRINGIFY(PORT_NQSERVER)"\n"
#define NQCFG "//disablehomedir 1\n//mainconfig ftenq\ncfg_save_auto 1\n" QCFG "set sv_nqplayerphysics 1\nset cl_loopbackprotocol auto\ncl_sbar 1\nset plug_sbar 0\nset sv_port "STRINGIFY(PORT_NQSERVER)"\ncl_defaultport "STRINGIFY(PORT_NQSERVER)"\nset m_preset_chosen 1\nset vid_wait 1\n"
//nehahra has to be weird with its extra cvars, and buggy fullbrights.
#define NEHCFG QCFG "set nospr32 0\nset cutscene 1\nalias startmap_sp \"map nehstart\"\nr_fb_bmodels 0\nr_fb_models 0\n"
/*stuff that makes dp-only mods work a bit better*/
@ -3537,13 +3562,13 @@ const gamemode_info_t gamemode_info[] = {
//alternative name, because fmf file install names are messy when a single name is used for registry install path.
{"-afterquake", NULL, "FTE-Quake",{"id1/pak0.pak", "id1/quake.rc"}, QCFG,{"id1", "qw", "*fte"}, "AfterQuake", UPDATEURL(Q1) /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
//netquake-specific quake that avoids qw/ with its nquake fuckups, and disables nqisms
{"-netquake", "nq", "FTE-Quake DarkPlaces-Quake",{"id1/pak0.pak", "id1/quake.rc"},NQCFG,{"id1"}, "NetQuake", UPDATEURL(Q1) /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
{"-netquake", NULL, "FTE-Quake DarkPlaces-Quake",{"id1/pak0.pak", "id1/quake.rc"},NQCFG,{"id1"}, "NetQuake", UPDATEURL(Q1) /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
//blurgh
{"-spasm", NULL, "FTE-Quake DarkPlaces-Quake",{"quakespasm.pak"}, NQCFG"fps_preset spasm\n",{"id1", "*"}, "FauxSpasm", UPDATEURL(Q1) /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
{"-spasm", NULL, "FTE-Quake DarkPlaces-Quake",{"quakespasm.pak"}, NQCFG"fps_preset builtin_spasm\nset cl_demoreel 0\n",{"/id1"}, "FauxSpasm", UPDATEURL(Q1) /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
//because we can. 'fps_preset spasm' is hopefully close enough...
{"-fitz", NULL, "FTE-Quake DarkPlaces-Quake",{"quakespasm.pak"}, NQCFG"fps_preset spasm\n",{"id1"}, "FauxFitz", UPDATEURL(Q1) /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
{"-fitz", "nq", "FTE-Quake DarkPlaces-Quake",{"id1/pak0.pak", "id1/quake.rc"},NQCFG"fps_preset builtin_spasm\n",{"id1"}, "FauxFitz", UPDATEURL(Q1) /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
//because we can
{"-tenebrae", NULL, "FTE-Quake DarkPlaces-Quake",{"id1/pak0.pak", "id1/quake.rc"},NQCFG"fps_preset tenebrae\n",{"id1","tenebrae"},"FauxTenebrae", UPDATEURL(Q1) /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
{"-tenebrae", NULL, "FTE-Quake DarkPlaces-Quake",{"id1/pak0.pak", "id1/quake.rc"},NQCFG"fps_preset builtin_tenebrae\n",{"id1","tenebrae"},"FauxTenebrae", UPDATEURL(Q1) /*,"id1/pak0.pak|http://quakeservers.nquake.com/qsw106.zip|http://nquake.localghost.net/qsw106.zip|http://qw.quakephil.com/nquake/qsw106.zip|http://fnu.nquake.com/qsw106.zip"*/},
//quake's mission packs should not be favoured over the base game nor autodetected
//third part mods also tend to depend upon the mission packs for their huds, even if they don't use any other content.
@ -4138,6 +4163,8 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags)
fl |= SPF_WRITABLE;
if (fs_manifest->gamepath[i].flags&GAMEDIR_PRIVATE)
fl |= SPF_PRIVATE;
if (fs_manifest->gamepath[i].flags&GAMEDIR_QSHACK)
fl |= SPF_QSHACK;
if (fs_manifest->gamepath[i].flags&GAMEDIR_USEBASEDIR)
{ //for doom - loading packages without an actual gamedir. note that this does not imply that we can write anything.
@ -4388,6 +4415,8 @@ static void FS_ReloadPackFilesFlags(unsigned int reloadflags)
while(oldpaths)
{
fs_restarts++;
next = oldpaths->next;
Con_DPrintf("%s is no longer needed\n", oldpaths->logicalpath);
@ -4444,7 +4473,6 @@ void FS_ReloadPackFiles(void)
FS_FLocateFile("gfx/palette.lmp", 0, &paletteloc2);
if (paletteloc.search != paletteloc2.search)
Cbuf_AddText("vid_reload\n", RESTRICT_LOCAL);
}
static void FS_ReloadPackFiles_f(void)
@ -5006,6 +5034,7 @@ static ftemanifest_t *FS_GenerateLegacyManifest(int game, const char *basedir)
{
ftemanifest_t *man;
size_t j;
const char *cexec;
if (gamemode_info[game].manifestfile)
man = FS_Manifest_ReadMem(NULL, basedir, gamemode_info[game].manifestfile);
@ -5013,9 +5042,15 @@ static ftemanifest_t *FS_GenerateLegacyManifest(int game, const char *basedir)
{
man = FS_Manifest_Create(NULL, basedir);
if (gamemode_info[game].customexec && !strncmp(gamemode_info[game].customexec, "//-nohome\n", 10))
for (cexec = gamemode_info[game].customexec; cexec[0] == '/' && cexec[1] == '/'; )
{
Cmd_TokenizeString("disablehomedir 1", false, false);
char line[256];
char *e = strchr(cexec, '\n');
if (!e)
break;
Q_strncpyz(line, cexec+2, min(e-(cexec+2)+1, sizeof(line)));
cexec = e+1;
Cmd_TokenizeString(line, false, false);
FS_Manifest_ParseTokens(man);
}
@ -5379,7 +5414,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
#ifdef HAVE_CLIENT
qboolean allowvidrestart = true;
char *vidfile[] = {"gfx.wad", "gfx/conback.lmp", //misc stuff
"gfx/palette.lmp", "pics/colormap.pcx"}; //palettes
"gfx/palette.lmp", "pics/colormap.pcx", "gfx/conchars.png"}; //palettes
searchpathfuncs_t *vidpath[countof(vidfile)];
#endif

View File

@ -99,6 +99,7 @@ struct modlist_s *Mods_GetMod(size_t diridx);
#define SPF_PRIVATE 32 //private to the client. ie: the fte dir. name is not networked.
#define SPF_WRITABLE 64 //safe to write here. lots of weird rules etc.
#define SPF_BASEPATH 128 //part of the basegames, and not the mod gamedir(s).
#define SPF_QSHACK 256 //part of the basegames, and not the mod gamedir(s).
qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, int *crc, unsigned int flags);
#ifdef AVAIL_XZDEC

View File

@ -1291,7 +1291,9 @@ const dtlsfuncs_t *SSPI_DTLS_InitClient(void)
#endif
#include <ntstatus.h>
//#include <ntstatus.h> //windows sucks too much to actually include this. oh well.
#define STATUS_SUCCESS ((NTSTATUS)0x00000000)
#define STATUS_INVALID_SIGNATURE ((NTSTATUS)0xC000A000)
enum hashvalidation_e SSPI_VerifyHash(qbyte *hashdata, size_t hashsize, const char *authority, qbyte *signdata, size_t signsize)
{
NTSTATUS status;
@ -1303,8 +1305,8 @@ enum hashvalidation_e SSPI_VerifyHash(qbyte *hashdata, size_t hashsize, const ch
size_t dersize;
static const void *(WINAPI *pCertCreateContext) (DWORD dwContextType, DWORD dwEncodingType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, PCERT_CREATE_CONTEXT_PARA pCreatePara);
static WINBOOL (WINAPI *pCryptImportPublicKeyInfoEx2) (DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pInfo, DWORD dwFlags, void *pvAuxInfo, BCRYPT_KEY_HANDLE *phKey);
static WINBOOL (WINAPI *pCertFreeCertificateContext) (PCCERT_CONTEXT pCertContext);
static BOOL (WINAPI *pCryptImportPublicKeyInfoEx2) (DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pInfo, DWORD dwFlags, void *pvAuxInfo, BCRYPT_KEY_HANDLE *phKey);
static BOOL (WINAPI *pCertFreeCertificateContext) (PCCERT_CONTEXT pCertContext);
static dllhandle_t *crypt32;
static dllfunction_t crypt32funcs[] = {
{(void**)&pCertCreateContext, "CertCreateContext"},

View File

@ -2305,6 +2305,8 @@ vfsfile_t *FS_OpenSSL(const char *peername, vfsfile_t *source, qboolean isserver
if (!f)
f = SSPI_OpenVFS(hostname, source, isserver);
#endif
if (!f) //it all failed.
VFS_CLOSE(source);
return f;
}
int TLS_GetChannelBinding(vfsfile_t *stream, qbyte *data, size_t *datasize)
@ -4627,6 +4629,7 @@ qboolean FTENET_TCP_ParseHTTPRequest(ftenet_tcp_connection_t *con, ftenet_tcp_st
qboolean sendingweirdness = false;
char arg[WCATTR_COUNT][64];
if (!net_enable_http.ival && !net_enable_websockets.ival && !net_enable_rtcbroker.ival)
{
//we need to respond, firefox will create 10 different connections if we just close it
@ -5278,6 +5281,25 @@ static enum{
if (headerscomplete)
{
#if defined(SUBSERVERS) && defined(HAVE_SERVER)
//this is a new subserver node...
if (!Q_strncasecmp(st->inbuffer, "NODE", 4))
{
char tmpbuf[256];
#ifdef HAVE_EPOLL
//the tcp connection will be handled elsewhere.
//make sure we don't get tcp-handler wakeups from this connection.
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, st->socketnum, NULL);
st->socketnum = INVALID_SOCKET;
st->epoll.Polled = NULL;
#endif
//now try to pass it over
MSV_NewNetworkedNode(st->clientstream, st->inbuffer, st->inbuffer+i, st->inlen-i, NET_AdrToString(tmpbuf, sizeof(tmpbuf), &st->remoteaddr));
st->clientstream = NULL; //qtv code took it.
return FTETCP_KILL;
}
else
#endif
#ifdef MVD_RECORDING
//for QTV connections, we just need the method and a blank line. our qtv parser will parse the actual headers.
if (!Q_strncasecmp(st->inbuffer, "QTV", 3))
@ -8453,6 +8475,23 @@ void NET_Init (void)
#if defined(HAVE_CLIENT)||defined(HAVE_SERVER)
Net_Master_Init();
#endif
#if defined(SUBSERVERS) && defined(HAVE_SERVER)
if (isDedicated && !SSV_IsSubServer())
{ //-clusterhost address:port password
//connects this server to a remote control/gateway server.
int i = COM_CheckParm("-clusterhost");
if (i && i+2 < com_argc)
{
vfsfile_t *f = FS_OpenTCP(com_argv[i+1], PORT_DEFAULTSERVER, true);
if (!f)
Sys_Error("Unable to resolve/connect to cluster host address \"%s\"\n", com_argv[i+1]);
VFS_PRINTF(f, "NODE\r\nPassword: \"%s\"\r\n", com_argv[i+2]);
SSV_SetupControlPipe(f);
return;
}
}
#endif
}
#ifdef HAVE_CLIENT
void NET_CloseClient(void)
@ -8949,12 +8988,23 @@ vfsfile_t *FS_WrapTCPSocket(SOCKET sock, qboolean conpending, const char *peerna
return &newf->funcs;
}
vfsfile_t *FS_OpenTCP(const char *name, int defaultport)
vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls)
{
netadr_t adr = {0};
if (NET_StringToAdr(name, defaultport, &adr))
{
return FS_WrapTCPSocket(TCP_OpenStream(&adr), true, name);
qboolean wanttls = (adr.prot == NP_TLS || (adr.prot != NP_STREAM && assumetls));
vfsfile_t *f;
#ifndef HAVE_SSL
if (wanttls)
return NULL; //don't even make the connection if we can't satisfy it.
#endif
f = FS_WrapTCPSocket(TCP_OpenStream(&adr), true, name);
#ifdef HAVE_SSL
if (f && wanttls)
f = FS_OpenSSL(name, f, false);
#endif
return f;
}
else
return NULL;

View File

@ -453,6 +453,6 @@ int TLS_GetChannelBinding(vfsfile_t *stream, qbyte *data, size_t *datasize); //d
#ifdef HAVE_PACKET
vfsfile_t *FS_WrapTCPSocket(SOCKET socket, qboolean conpending, const char *peername); //conpending allows us to reject any writes until the connection has succeeded. considers the socket owned (so be sure to stop using the direct socket at least before the VFS_CLOSE call).
#endif
vfsfile_t *FS_OpenTCP(const char *name, int defaultport);
vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls);
#endif //NETINC_INCLUDED

View File

@ -802,7 +802,7 @@ static qhandle_t QDECL Plug_Net_Accept(qhandle_t handle, char *outaddress, int o
static qhandle_t QDECL Plug_Net_TCPConnect(const char *remoteip, int remoteport)
{
int handle;
vfsfile_t *stream = FS_OpenTCP(remoteip, remoteport);
vfsfile_t *stream = FS_OpenTCP(remoteip, remoteport, false);
if (!currentplug || !stream)
return -1;
handle = Plug_NewStreamHandle(STREAM_VFS);

View File

@ -32,6 +32,7 @@ cvar_t pr_brokenfloatconvert = CVAR("pr_brokenfloatconvert", "0");
cvar_t pr_fixbrokenqccarrays = CVARFD("pr_fixbrokenqccarrays", "0", CVAR_LATCH, "As part of its nq/qw/h2/csqc support, FTE remaps QC fields to match an internal order. This is a faster way to handle extended fields. However, some QCCs are buggy and don't report all field defs.\n0: do nothing. QCC must be well behaved.\n1: Duplicate engine fields, remap the ones we can to known offsets. This is sufficient for QCCX/FrikQCC mods that use hardcoded or even occasional calculated offsets (fixes ktpro).\n2: Scan the mod for field accessing instructions, and assume those are the fields (and that they don't alias non-fields). This can be used to work around gmqcc's WTFs (fixes xonotic).");
cvar_t pr_tempstringcount = CVARD("pr_tempstringcount", "", "Obsolete. Set to 16 if you want to recycle+reuse the same 16 tempstring references and break lots of mods.");
cvar_t pr_tempstringsize = CVARD("pr_tempstringsize", "4096", "Obsolete");
cvar_t pr_gc_threaded = CVARD("pr_gc_threaded", "0", "Says whether to use a separate thread for tempstring garbage collections. This avoids main-thread stalls but at the expense of more memory usage.");
cvar_t pr_sourcedir = CVARD("pr_sourcedir", "src", "Subdirectory where your qc source is located. Used by the internal compiler and qc debugging functionality.");
cvar_t pr_enable_uriget = CVARD("pr_enable_uriget", "1", "Allows gamecode to make direct http requests");
cvar_t pr_enable_profiling = CVARD("pr_enable_profiling", "0", "Enables profiling support. Will run more slowly. Change the map and then use the profile_ssqc/profile_csqc commands to see the results.");
@ -88,6 +89,7 @@ void PF_Common_RegisterCvars(void)
Cvar_Register (&pr_brokenfloatconvert, cvargroup_progs);
Cvar_Register (&pr_tempstringcount, cvargroup_progs);
Cvar_Register (&pr_tempstringsize, cvargroup_progs);
Cvar_Register (&pr_gc_threaded, cvargroup_progs);
#ifdef WEBCLIENT
Cvar_Register (&pr_enable_uriget, cvargroup_progs);
#endif
@ -1222,7 +1224,7 @@ void QCBUILTIN PF_findchainflags (pubprogfuncs_t *prinst, struct globalvars_s *p
int s;
wedict_t *ent, *chain;
chain = (wedict_t *) *prinst->parms->sv_edicts;
chain = (wedict_t *) *prinst->parms->edicts;
ff = G_INT(OFS_PARM0)+prinst->fieldadjust;
s = G_FLOAT(OFS_PARM1);
@ -1231,7 +1233,7 @@ void QCBUILTIN PF_findchainflags (pubprogfuncs_t *prinst, struct globalvars_s *p
else
cf = &((comentvars_t*)NULL)->chain - (int*)NULL;
for (i = 1; i < *prinst->parms->sv_num_edicts; i++)
for (i = 1; i < *prinst->parms->num_edicts; i++)
{
ent = WEDICT_NUM_PB(prinst, i);
if (ED_ISFREE(ent))
@ -1253,7 +1255,7 @@ void QCBUILTIN PF_findchainfloat (pubprogfuncs_t *prinst, struct globalvars_s *p
float s;
wedict_t *ent, *chain;
chain = (wedict_t *) *prinst->parms->sv_edicts;
chain = (wedict_t *) *prinst->parms->edicts;
ff = G_INT(OFS_PARM0)+prinst->fieldadjust;
s = G_FLOAT(OFS_PARM1);
@ -1262,7 +1264,7 @@ void QCBUILTIN PF_findchainfloat (pubprogfuncs_t *prinst, struct globalvars_s *p
else
cf = &((comentvars_t*)NULL)->chain - (int*)NULL;
for (i = 1; i < *prinst->parms->sv_num_edicts; i++)
for (i = 1; i < *prinst->parms->num_edicts; i++)
{
ent = WEDICT_NUM_PB(prinst, i);
if (ED_ISFREE(ent))
@ -1286,7 +1288,7 @@ void QCBUILTIN PF_findchain (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo
string_t t;
wedict_t *ent, *chain;
chain = (wedict_t *) *prinst->parms->sv_edicts;
chain = (wedict_t *) *prinst->parms->edicts;
ff = G_INT(OFS_PARM0)+prinst->fieldadjust;
s = PR_GetStringOfs(prinst, OFS_PARM1);
@ -1295,7 +1297,7 @@ void QCBUILTIN PF_findchain (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo
else
cf = &((comentvars_t*)NULL)->chain - (int*)NULL;
for (i = 1; i < *prinst->parms->sv_num_edicts; i++)
for (i = 1; i < *prinst->parms->num_edicts; i++)
{
ent = WEDICT_NUM_PB(prinst, i);
if (ED_ISFREE(ent))
@ -1325,7 +1327,7 @@ void QCBUILTIN PF_FindFlags (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo
f = G_INT(OFS_PARM1)+prinst->fieldadjust;
s = G_FLOAT(OFS_PARM2);
for (e++; e < *prinst->parms->sv_num_edicts; e++)
for (e++; e < *prinst->parms->num_edicts; e++)
{
ed = WEDICT_NUM_PB(prinst, e);
if (ED_ISFREE(ed))
@ -1337,7 +1339,7 @@ void QCBUILTIN PF_FindFlags (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo
}
}
RETURN_EDICT(prinst, *prinst->parms->sv_edicts);
RETURN_EDICT(prinst, *prinst->parms->edicts);
}
//entity(entity start, float fld, float match) findfloat = #98
@ -1357,7 +1359,7 @@ void QCBUILTIN PF_FindFloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo
f = G_INT(OFS_PARM1)+prinst->fieldadjust;
s = G_INT(OFS_PARM2);
for (e++; e < *prinst->parms->sv_num_edicts; e++)
for (e++; e < *prinst->parms->num_edicts; e++)
{
ed = WEDICT_NUM_PB(prinst, e);
if (ED_ISFREE(ed))
@ -1369,7 +1371,7 @@ void QCBUILTIN PF_FindFloat (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo
}
}
RETURN_EDICT(prinst, *prinst->parms->sv_edicts);
RETURN_EDICT(prinst, *prinst->parms->edicts);
}
// entity (entity start, .string field, string match) find = #5;
@ -1390,7 +1392,7 @@ void QCBUILTIN PF_FindString (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl
return;
}
for (e++ ; e < *prinst->parms->sv_num_edicts ; e++)
for (e++ ; e < *prinst->parms->num_edicts ; e++)
{
ed = WEDICT_NUM_PB(prinst, e);
if (ED_ISFREE(ed))
@ -1405,7 +1407,7 @@ void QCBUILTIN PF_FindString (pubprogfuncs_t *prinst, struct globalvars_s *pr_gl
}
}
RETURN_EDICT(prinst, *prinst->parms->sv_edicts);
RETURN_EDICT(prinst, *prinst->parms->edicts);
}
//Finding
@ -3263,9 +3265,9 @@ void QCBUILTIN PF_nextent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globa
while (1)
{
i++;
if (i == *prinst->parms->sv_num_edicts)
if (i == *prinst->parms->num_edicts)
{
RETURN_EDICT(prinst, *prinst->parms->sv_edicts);
RETURN_EDICT(prinst, *prinst->parms->edicts);
return;
}
ent = WEDICT_NUM_PB(prinst, i);

View File

@ -71,6 +71,7 @@ extern cvar_t pr_tempstringsize;
extern cvar_t pr_tempstringcount;
extern cvar_t pr_enable_profiling;
extern cvar_t pr_fixbrokenqccarrays;
extern cvar_t pr_gc_threaded;
extern int qcinput_scan;
extern int qcinput_unicode;

View File

@ -276,82 +276,71 @@ void Sys_Sleep (double seconds)
#include <errno.h>
#include <sys/wait.h>
typedef struct slaveserver_s
typedef struct
{
pubsubserver_t pub;
vfsfile_t pub;
int inpipe;
int outpipe;
pid_t pid; //so we don't end up with zombie processes
qbyte inbuffer[2048];
int inbufsize;
} linsubserver_t;
static void Sys_InstructSlave(pubsubserver_t *ps, sizebuf_t *cmd)
static int QDECL Sys_MSV_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)
{
//FIXME: this is blocking. this is bad if the target is also blocking while trying to write to us.
//FIXME: merge buffering logic with SSV_InstructMaster, and allow for failure if full
linsubserver_t *s = (linsubserver_t*)ps;
if (s->outpipe == -1)
return; //it already died.
cmd->data[0] = cmd->cursize & 0xff;
cmd->data[1] = (cmd->cursize>>8) & 0xff;
write(s->outpipe, cmd->data, cmd->cursize);
}
static int Sys_SubServerRead(pubsubserver_t *ps)
{
linsubserver_t *s = (linsubserver_t*)ps;
if (s->inbufsize < sizeof(s->inbuffer) && s->inpipe != -1)
linsubserver_t *s = (linsubserver_t*)file;
ssize_t avail = read(s->inpipe, buffer, bytestoread);
if (!avail)
return -1; //EOF
if (avail < 0)
{
ssize_t avail = read(s->inpipe, s->inbuffer+s->inbufsize, sizeof(s->inbuffer)-s->inbufsize);
if (!avail)
{ //eof
close(s->inpipe);
close(s->outpipe);
Con_Printf("%i:%s has died\n", s->pub.id, s->pub.name);
s->inpipe = -1;
s->outpipe = -1;
waitpid(s->pid, NULL, 0);
}
else if (avail < 0)
{
int e = errno;
if (e == EAGAIN || e == EWOULDBLOCK)
;
else
perror("subserver read");
}
int e = errno;
if (e == EAGAIN || e == EWOULDBLOCK || e == EINTR)
return 0; //no data available
else
s->inbufsize += avail;
}
if(s->inbufsize >= 2)
{
unsigned short len = s->inbuffer[0] | (s->inbuffer[1]<<8);
if (s->inbufsize >= len && len>=2)
{
memcpy(net_message.data, s->inbuffer+2, len-2);
net_message.cursize = len-2;
memmove(s->inbuffer, s->inbuffer+len, s->inbufsize - len);
s->inbufsize -= len;
MSG_BeginReading (msg_nullnetprim);
return 1;
perror("subserver read");
return -1; //some sort of error
}
}
else if (s->inpipe == -1)
return -1;
return 0;
return avail;
}
static int QDECL Sys_MSV_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestowrite)
{
linsubserver_t *s = (linsubserver_t*)file;
ssize_t wrote = write(s->outpipe, buffer, bytestowrite);
if (!wrote)
return -1; //EOF
if (wrote < 0)
{
int e = errno;
if (e == EAGAIN || e == EWOULDBLOCK || e == EINTR)
return 0; //no space available
else
{
perror("subserver write");
return -1; //some sort of error
}
}
return wrote;
}
static qboolean QDECL Sys_MSV_Close (struct vfsfile_s *file)
{
linsubserver_t *s = (linsubserver_t*)file;
close(s->inpipe);
close(s->outpipe);
s->inpipe = -1;
s->outpipe = -1;
waitpid(s->pid, NULL, 0);
Z_Free(s);
return true;
}
#ifdef SQL
#include "sv_sql.h"
#endif
pubsubserver_t *Sys_ForkServer(void)
vfsfile_t *Sys_ForkServer(void)
{
#ifdef SERVERONLY
// extern jmp_buf host_abort;
@ -473,23 +462,29 @@ pubsubserver_t *Sys_ForkServer(void)
close(toslave[0]);
ctx->outpipe = toslave[1];
ctx->pub.funcs.InstructSlave = Sys_InstructSlave;
ctx->pub.funcs.SubServerRead = Sys_SubServerRead;
ctx->pub.ReadBytes = Sys_MSV_ReadBytes;
ctx->pub.WriteBytes = Sys_MSV_WriteBytes;
ctx->pub.Close = Sys_MSV_Close;
return &ctx->pub;
}
void Sys_InstructMaster(sizebuf_t *cmd)
{
write(STDOUT_FILENO, cmd->data, cmd->cursize);
//FIXME: handle partial writes.
static int QDECL Sys_StdoutWrite (struct vfsfile_s *file, const void *buffer, int bytestowrite)
{
ssize_t r = write(STDOUT_FILENO, buffer, bytestowrite);
if (r == 0 && bytestowrite)
return -1; //eof
if (r < 0)
{
int e = errno;
if (e == EINTR || e == EAGAIN || e == EWOULDBLOCK)
return 0;
}
return r;
}
void SSV_CheckFromMaster(void)
static int QDECL Sys_StdinRead (struct vfsfile_s *file, void *buffer, int bytestoread)
{
static char inbuffer[1024];
static int inbufsize;
ssize_t r;
#if defined(__linux__) && defined(_DEBUG)
int fl = fcntl (STDIN_FILENO, F_GETFL, 0);
if (!(fl & FNDELAY))
@ -499,53 +494,28 @@ void SSV_CheckFromMaster(void)
}
#endif
for(;;)
r = read(STDIN_FILENO, buffer, bytestoread);
if (r == 0 && bytestoread)
return -1; //eof
if (r < 0)
{
if(inbufsize >= 2)
{
unsigned short len = inbuffer[0] | (inbuffer[1]<<8);
if (inbufsize >= len && len>=2)
{
memcpy(net_message.data, inbuffer+2, len-2);
net_message.cursize = len-2;
memmove(inbuffer, inbuffer+len, inbufsize - len);
inbufsize -= len;
MSG_BeginReading (msg_nullnetprim);
SSV_ReadFromControlServer();
continue; //keep trying to handle it
}
}
if (inbufsize == sizeof(inbuffer))
{ //fatal: we can't easily recover from this.
SV_FinalMessage("Cluster message too large\n");
Cmd_ExecuteString("quit force", RESTRICT_LOCAL);
break;
}
{
ssize_t avail = read(STDIN_FILENO, inbuffer+inbufsize, sizeof(inbuffer)-inbufsize);
if (!avail)
{ //eof
SV_FinalMessage("Cluster shut down\n");
Cmd_ExecuteString("quit force", RESTRICT_LOCAL);
break;
}
else if (avail < 0)
{
int e = errno;
if (e == EAGAIN || e == EWOULDBLOCK)
;
else
perror("master read");
break;
}
else
inbufsize += avail;
}
int e = errno;
if (e == EINTR || e == EAGAIN || e == EWOULDBLOCK)
return 0;
}
return r;
}
qboolean QDECL Sys_StdinOutClose(vfsfile_t *fs)
{
Sys_Error("Shutdown\n");
}
vfsfile_t *Sys_GetStdInOutStream(void)
{
vfsfile_t *stream = Z_Malloc(sizeof(*stream));
stream->WriteBytes = Sys_StdoutWrite;
stream->ReadBytes = Sys_StdinRead;
stream->Close = Sys_StdinOutClose;
return stream;
}
#endif

View File

@ -460,66 +460,51 @@ void Sys_DestroyConditional(void *condv)
#endif
#ifdef SUBSERVERS
typedef struct slaveserver_s
typedef struct
{
pubsubserver_t pub;
vfsfile_t pub;
HANDLE inpipe;
HANDLE outpipe;
qbyte inbuffer[2048];
int inbufsize;
} winsubserver_t;
static void Sys_InstructSlave(pubsubserver_t *ps, sizebuf_t *cmd)
{
//FIXME: this is blocking. this is bad if the target is also blocking while trying to write to us.
//FIXME: merge buffering logic with SSV_InstructMaster, and allow for failure if full
winsubserver_t *s = (winsubserver_t*)ps;
DWORD written = 0;
cmd->data[0] = cmd->cursize & 0xff;
cmd->data[1] = (cmd->cursize>>8) & 0xff;
WriteFile(s->outpipe, cmd->data, cmd->cursize, &written, NULL);
}
static int Sys_SubServerRead(pubsubserver_t *ps)
static int QDECL Sys_MSV_ReadBytes (struct vfsfile_s *file, void *buffer, int bytestoread)
{
winsubserver_t *s = (winsubserver_t*)file;
DWORD avail;
winsubserver_t *s = (winsubserver_t*)ps;
//trying to do this stuff without blocking is a real pain.
if (!PeekNamedPipe(s->inpipe, NULL, 0, NULL, &avail, NULL))
return -1; //EOF
if (avail)
{
CloseHandle(s->inpipe);
CloseHandle(s->outpipe);
Con_Printf("%i:%s has died\n", s->pub.id, s->pub.name);
return -1;
}
else if (avail)
{
if (avail > sizeof(s->inbuffer)-1-s->inbufsize)
avail = sizeof(s->inbuffer)-1-s->inbufsize;
if (ReadFile(s->inpipe, s->inbuffer+s->inbufsize, avail, &avail, NULL))
s->inbufsize += avail;
}
if(s->inbufsize >= 2)
{
unsigned short len = s->inbuffer[0] | (s->inbuffer[1]<<8);
if (s->inbufsize >= len && len>=2)
{
memcpy(net_message.data, s->inbuffer+2, len-2);
net_message.cursize = len-2;
memmove(s->inbuffer, s->inbuffer+len, s->inbufsize - len);
s->inbufsize -= len;
MSG_BeginReading (msg_nullnetprim);
return 1;
}
if (avail > bytestoread)
avail = bytestoread;
if (ReadFile(s->inpipe, buffer, avail, &avail, NULL))
return avail;
}
return 0;
}
static int QDECL Sys_MSV_WriteBytes (struct vfsfile_s *file, const void *buffer, int bytestowrite)
{
winsubserver_t *s = (winsubserver_t*)file;
DWORD wrote = 0;
//blocks. life sucks.
if (!WriteFile(s->outpipe, buffer, bytestowrite, &wrote, NULL))
return -1;
return wrote;
}
static qboolean QDECL Sys_MSV_Close (struct vfsfile_s *file)
{
winsubserver_t *s = (winsubserver_t*)file;
pubsubserver_t *Sys_ForkServer(void)
CloseHandle(s->inpipe);
CloseHandle(s->outpipe);
//we already closed any process handles. the child will detect its input became unreadable and take that as a signal to die.
Z_Free(s);
return true;
}
vfsfile_t *Sys_ForkServer(void)
{
wchar_t exename[256];
wchar_t curdir[256];
@ -560,8 +545,9 @@ pubsubserver_t *Sys_ForkServer(void)
CloseHandle(startinfo.hStdOutput);
CloseHandle(startinfo.hStdInput);
ctx->pub.funcs.InstructSlave = Sys_InstructSlave;
ctx->pub.funcs.SubServerRead = Sys_SubServerRead;
ctx->pub.ReadBytes = Sys_MSV_ReadBytes;
ctx->pub.WriteBytes = Sys_MSV_WriteBytes;
ctx->pub.Close = Sys_MSV_Close;
return &ctx->pub;
}

View File

@ -471,6 +471,41 @@ void GL_SetupFormats(void)
glfmtb(PTI_ASTC_12X10_HDR, GL_COMPRESSED_RGBA_ASTC_12x10_KHR);
glfmtb(PTI_ASTC_12X12_HDR, GL_COMPRESSED_RGBA_ASTC_12x12_KHR);
}
#ifdef ASTC3D
if (sh_config.hw_astc >= 3)
{ //the full profile gives 3d texture support too
glfmtb(PTI_ASTC_3X3X3_LDR, GL_COMPRESSED_RGBA_ASTC_3x3x3_OES);
glfmtb(PTI_ASTC_4X3X3_LDR, GL_COMPRESSED_RGBA_ASTC_4x3x3_OES);
glfmtb(PTI_ASTC_4X4X3_LDR, GL_COMPRESSED_RGBA_ASTC_4x4x3_OES);
glfmtb(PTI_ASTC_4X4X4_LDR, GL_COMPRESSED_RGBA_ASTC_4x4x4_OES);
glfmtb(PTI_ASTC_5X4X4_LDR, GL_COMPRESSED_RGBA_ASTC_5x4x4_OES);
glfmtb(PTI_ASTC_5X5X4_LDR, GL_COMPRESSED_RGBA_ASTC_5x5x4_OES);
glfmtb(PTI_ASTC_5X5X5_LDR, GL_COMPRESSED_RGBA_ASTC_5x5x5_OES);
glfmtb(PTI_ASTC_6X5X5_LDR, GL_COMPRESSED_RGBA_ASTC_6x5x5_OES);
glfmtb(PTI_ASTC_6X6X5_LDR, GL_COMPRESSED_RGBA_ASTC_6x6x5_OES);
glfmtb(PTI_ASTC_6X6X6_LDR, GL_COMPRESSED_RGBA_ASTC_6x6x6_OES);
glfmtb(PTI_ASTC_3X3X3_SRGB, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES);
glfmtb(PTI_ASTC_4X3X3_SRGB, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES);
glfmtb(PTI_ASTC_4X4X3_SRGB, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES);
glfmtb(PTI_ASTC_4X4X4_SRGB, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES);
glfmtb(PTI_ASTC_5X4X4_SRGB, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES);
glfmtb(PTI_ASTC_5X5X4_SRGB, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES);
glfmtb(PTI_ASTC_5X5X5_SRGB, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES);
glfmtb(PTI_ASTC_6X5X5_SRGB, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES);
glfmtb(PTI_ASTC_6X6X5_SRGB, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES);
glfmtb(PTI_ASTC_6X6X6_SRGB, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES);
glfmtb(PTI_ASTC_3X3X3_HDR, GL_COMPRESSED_RGBA_ASTC_3x3x3_OES);
glfmtb(PTI_ASTC_4X3X3_HDR, GL_COMPRESSED_RGBA_ASTC_4x3x3_OES);
glfmtb(PTI_ASTC_4X4X3_HDR, GL_COMPRESSED_RGBA_ASTC_4x4x3_OES);
glfmtb(PTI_ASTC_4X4X4_HDR, GL_COMPRESSED_RGBA_ASTC_4x4x4_OES);
glfmtb(PTI_ASTC_5X4X4_HDR, GL_COMPRESSED_RGBA_ASTC_5x4x4_OES);
glfmtb(PTI_ASTC_5X5X4_HDR, GL_COMPRESSED_RGBA_ASTC_5x5x4_OES);
glfmtb(PTI_ASTC_5X5X5_HDR, GL_COMPRESSED_RGBA_ASTC_5x5x5_OES);
glfmtb(PTI_ASTC_6X5X5_HDR, GL_COMPRESSED_RGBA_ASTC_6x5x5_OES);
glfmtb(PTI_ASTC_6X6X5_HDR, GL_COMPRESSED_RGBA_ASTC_6x6x5_OES);
glfmtb(PTI_ASTC_6X6X6_HDR, GL_COMPRESSED_RGBA_ASTC_6x6x6_OES);
}
#endif
}
/*
@ -732,7 +767,7 @@ qboolean GL_LoadTextureMips(texid_t tex, const struct pendingtextureinfo *mips)
uploadfmt_t encoding = mips->encoding;
qboolean compress;
qboolean storage = true;
unsigned int bb, bw, bh;
unsigned int bb, bw, bh, bd;
int levels = 0, genlevels;
int ttype = (tex->flags & IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT;
@ -901,7 +936,7 @@ qboolean GL_LoadTextureMips(texid_t tex, const struct pendingtextureinfo *mips)
qglTexParameteri(targ, GL_TEXTURE_SWIZZLE_A, gl_config.formatinfo[encoding].swizzle_a);
}
Image_BlockSizeForEncoding(encoding, &bb, &bw, &bh);
Image_BlockSizeForEncoding(encoding, &bb, &bw, &bh, &bd);
switch(bb)
{
case 3:

View File

@ -2682,6 +2682,8 @@ int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int max
for (;;)
{
if (l >= end)
break;
n = Font_Decode(l, &codeflags, &codepoint);
if (!(codeflags & CON_HIDDEN) && (codepoint != ' '))
break;

View File

@ -3389,7 +3389,7 @@ static void Mod_LoadMiptex(model_t *loadmodel, texture_t *tx, miptex_t *mt, size
for (; extofs < miptexsize; extofs += extsize)
{
size_t sz, w, h;
unsigned int bb,bw,bh;
unsigned int bb,bw,bh,bd;
int mip;
qbyte *extdata = (void*)((qbyte*)mt+extofs);
char *extfmt = (char*)(extdata+4);
@ -3447,7 +3447,7 @@ static void Mod_LoadMiptex(model_t *loadmodel, texture_t *tx, miptex_t *mt, size
//alternative textures are usually compressed
//this means we insist on a FULL mip chain
//npot mips are explicitly round-down (but don't drop to 0 with non-square).
Image_BlockSizeForEncoding(newfmt, &bb, &bw, &bh);
Image_BlockSizeForEncoding(newfmt, &bb, &bw, &bh, &bd);
neww = (extdata[8]<<0)|(extdata[9]<<8)|(extdata[10]<<16)|(extdata[11]<<24);
newh = (extdata[12]<<0)|(extdata[13]<<8)|(extdata[14]<<16)|(extdata[15]<<24);
for (mip = 0, w=neww, h=newh, sz=0; w || h; mip++, w>>=1,h>>=1)

View File

@ -107,6 +107,7 @@ void R_AnimateLight (void)
{
int i,j;
float f;
static int fbmodcount;
//if (r_lightstylescale.value > 2)
@ -121,7 +122,12 @@ void R_AnimateLight (void)
i = (int)f;
f -= i; //this can require updates at 1000 times a second.. Depends on your framerate of course
for (j=0 ; j<cl_max_lightstyles ; j++)
if (r_fullbright.value)
{
for (j=0 ; j<cl_max_lightstyles ; j++)
d_lightstylevalue[j] = r_fullbright.value*255;
}
else for (j=0 ; j<cl_max_lightstyles ; j++)
{
int v1, v2, vd;
if (!cl_lightstyle[j].length)
@ -148,6 +154,18 @@ void R_AnimateLight (void)
else
d_lightstylevalue[j] = (v1*(1-f) + v2*(f))*22*r_lightstylescale.value;
}
if (r_fullbright.modified != fbmodcount)
{
fbmodcount = r_fullbright.modified;
for (j=0 ; j<cl_max_lightstyles ; j++)
{
if (r_fullbright.value)
cl_lightstyle[j].colourkey = 0xff;
else
cl_lightstyle[j].colourkey = (int)(cl_lightstyle[j].colours[0]*0x400) ^ (int)(cl_lightstyle[j].colours[1]*0x100000) ^ (int)(cl_lightstyle[j].colours[2]*0x40000000);
}
}
}
/*

View File

@ -1640,6 +1640,7 @@ static qboolean Shader_LoadPermutations(char *name, program_t *prog, char *scrip
var = Cvar_Get(namebuf, valuebuf, CVAR_SHADERSYSTEM, "GLSL Variables");
if (var)
{
var->flags |= CVAR_SHADERSYSTEM;
if (srgb)
{
if (type == '4')
@ -6459,7 +6460,7 @@ char *Shader_DefaultBSPWater(parsestate_t *ps, const char *shortname, char *buff
return (
"{\n"
"{\n"
// "program defaultfill\n"
//"program defaultfill\n"
"map $whiteimage\n"
"rgbgen srgb $r_fastturbcolour\n"
"}\n"
@ -6485,7 +6486,7 @@ char *Shader_DefaultBSPWater(parsestate_t *ps, const char *shortname, char *buff
"}\n"
"surfaceparm hasdiffuse\n"
"}\n"
, explicitalpha?"":va("#ALPHA=%g",alpha), alpha, alpha);
, (explicitalpha||alpha==1)?"":va("#ALPHA=%g",alpha), alpha, alpha);
return buffer;
case 2: //refraction of the underwater surface, with a fresnel
return (

View File

@ -4176,8 +4176,8 @@ qboolean Sh_StencilShadowsActive(void)
{
#if defined(RTLIGHTS) && !defined(SERVERONLY)
//if shadowmapping is forced on all lights then we don't need special depth stuff
// if (r_shadow_shadowmapping.ival)
// return false;
if (r_shadow_shadowmapping.ival)
return false;
if (isDedicated)
return false;
return (r_shadow_realtime_dlight.ival && r_shadow_realtime_dlight_shadows.ival) ||

View File

@ -1518,7 +1518,11 @@ static const char *glsl_hdrs[] =
"uniform vec3 v_eyepos;"
"uniform vec4 w_fog[2];\n"
"#define w_fogcolour w_fog[0].rgb\n"
"#ifdef FOG\n"
"#define w_fogalpha w_fog[0].a\n"
"#else\n"
"#define w_fogalpha 0.0\n"
"#endif\n"
"#define w_fogdensity w_fog[1].x\n"
"#define w_fogdepthbias w_fog[1].y\n"
"uniform vec4 w_user[16];\n"
@ -1796,7 +1800,7 @@ static const char *glsl_hdrs[] =
"return vec4(fog3(regularcolour.rgb), 1.0) * regularcolour.a;\n"
"}\n"
"vec4 fog4additive(in vec4 regularcolour)"
"{"
"{" //fog function for additive blends
"float z = w_fogdensity * gl_FragCoord.z / gl_FragCoord.w;\n"
"z = max(0.0,z-w_fogdepthbias);\n"
"#if #include \"cvar/r_fog_exp2\"\n"
@ -1807,7 +1811,7 @@ static const char *glsl_hdrs[] =
"return regularcolour * vec4(fac, fac, fac, 1.0);\n"
"}\n"
"vec4 fog4blend(in vec4 regularcolour)"
"{"
"{" //fog function for regular alpha blends (uses the blend for fading, to avoid fighting the surface behind)
"float z = w_fogdensity * gl_FragCoord.z / gl_FragCoord.w;\n"
"z = max(0.0,z-w_fogdepthbias);\n"
"#if #include \"cvar/r_fog_exp2\"\n"
@ -1818,6 +1822,7 @@ static const char *glsl_hdrs[] =
"return regularcolour * vec4(1.0, 1.0, 1.0, fac);\n"
"}\n"
"#else\n"
"#define w_fogalpha 0.0\n"
/*don't use macros for this - mesa bugs out*/
"vec3 fog3(in vec3 regularcolour) { return regularcolour; }\n"
"vec3 fog3additive(in vec3 regularcolour) { return regularcolour; }\n"

View File

@ -35,6 +35,7 @@ static void GL_DrawSkyGrid (texnums_t *tex);
extern cvar_t gl_skyboxdist;
extern cvar_t r_fastsky;
extern cvar_t r_fastskycolour;
extern cvar_t r_skycloudalpha;
static shader_t *forcedsky;
static shader_t *skyboxface;
@ -1100,13 +1101,14 @@ void R_InitSky (shader_t *shader, const char *skyname, uploadfmt_t fmt, qbyte *s
if (fmt & PTI_FULLMIPCHAIN)
{ //input is expected to make sense...
qbyte *front, *back;
unsigned int bb, bw, bh;
unsigned int bb, bw, bh, bd;
unsigned int w, h, y;
fmt = fmt&~PTI_FULLMIPCHAIN;
Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh);
Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh, &bd);
w = (width+bw-1)/bw;
h = (height+bh-1)/bh;
//d = (depth+bd-1)/bd;
back = BZ_Malloc(bb*w*2*h);
front = back + bb*w*h;
@ -1162,7 +1164,9 @@ void R_InitSky (shader_t *shader, const char *skyname, uploadfmt_t fmt, qbyte *s
((qbyte *)&transpix)[1] = g/(width*height);
((qbyte *)&transpix)[2] = b/(width*height);
((qbyte *)&transpix)[3] = 0;
alphamask = LittleLong(0x7fffffff);
alphamask = r_skycloudalpha.value*255;
alphamask = ((bound(0, alphamask, 0xff)<<24) | 0x00ffffff);
alphamask = LittleLong(alphamask);
for (i=0 ; i<height ; i++)
for (j=0 ; j<width ; j++)
{

View File

@ -773,6 +773,28 @@ typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD
#endif
#ifndef GL_COMPRESSED_RGBA_ASTC_3x3x3_OES
#define GL_COMPRESSED_RGBA_ASTC_3x3x3_OES 0x93C0
#define GL_COMPRESSED_RGBA_ASTC_4x3x3_OES 0x93C1
#define GL_COMPRESSED_RGBA_ASTC_4x4x3_OES 0x93C2
#define GL_COMPRESSED_RGBA_ASTC_4x4x4_OES 0x93C3
#define GL_COMPRESSED_RGBA_ASTC_5x4x4_OES 0x93C4
#define GL_COMPRESSED_RGBA_ASTC_5x5x4_OES 0x93C5
#define GL_COMPRESSED_RGBA_ASTC_5x5x5_OES 0x93C6
#define GL_COMPRESSED_RGBA_ASTC_6x5x5_OES 0x93C7
#define GL_COMPRESSED_RGBA_ASTC_6x6x5_OES 0x93C8
#define GL_COMPRESSED_RGBA_ASTC_6x6x6_OES 0x93C9
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES 0x93E0
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES 0x93E1
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES 0x93E2
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES 0x93E3
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES 0x93E4
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES 0x93E5
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES 0x93E6
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES 0x93E7
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES 0x93E8
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES 0x93E9
#endif
#ifndef GL_ARB_pixel_buffer_object
#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB

View File

@ -363,6 +363,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"!!samps =REFLECT reflect=1\n"
"!!samps =RIPPLEMAP ripplemap=2\n"
"!!samps =DEPTH refractdepth=3\n"
"!!permu FOG\n"
"#include \"sys/defs.h\"\n"
@ -445,9 +446,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"}\n"
"#endif\n"
"#ifdef FRAGMENT_SHADER\n"
"#ifdef ALPHA\n"
"#include \"sys/fog.h\"\n"
"#endif\n"
"void main (void)\n"
@ -536,6 +535,8 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"vec4 ts = texture2D(s_diffuse, ntc);\n"
"vec4 surf = fog4blend(vec4(ts.rgb, float(ALPHA)*ts.a));\n"
"refr = mix(refr, surf.rgb, surf.a);\n"
"#else\n"
"refr = fog3(refr); \n"
"#endif\n"
//done
@ -4159,7 +4160,8 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
#ifdef GLQUAKE
{QR_OPENGL, 110, "defaultsky",
"!!permu FOG\n"
"!!samps 2\n"
"!!samps base=0, cloud=1\n"
"!!cvardf r_skyfog=0.5\n"
"#include \"sys/fog.h\"\n"
//regular sky shader for scrolling q1 skies
@ -4184,10 +4186,15 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"dir.z *= 3.0;\n"
"dir.xy /= 0.5*length(dir);\n"
"tccoord = (dir.xy + e_time*0.03125);\n"
"vec3 solid = vec3(texture2D(s_t0, tccoord));\n"
"vec3 sky = vec3(texture2D(s_base, tccoord));\n"
"tccoord = (dir.xy + e_time*0.0625);\n"
"vec4 clouds = texture2D(s_t1, tccoord);\n"
"gl_FragColor = vec4(fog3((solid.rgb*(1.0-clouds.a)) + (clouds.a*clouds.rgb)), 1.0);\n"
"vec4 clouds = texture2D(s_cloud, tccoord);\n"
"sky = (sky.rgb*(1.0-clouds.a)) + (clouds.a*clouds.rgb);\n"
"#ifdef FOG\n"
"sky.rgb = mix(sky.rgb, w_fogcolour, float(r_skyfog)*w_fogalpha); //flat fog ignoring actual geometry\n"
//sky = fog3(sky); //fog according to actual geometry
"#endif\n"
"gl_FragColor = vec4(sky, 1.0);\n"
"}\n"
"#endif\n"
},
@ -4643,7 +4650,14 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"void main ()\n"
"{\n"
"vec4 skybox = textureCube(s_reflectcube, pos);\n"
"gl_FragColor = vec4(mix(skybox.rgb, fog3(skybox.rgb), float(r_skyfog)), 1.0);\n"
//Fun question: should sky be fogged as if infinite, or as if an actual surface?
"#if 1\n"
"skybox.rgb = mix(skybox.rgb, w_fogcolour_ float(r_skyfog)*w_fogalpha); //flat fog ignoring actual geometry\n"
"#else\n"
"skybox.rgb = mix(skybox.rgb, fog3(skybox.rgb), float(r_skyfog)); //fog in terms of actual geometry distance\n"
"#endif\n"
"gl_FragColor = skybox;\n"
"}\n"
"#endif\n"
},
@ -5617,7 +5631,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"!!ver 100 450\n"
"!!permu TESS\n"
"!!permu DELUXE\n"
"!!permu FULLBRIGHT\n"
"!!permu FULLBRIGHT //lumas rather than no lightmaps\n"
"!!permu FOG\n"
"!!permu LIGHTSTYLED\n"
"!!permu BUMP\n"
@ -5634,7 +5648,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
//diffuse gives us alpha, and prevents dlight from bugging out when there's no diffuse.
"!!samps =EIGHTBIT paletted 1\n"
"!!samps =SPECULAR specular\n"
"!!samps lightmap\n"
"!!samps !VERTEXLIT lightmap\n"
"!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3\n"
"!!samps =DELUXE deluxemap\n"
"!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3\n"
@ -5885,7 +5899,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
//optional: round the lightmap coords to ensure all pixels within a texel have different lighting values either. it just looks wrong otherwise.
//don't bother if its lightstyled, such cases will have unpredictable correlations anyway.
//FIXME: this rounding is likely not correct with respect to software rendering. oh well.
"#if __VERSION__ >= 130\n"
"#if __VERSION__ >= 130 && !defined(VERTEXLIT)\n"
"vec2 lmsize = vec2(textureSize(s_lightmap0, 0));\n"
"#else\n"
"#define lmsize vec2(128.0,2048.0)\n"
@ -7021,7 +7035,6 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
{QR_OPENGL, 110, "defaultwarp",
"!!ver 100 450\n"
"!!permu FOG\n"
"!!cvarf r_wateralpha\n"
"!!samps diffuse lightmap\n"
"#include \"sys/defs.h\"\n"
@ -7030,6 +7043,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
//this is expected to be moderately fast.
"#include \"sys/fog.h\"\n"
"varying vec2 tc;\n"
"#ifdef LIT\n"
"varying vec2 lm0;\n"
@ -7048,12 +7062,6 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"}\n"
"#endif\n"
"#ifdef FRAGMENT_SHADER\n"
"#ifndef ALPHA\n"
"uniform float cvar_r_wateralpha;\n"
"#define USEALPHA cvar_r_wateralpha\n"
"#else\n"
"#define USEALPHA float(ALPHA)\n"
"#endif\n"
"void main ()\n"
"{\n"
"vec2 ntc;\n"
@ -7065,7 +7073,11 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"ts *= (texture2D(s_lightmap, lm0) * e_lmscale).rgb;\n"
"#endif\n"
"gl_FragColor = fog4blend(vec4(ts, USEALPHA) * e_colourident);\n"
"#ifdef ALPHA\n"
"gl_FragColor = fog4blend(vec4(ts, float(ALPHA)) * e_colourident);\n"
"#else\n"
"gl_FragColor = fog4(vec4(ts, 1.0) * e_colourident);\n"
"#endif\n"
"}\n"
"#endif\n"
},

View File

@ -1116,15 +1116,86 @@ void QCBUILTIN PF_memsetval (pubprogfuncs_t *inst, struct globalvars_s *globals)
//#define GCTIMINGS
#ifdef QCGC
#define smallbool char
static smallbool *PR_QCGC_Mark(void *mem, size_t memsize, size_t numtemps)
{
unsigned int *str; //the reference we're considering
size_t p;
smallbool *marked; //just booleans. could compact.
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 = mem, p = 0; p < memsize; p+=sizeof(*str), str++)
{
if ((*str & STRING_SPECMASK) == STRING_TEMP)
{
unsigned int idx = *str &~ STRING_SPECMASK;
if (idx < numtemps)
marked[idx] = true;
}
}
return marked;
}
static size_t PR_QCGC_Sweep(progfuncs_t *progfuncs, smallbool *marked, tempstr_t **tempstrings, unsigned int numtemps)
{
unsigned int p;
unsigned int swept = 0;
#ifdef GCTIMINGS
unsigned int unswept = 0;
unsigned int errors = 0;
#endif
for (p = 0; p < numtemps; p++)
{
if (marked[p])
{ //still live...
#ifdef GCTIMINGS
unswept++;
if (!tempstrings[p])
errors++;
#endif
}
else if (tempstrings[p])
{ //not marked, but was valid at the time our snapshot was taken
#ifdef _DEBUG
if (tempstrings[p] != prinst.tempstrings[p])
{ //something weird happened. tempstrings are supposed to be immutable (at least in length).
externs->Sys_Error("tempstring was reallocated while qc was running");
continue;
}
#endif
swept++;
//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.
prinst.tempstrings[p] = NULL;
externs->memfree(tempstrings[p]);
}
}
free(marked);
return swept;
}
#ifdef THREADEDGC
#include "quakedef.h"
struct qcgccontext_s
{
int done;
size_t clearedtemps; //number of temps that were swept away
unsigned int clearedtemps; //number of temps that were swept away
progfuncs_t *progfuncs; //careful!
size_t numtemps; //so it doesn't go stale
size_t maxtemps; //so it doesn't go stale
tempstr_t **tempstrings;//so we don't get confused over temps added while marking
size_t memsize;
@ -1138,91 +1209,39 @@ void PR_QCGC_Done(void *ctx, void *data, size_t a, size_t b)
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;
progfuncs_t *progfuncs = gc->progfuncs;
smallbool *marked;
#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;
}
}
marked = PR_QCGC_Mark(gc->amem, gc->memsize, gc->maxtemps);
#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);
gc->clearedtemps = PR_QCGC_Sweep(progfuncs, marked, gc->tempstrings, gc->maxtemps);
#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);
gc->externs->Printf("live: %u, dead: %u, threadtime: mark=%f, sweep=%f, total=%f\n", prinst.livetemps-gc->clearedtemps, gc->clearedtemps, (markedtime - starttime), (endtime - markedtime), endtime-starttime);
#endif
COM_InsertWork(WG_MAIN, PR_QCGC_Done, gc, NULL, 0, 0);
}
#endif
static void PR_ExpandTempStrings(progfuncs_t *progfuncs, size_t newmax)
{
tempstr_t **ntable = progfuncs->funcs.parms->memalloc(sizeof(*ntable) * newmax);
memcpy(ntable, prinst.tempstrings, sizeof(*ntable) * prinst.maxtempstrings);
memset(ntable+prinst.maxtempstrings, 0, sizeof(*ntable) * (newmax-prinst.maxtempstrings));
prinst.maxtempstrings = newmax;
if (prinst.tempstrings)
progfuncs->funcs.parms->memfree(prinst.tempstrings);
prinst.tempstrings = ntable;
}
static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, unsigned int len)
{
progfuncs_t *fte_restrict progfuncs = (progfuncs_t *)ppf;
tempstr_t **ntable;
int newmax;
progfuncs_t *progfuncs = (progfuncs_t *)ppf;
int i;
if (!str)
@ -1230,21 +1249,16 @@ static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str,
if (prinst.livetemps == prinst.maxtempstrings)
{
#ifdef THREADEDGC
//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);
}
#endif
newmax = prinst.maxtempstrings*2 + 1024;
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;
PR_ExpandTempStrings(progfuncs, prinst.maxtempstrings*2 + 1024);
}
for (i = prinst.nexttempstring; i < prinst.maxtempstrings && prinst.tempstrings[i]; i++)
@ -1265,15 +1279,18 @@ static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str,
return (string_t)((unsigned int)i | STRING_TEMP);
}
pbool PR_RunGC (progfuncs_t *progfuncs)
void PR_RunGC (progfuncs_t *progfuncs)
{
#ifdef THREADEDGC
if (!prinst.gccontext)
#endif
{
if (prinst.livetemps < prinst.maxtempstrings/2 || prinst.nexttempstring < prinst.maxtempstrings/2)
{ //don't bother yet
return false;
return;
}
else
#ifdef THREADEDGC
if (externs->usethreadedgc)
{
#ifdef GCTIMINGS
double starttime = Sys_DoubleTime(), endtime;
@ -1286,18 +1303,30 @@ pbool PR_RunGC (progfuncs_t *progfuncs)
gc->memsize = prinst.addressableused;
memcpy(gc->amem, prinst.addressablehunk, prinst.addressableused);
gc->numtemps = prinst.maxtempstrings;
gc->maxtemps = prinst.maxtempstrings;
gc->tempstrings = (void*)((char*)gc->amem+prinst.addressableused);
memcpy(gc->tempstrings, prinst.tempstrings, sizeof(*gc->tempstrings)*gc->numtemps);
memcpy(gc->tempstrings, prinst.tempstrings, sizeof(*gc->tempstrings)*gc->maxtemps);
COM_InsertWork(WG_LOADER, PR_QCGC_Thread, gc, NULL, 0, 0);
COM_AddWork(WG_LOADER, PR_QCGC_Thread, gc, NULL, 0, 0);
#ifdef GCTIMINGS
endtime = Sys_DoubleTime();
gc->externs->Printf("preparetime=%f\n", (endtime - starttime));
#endif
return;
}
#endif
{ //same-thread gc.
smallbool *marked = PR_QCGC_Mark(prinst.addressablehunk, prinst.addressableused, prinst.maxtempstrings);
size_t swept = PR_QCGC_Sweep(progfuncs, marked, prinst.tempstrings, prinst.maxtempstrings);
prinst.livetemps -= swept;
//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)
PR_ExpandTempStrings(progfuncs, prinst.maxtempstrings * 2);
}
}
#ifdef THREADEDGC
else if (prinst.gccontext->done)
{
prinst.livetemps -= prinst.gccontext->clearedtemps;
@ -1306,29 +1335,21 @@ pbool PR_RunGC (progfuncs_t *progfuncs)
//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;
PR_ExpandTempStrings(progfuncs, prinst.maxtempstrings * 2);
}
return true; //running...
#endif
}
static void PR_FreeAllTemps (progfuncs_t *progfuncs)
{
unsigned int i;
#ifdef THREADEDGC
while (prinst.gccontext)
{
COM_WorkerPartialSync(prinst.gccontext, &prinst.gccontext->done, false);
PR_RunGC(progfuncs);
}
#endif
for (i = 0; i < prinst.maxtempstrings; i++)
{
externs->memfree(prinst.tempstrings[i]);
@ -1337,151 +1358,11 @@ static void PR_FreeAllTemps (progfuncs_t *progfuncs)
prinst.maxtempstrings = 0;
prinst.nexttempstring = 0;
}
#elif defined(QCGC)
pbool PR_RunGC (progfuncs_t *progfuncs)
{
unsigned int p;
char *marked;
unsigned int *str;
unsigned int r_l, r_d;
#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;
#ifdef GCTIMINGS
starttime = Sys_DoubleTime();
#endif
marked = malloc(sizeof(*marked) * prinst.numtempstrings);
memset(marked, 0, sizeof(*marked) * prinst.numtempstrings);
//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 = (unsigned int*)prinst.addressablehunk, p = 0; p < prinst.addressableused; p+=sizeof(*str), str++)
{
if ((*str & STRING_SPECMASK) == STRING_TEMP)
{
unsigned int idx = *str &~ STRING_SPECMASK;
if (idx < prinst.numtempstrings)
marked[idx] = true;
}
}
//sweep
#ifdef GCTIMINGS
markedtime = Sys_DoubleTime();
#endif
r_l = 0;
r_d = 0;
for (p = 0; p < prinst.numtempstrings; p++)
{
if (marked[p])
{
r_l++;
}
else
break;
}
prinst.nexttempstring = p;
for (; p < prinst.numtempstrings; p++)
{
if (marked[p])
{
r_l++;
}
else if (prinst.tempstrings[p])
{
r_d++;
externs->memfree(prinst.tempstrings[p]);
prinst.tempstrings[p] = NULL;
}
}
while (prinst.numtempstrings > 0 && prinst.tempstrings[prinst.numtempstrings-1] == NULL)
prinst.numtempstrings--;
free(marked);
//if over half the (max)strings are still live, just increase the max so we are not spamming collections
r_d += prinst.maxtempstrings - prinst.numtempstrings;
if (r_l > r_d)
{
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;
}
#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;
tempstr_t **ntable, *n;
int newmax;
int i;
@ -1505,9 +1386,10 @@ static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str,
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;
n = progfuncs->funcs.parms->memalloc(sizeof(tempstr_t) - sizeof(((tempstr_t*)NULL)->value) + len);
n->size = len;
*str = n->value;
prinst.tempstrings[i] = n; //doesn't have its value yet...
return (string_t)((unsigned int)i | STRING_TEMP);
}
@ -1738,53 +1620,6 @@ static void PDECL qclib_free(void *ptr)
#define printf NULL //should be some null wrapper instead
#endif
//defs incase following structure is not passed.
static struct edict_s *safesv_edicts;
static int safesv_num_edicts;
static double safetime=0;
static progexterns_t defexterns = {
PROGSTRUCT_VERSION,
NULL, //char *(*ReadFile) (char *fname, void *buffer, int len);
NULL, //int (*FileSize) (char *fname); //-1 if file does not exist
NULL, //bool (*WriteFile) (char *name, void *data, int len);
qclib_null_printf, //void (*printf) (char *, ...);
qclib_null_printf, //void (*dprintf) (char *, ...);
(void*)exit, //void (*Sys_Error) (char *, ...);
NULL, //void (*Abort) (char *, ...);
NULL,
NULL, //void (*entspawn) (struct edict_s *ent); //ent has been spawned, but may not have all the extra variables (that may need to be set) set
NULL, //bool (*entcanfree) (struct edict_s *ent); //return true to stop ent from being freed
NULL, //void (*stateop) (float var, func_t func);
NULL,
NULL,
NULL,
//used when loading a game
NULL, //builtin_t *(*builtinsfor) (int num); //must return a pointer to the builtins that were used before the state was saved.
NULL, //void (*loadcompleate) (int edictsize); //notification to reset any pointers.
NULL,
qclib_malloc, //void *(*memalloc) (int size); //small string allocation malloced and freed randomly by the executor. (use memalloc if you want)
qclib_free, //void (*memfree) (void * mem);
NULL, //int (*useeditor) (char *filename, int line, int nump, char **parms);
NULL, //relocated
NULL, //builtin_t *globalbuiltins; //these are available to all progs
0, //int numglobalbuiltins;
PR_NOCOMPILE,
&safetime, //double *gametime;
&safesv_edicts, //struct edict_s **sv_edicts;
&safesv_num_edicts, //int *sv_num_edicts;
sizeof(edictrun_t), //int edictsize; //size of edict_t
};
//progfuncs_t *progfuncs = NULL;
#undef memfree
#undef prinst
@ -1853,17 +1688,13 @@ pubprogfuncs_t * PDECL InitProgs(progexterns_t *ext)
progfuncs_t *funcs;
if (!ext)
ext = &defexterns;
else
{
int i;
if (ext->progsversion > PROGSTRUCT_VERSION)
return NULL;
static progexterns_t defexterns;
ext = &defexterns;
}
else if (ext->progsversion != PROGSTRUCT_VERSION)
return NULL;
for (i=0;i<sizeof(progexterns_t); i+=4) //make sure there are no items left out.
if (!*(int *)((char *)ext+i))
*(int *)((char *)ext+i) = *(int *)((char *)&defexterns+i);
}
#undef memalloc
#undef pr_progstate
#undef pr_argc
@ -1875,6 +1706,24 @@ pubprogfuncs_t * PDECL InitProgs(progexterns_t *ext)
funcs->funcs.parms = ext;
{
//defs incase following structure is not passed.
static struct edict_s *safe_edicts;
static int safe_num_edicts;
static double safetime=0;
if (!ext->progsversion) ext->progsversion = PROGSTRUCT_VERSION;
if (!ext->Printf) ext->Printf = qclib_null_printf;
if (!ext->DPrintf) ext->DPrintf = qclib_null_printf;
if (!ext->Sys_Error) ext->Sys_Error = (void*)exit;
if (!ext->memalloc) ext->memalloc = qclib_malloc;
if (!ext->memfree) ext->memfree = qclib_free;
if (!ext->gametime) ext->gametime = &safetime;
if (!ext->edicts) ext->edicts = &safe_edicts;
if (!ext->num_edicts) ext->num_edicts = &safe_num_edicts;
if (!ext->edictsize) ext->edictsize = sizeof(edictrun_t);
}
SetEndian();
return &funcs->funcs;

View File

@ -887,12 +887,13 @@ Returns a string with a description and the contents of a global,
padded to 20 field width
============
*/
char *PR_GlobalString (progfuncs_t *progfuncs, int ofs)
#include "qcc.h"
char *PR_GlobalString (progfuncs_t *progfuncs, int ofs, struct QCC_type_s **typehint)
{
char *s;
int i;
ddef16_t *def16;
ddef32_t *def32;
ddef32_t *def32, def32tmp;
void *val;
static char line[128];
@ -900,41 +901,57 @@ char *PR_GlobalString (progfuncs_t *progfuncs, int ofs)
{
case PST_DEFAULT:
case PST_KKQWSV:
val = (void *)&pr_globals[ofs];
def16 = ED_GlobalAtOfs16(progfuncs, ofs);
if (!def16)
sprintf (line,"%i(?""?""?)", ofs);
else
if (def16)
{
s = PR_ValueString (progfuncs, def16->type, val, false);
sprintf (line,"%i(%s)%s", ofs, def16->s_name+progfuncs->funcs.stringtable, s);
def32 = &def32tmp;
def32->ofs = def16->ofs;
def32->type = def16->type;
def32->s_name = def16->s_name;
}
i = strlen(line);
for ( ; i<20 ; i++)
strcat (line," ");
strcat (line," ");
return line;
else
def32 = NULL;
break;
case PST_QTEST:
case PST_FTE32:
val = (void *)&pr_globals[ofs];
def32 = ED_GlobalAtOfs32(progfuncs, ofs);
if (!def32)
sprintf (line,"%i(?""?""?)", ofs);
else
{
s = PR_ValueString (progfuncs, def32->type, val, false);
sprintf (line,"%i(%s)%s", ofs, def32->s_name+progfuncs->funcs.stringtable, s);
}
i = strlen(line);
for ( ; i<20 ; i++)
strcat (line," ");
strcat (line," ");
return line;
break;
default:
externs->Sys_Error("Bad struct type in PR_GlobalString");
return "";
}
externs->Sys_Error("Bad struct type in PR_GlobalString");
return "";
val = (void *)&pr_globals[ofs];
if (!def32)
{
etype_t type;
//urgh, this is so hideous
if (typehint == &type_float)
type = ev_float;
else if (typehint == &type_string)
type = ev_string;
else if (typehint == &type_vector)
type = ev_vector;
else if (typehint == &type_function)
type = ev_function;
else if (typehint == &type_field)
type = ev_field;
else
type = ev_integer;
s = PR_ValueString (progfuncs, type, val, false);
sprintf (line,"%i(?)%s", ofs, s);
}
else
{
s = PR_ValueString (progfuncs, def32->type, val, false);
sprintf (line,"%i(%s)%s", ofs, def32->s_name+progfuncs->funcs.stringtable, s);
}
i = strlen(line);
for ( ; i<20 ; i++)
strcat (line," ");
strcat (line," ");
return line;
}
char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs)

View File

@ -122,6 +122,7 @@ static void PR_PrintStatement (progfuncs_t *progfuncs, int statementnum)
}
#if !defined(MINIMAL) && !defined(OMIT_QCC)
#define TYPEHINT(a) (pr_opcodes[op].type_##a)
if ( (unsigned)op < OP_NUMOPS)
{
int i;
@ -134,23 +135,27 @@ static void PR_PrintStatement (progfuncs_t *progfuncs, int statementnum)
#endif
externs->Printf ("op%3i ", op);
#ifndef TYPEHINT
#define TYPEHINT(a) NULL
#endif
if (op == OP_IF_F || op == OP_IFNOT_F)
externs->Printf ("%sbranch %i",PR_GlobalString(progfuncs, arg[0]),arg[1]);
externs->Printf ("%sbranch %i",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)),arg[1]);
else if (op == OP_GOTO)
{
externs->Printf ("branch %i",arg[0]);
}
else if ( (unsigned)(op - OP_STORE_F) < 6)
{
externs->Printf ("%s",PR_GlobalString(progfuncs, arg[0]));
externs->Printf ("%s",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)));
externs->Printf ("%s", PR_GlobalStringNoContents(progfuncs, arg[1]));
}
else
{
if (arg[0])
externs->Printf ("%s",PR_GlobalString(progfuncs, arg[0]));
externs->Printf ("%s",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)));
if (arg[1])
externs->Printf ("%s",PR_GlobalString(progfuncs, arg[1]));
externs->Printf ("%s",PR_GlobalString(progfuncs, arg[1], TYPEHINT(b)));
if (arg[2])
externs->Printf ("%s", PR_GlobalStringNoContents(progfuncs, arg[2]));
}
@ -233,19 +238,19 @@ void PDECL PR_GenerateStatementString (pubprogfuncs_t *ppf, int statementnum, ch
if (op == OP_IF_F || op == OP_IFNOT_F || op == OP_IF_I || op == OP_IFNOT_I || op == OP_IF_S || op == OP_IFNOT_S)
{
QC_snprintfz (out, outlen, "%sbranch %i(%i)",PR_GlobalStringNoContents(progfuncs, arg[0]),(short)arg[1], statementnum+(short)arg[0]);
QC_snprintfz (out, outlen, "%sbranch %i(%+i)",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)),(short)arg[1], statementnum+(short)arg[0]);
outlen -= strlen(out);
out += strlen(out);
}
else if (op == OP_GOTO)
{
QC_snprintfz (out, outlen, "branch %i(%i)",(short)arg[0], statementnum+(short)arg[0]);
QC_snprintfz (out, outlen, "branch %i(%+i)",(short)arg[0], statementnum+(short)arg[0]);
outlen -= strlen(out);
out += strlen(out);
}
else if ( (unsigned)(op - OP_STORE_F) < 6)
{
QC_snprintfz (out, outlen, "%s",PR_GlobalStringNoContents(progfuncs, arg[0]));
QC_snprintfz (out, outlen, "%s",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)));
outlen -= strlen(out);
out += strlen(out);
QC_snprintfz (out, outlen, "%s", PR_GlobalStringNoContents(progfuncs, arg[1]));
@ -256,13 +261,13 @@ void PDECL PR_GenerateStatementString (pubprogfuncs_t *ppf, int statementnum, ch
{
if (arg[0])
{
QC_snprintfz (out, outlen, "%s",PR_GlobalStringNoContents(progfuncs, arg[0]));
QC_snprintfz (out, outlen, "%s",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)));
outlen -= strlen(out);
out += strlen(out);
}
if (arg[1])
{
QC_snprintfz (out, outlen, "%s",PR_GlobalStringNoContents(progfuncs, arg[1]));
QC_snprintfz (out, outlen, "%s",PR_GlobalString(progfuncs, arg[1], TYPEHINT(b)));
outlen -= strlen(out);
out += strlen(out);
}

View File

@ -86,29 +86,28 @@ typedef struct
prclocks_t timestamp;
} prstack_t;
#if defined(QCGC) && defined(MULTITHREAD)
#define THREADEDGC
#endif
typedef struct
{
unsigned int size;
char value[4];
unsigned int size; //size of the data.
char value[4]; //contents of the tempstring (or really any binary data - but not tempstring references because we don't mark these!).
} 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
#if defined(QCGC)
unsigned int nexttempstring;
unsigned int livetemps; //increased on alloc, decremented after sweep
struct qcgccontext_s *gccontext;
#elif defined(QCGC)
unsigned int numtempstrings;
unsigned int nexttempstring;
#ifdef THREADEDGC
struct qcgccontext_s *gccontext;
#endif
#else
unsigned int numtempstrings;
unsigned int numtempstringsstack;
@ -235,8 +234,8 @@ extern QCC_opcode_t pr_opcodes[]; // sized by initialization
#define min(a,b) ((a) < (b) ? (a) : (b))
#endif
#define sv_num_edicts (*externs->sv_num_edicts)
#define sv_edicts (*externs->sv_edicts)
#define sv_num_edicts (*externs->num_edicts)
#define sv_edicts (*externs->edicts)
#define PR_DPrintf externs->DPrintf
//#define printf syntax error
@ -405,7 +404,12 @@ void PR_Profile_f (void);
struct edict_s *PDECL ED_Alloc (pubprogfuncs_t *progfuncs, pbool object, size_t extrasize);
void PDECL ED_Free (pubprogfuncs_t *progfuncs, struct edict_s *ed, pbool instant);
pbool PR_RunGC (progfuncs_t *progfuncs);
#ifdef QCGC
void PR_RunGC (progfuncs_t *progfuncs);
#else
void PR_FreeTemps (progfuncs_t *progfuncs, int depth);
#endif
string_t PDECL PR_AllocTempString (pubprogfuncs_t *ppf, const char *str);
char *PDECL ED_NewString (pubprogfuncs_t *ppf, const char *string, int minlength, pbool demarkup);
// returns a copy of the string allocated from the server's string heap
@ -531,9 +535,7 @@ ddef32_t *ED_GlobalAtOfs32 (progfuncs_t *progfuncs, unsigned int ofs);
string_t PDECL PR_StringToProgs (pubprogfuncs_t *inst, const char *str);
const char *ASMCALL PR_StringToNative (pubprogfuncs_t *inst, string_t str);
void PR_FreeTemps (progfuncs_t *progfuncs, int depth);
char *PR_GlobalString (progfuncs_t *progfuncs, int ofs);
char *PR_GlobalString (progfuncs_t *progfuncs, int ofs, struct QCC_type_s **typehint);
char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs);
pbool CompileFile(progfuncs_t *progfuncs, const char *filename);

View File

@ -239,8 +239,10 @@ typedef struct progexterns_s {
double *gametime; //used to prevent the vm from reusing an entity faster than 2 secs.
struct edict_s **sv_edicts; //pointer to the engine's reference to world.
unsigned int *sv_num_edicts; //pointer to the engine's edict count.
pbool usethreadedgc;
struct edict_s **edicts; //pointer to the engine's reference to world.
unsigned int *num_edicts; //pointer to the engine's edict count.
int edictsize; //size of edict_t
void *user; /*contains the owner's world reference in FTE*/

View File

@ -808,9 +808,12 @@ void Q_SetProgsParms(qboolean forcompiler)
svprogparms.autocompile = PR_COMPILEIGNORE;//PR_COMPILECHANGED;//enum {PR_NOCOMPILE, PR_COMPILENEXIST, PR_COMPILECHANGED, PR_COMPILEALWAYS} autocompile;
svprogparms.gametime = &sv.time;
#ifdef MULTITHREAD
svprogparms.usethreadedgc = pr_gc_threaded.ival;
#endif
svprogparms.sv_edicts = (edict_t**)&sv.world.edicts;
svprogparms.sv_num_edicts = &sv.world.num_edicts;
svprogparms.edicts = (edict_t**)&sv.world.edicts;
svprogparms.num_edicts = &sv.world.num_edicts;
svprogparms.useeditor = QCEditor;
@ -12143,6 +12146,27 @@ void PR_DumpPlatform_f(void)
{"global_gravitydir", "vector", QW|NQ|CS, D("The direction gravity should act in if not otherwise specified per entity."), 0,"'0 0 -1'"},
{"serverid", "int", QW|NQ|CS, D("The unique id of this server within the server cluster.")},
{"button3", ".float", QW|NQ},
{"button4", ".float", QW|NQ},
{"button5", ".float", QW|NQ},
{"button6", ".float", QW|NQ},
{"button7", ".float", QW|NQ},
{"button8", ".float", QW|NQ},
//and for dp compat (these names are fucked, yes)
// {"buttonuse", ".float", QW|NQ},
// {"buttonchat", ".float", QW|NQ},
// {"cursor_active", ".float", QW|NQ},
// {"button9", ".float", QW|NQ},
// {"button10", ".float", QW|NQ},
// {"button11", ".float", QW|NQ},
// {"button12", ".float", QW|NQ},
// {"button13", ".float", QW|NQ},
// {"button14", ".float", QW|NQ},
// {"button15", ".float", QW|NQ},
// {"button16", ".float", QW|NQ},
#define comfieldfloat(name,desc) {#name, ".float", FL, D(desc)},
#define comfieldint(name,desc) {#name, ".int", FL, D(desc)},
#define comfieldvector(name,desc) {#name, ".vector", FL, D(desc)},

View File

@ -1211,28 +1211,6 @@ void SV_FixupName(const char *in, char *out, unsigned int outlen);
#ifdef SUBSERVERS
//cluster stuff
typedef struct pubsubserver_s
{
struct
{
void (*InstructSlave)(struct pubsubserver_s *ps, sizebuf_t *cmd); //send to. first two bytes of the message should be ignored (overwrite them to carry size)
int (*SubServerRead)(struct pubsubserver_s *ps); //read from. fills up net_message
} funcs;
struct pubsubserver_s *next;
unsigned int id;
char name[64];
int activeplayers;
int transferingplayers;
netadr_t addrv4;
netadr_t addrv6;
char printtext[4096]; //to split it into lines.
qboolean started;
#ifdef HAVE_CLIENT
console_t *console;
#endif
} pubsubserver_t;
extern qboolean isClusterSlave;
void SSV_UpdateAddresses(void);
void SSV_InitiatePlayerTransfer(client_t *cl, const char *newserver);
void SSV_InstructMaster(sizebuf_t *cmd);
@ -1242,9 +1220,13 @@ void SSV_ReadFromControlServer(void);
void SSV_SavePlayerStats(client_t *cl, int reason); //initial, periodic (in case of node crashes), part
void SSV_RequestShutdown(void); //asks the cluster to not send us new players
pubsubserver_t *Sys_ForkServer(void);
vfsfile_t *Sys_ForkServer(void);
void Sys_InstructMaster(sizebuf_t *cmd); //first two bytes will always be the length of the data
vfsfile_t *Sys_GetStdInOutStream(void); //obtains a bi-directional pipe for reading/writing via stdin/stdout. make sure the system code won't be using it.
qboolean MSV_NewNetworkedNode(vfsfile_t *stream, qbyte *reqstart, qbyte *buffered, size_t buffersize, const char *remoteaddr); //call to register a pipe to a newly discovered node.
void SSV_SetupControlPipe(vfsfile_t *stream); //call to register the pipe.
extern qboolean isClusterSlave;
#define SSV_IsSubServer() isClusterSlave

View File

@ -32,12 +32,41 @@
// destination receives connection from client (or times out) and sends a ccmd_saveplayer(0) to the root, root sees the server change and sends ccmd_transferedplayer to the source.
// source knows that the player is no longer present (or aborts the transfer if it was a timeout, reenabling other transfers/retries).
//to connect a new server to a remote gateway, add '-clusterhost GATEWAY:TCPPORT PASSWORD' to the new server's commandline. The gateway needs eg sv_port_tcp open (you might wish to ipfilter for added security).
#ifdef SUBSERVERS
#ifdef SQL
#include "sv_sql.h"
#endif
typedef struct pubsubserver_s
{
vfsfile_t *stream;
struct pubsubserver_s *next;
unsigned int id;
char name[64];
int activeplayers;
int transferingplayers;
netadr_t addrv4;
netadr_t addrv6;
char printtext[4096]; //to split it into lines.
qboolean started;
#ifdef HAVE_CLIENT
console_t *console;
#endif
size_t inbuffersize;
qbyte inbuffer[8192];
qboolean outfailed;
// size_t outbuffersize;
// qbyte outbuffer[8192];
} pubsubserver_t;
extern cvar_t sv_serverip;
void VARGS SV_RejectMessage(enum serverprotocols_e protocol, char *format, ...);
@ -61,8 +90,51 @@ typedef struct {
static pubsubserver_t *subservers;
static link_t clusterplayers;
qboolean isClusterSlave;
static vfsfile_t *controlconnection = NULL;
static unsigned int nextserverid;
static void MSV_WriteSlave(pubsubserver_t *ps, sizebuf_t *cmd)
{
//FIXME: this is blocking. this is bad if the target is also blocking while trying to write to us.
//FIXME: merge buffering logic with SSV_InstructMaster, and allow for failure if full
vfsfile_t *s = ps->stream;
int wrote;
if (ps->outfailed)
return; //give up after the first failure, to avoid corruption.
cmd->data[0] = cmd->cursize & 0xff;
cmd->data[1] = (cmd->cursize>>8) & 0xff;
wrote = VFS_WRITE(s, cmd->data, cmd->cursize);
if (wrote != cmd->cursize)
ps->outfailed = true;
}
static int MSV_SubServerRead(pubsubserver_t *ps)
{
if (ps->inbuffersize < sizeof(ps->inbuffer))
{
int avail = VFS_READ(ps->stream, ps->inbuffer+ps->inbuffersize, sizeof(ps->inbuffer)-ps->inbuffersize);
if (avail < 0)
return avail;
ps->inbuffersize += avail;
}
if(ps->inbuffersize >= 2)
{
unsigned short len = ps->inbuffer[0] | (ps->inbuffer[1]<<8);
if (ps->inbuffersize >= len && len>=2)
{
memcpy(net_message.data, ps->inbuffer+2, len-2);
net_message.cursize = len-2;
memmove(ps->inbuffer, ps->inbuffer+len, ps->inbuffersize - len);
ps->inbuffersize -= len;
MSG_BeginReading (msg_nullnetprim);
return len;
}
}
return 0;
}
static clusterplayer_t *MSV_FindPlayerId(unsigned int playerid)
{
link_t *l;
@ -116,6 +188,8 @@ static void MSV_ServerCrashed(pubsubserver_t *server)
}
}
if (server->stream)
VFS_CLOSE(server->stream);
Z_Free(server);
}
@ -131,29 +205,6 @@ pubsubserver_t *MSV_FindSubServer(unsigned int id)
return NULL;
}
static vfsfile_t *msv_loop_to_ss;
static vfsfile_t *msv_loop_from_ss;
static void MSV_Loop_Instruct(pubsubserver_t *ps, sizebuf_t *cmd)
{
unsigned short size = cmd->cursize;
cmd->data[0] = cmd->cursize & 0xff;
cmd->data[1] = (cmd->cursize>>8) & 0xff;
VFS_WRITE(msv_loop_to_ss, cmd->data, size);
}
static int MSV_Loop_Read(pubsubserver_t *ps)
{
unsigned short size;
if (sv.state < ss_loading)
return -1; //failure
if (!VFS_READ(msv_loop_from_ss, &size, sizeof(size)))
return 0;
net_message.cursize = size-2;
VFS_READ(msv_loop_from_ss, net_message.data, net_message.cursize);
MSG_BeginReading (msg_nullnetprim);
return 1;
}
static void MSV_SendCvars(pubsubserver_t *s)
{
extern cvar_t skill, sv_nqplayerphysics, sv_pure, sv_minpitch, sv_maxpitch;
@ -179,18 +230,21 @@ static void MSV_SendCvars(pubsubserver_t *s)
MSG_WriteByte(&send, ccmd_setcvar);
MSG_WriteString(&send, cvars[v]->name);
MSG_WriteString(&send, cvars[v]->string);
s->funcs.InstructSlave(s, &send);
MSV_WriteSlave(s, &send);
}
}
static void MSV_Link_Server(pubsubserver_t *s, int id, const char *mapname)
static pubsubserver_t *MSV_Link_Server(vfsfile_t *stream, int id, const char *mapname)
{
pubsubserver_t *s;
sizebuf_t send;
char send_buf[1024];
if (!id)
{
do id = ++nextserverid; while(MSV_FindSubServer(id));
}
s = Z_Malloc(sizeof(*s));
s->stream = stream;
s->id = id;
s->next = subservers;
subservers = s;
@ -208,12 +262,96 @@ static void MSV_Link_Server(pubsubserver_t *s, int id, const char *mapname)
MSG_WriteByte(&send, ccmd_acceptserver);
MSG_WriteLong(&send, s->id);
MSG_WriteString(&send, s->name);
s->funcs.InstructSlave(s, &send);
MSV_WriteSlave(s, &send);
}
return s;
}
pubsubserver_t *MSV_Loop_GetLocalServer(void)
//network code just found a new node trying to announce itself to us.
qboolean MSV_NewNetworkedNode(vfsfile_t *stream, qbyte *reqstart, qbyte *buffered, size_t buffersize, const char *remoteaddr)
{
if (stream)
{
const char *pwd = NULL;
qbyte *line, *colon;
while (reqstart < buffered)
{
colon = NULL;
for (line = reqstart; line < buffered && *line; line++)
{
if (*line == ':')
{
line++;
colon = line;
break;
}
if (*line == '\n')
break;
}
for (; line < buffered && *line; line++)
{
if (*line == '\n')
break;
}
*line++ = 0;
if (colon)
{
if (colon-reqstart == 9 && !strncmp(reqstart, "Password:", 9))
pwd = colon;
}
reqstart = line;
}
if (sv.state == ss_clustermode) //only allow remote node additions if we're actually using this stuff.
{
extern cvar_t rcon_password;
COM_ParseOut(pwd, com_token, sizeof(com_token));
if (*rcon_password.string && !strcmp(com_token, rcon_password.string))
{
pubsubserver_t *s = MSV_Link_Server(stream, 0, "");
if (s)
{ //and make sure we don't drop any data that was sent after the header.
memcpy(s->inbuffer, buffered, buffersize);
s->inbuffersize = buffersize;
Con_Printf("Server node at %s connected\n", remoteaddr);
return true;
}
}
else
Con_Printf("Server node at %s rejected - bad password\n", remoteaddr);
}
VFS_CLOSE(stream);
}
return false;
}
//our pipes are one-way (one side writes, the other side reads).
static vfsfile_t *msv_loop_to_ss;
static vfsfile_t *msv_loop_from_ss;
//but the master needs two-way pipes like tcp (each side can read the other's writes).
static int QDECL MSV_Loop_Read (struct vfsfile_s *file, void *buffer, int bytestoread)
{
return VFS_READ(msv_loop_to_ss, buffer, bytestoread);
}
static int QDECL MSV_Loop_Write (struct vfsfile_s *file, const void *buffer, int bytestowrite)
{
return VFS_WRITE(msv_loop_to_ss, buffer, bytestowrite);
}
static qboolean QDECL MSV_Loop_Close (struct vfsfile_s *file)
{
Z_Free(file);
return true;
}
static pubsubserver_t *MSV_Loop_GetLocalServer(void)
{
vfsfile_t *f;
pubsubserver_t *s = MSV_FindSubServer(svs.clusterserverid);
if (s)
return s;
@ -223,27 +361,38 @@ pubsubserver_t *MSV_Loop_GetLocalServer(void)
msv_loop_to_ss = VFSPIPE_Open(1, false);
msv_loop_from_ss = VFSPIPE_Open(1, false);
s = Z_Malloc(sizeof(*s));
s->funcs.InstructSlave = MSV_Loop_Instruct;
s->funcs.SubServerRead = MSV_Loop_Read;
f = Z_Malloc(sizeof(*f));
f->ReadBytes = MSV_Loop_Read;
f->WriteBytes = MSV_Loop_Write;
f->Close = MSV_Loop_Close;
MSV_Link_Server(s, 0, "");
s = MSV_Link_Server(f, 0, "");
Q_strncpyz(s->name, sv.mapname, sizeof(s->name));
svs.clusterserverid = s->id;
return s;
}
pubsubserver_t *MSV_StartSubServer(unsigned int id, const char *mapname)
//called at startup to let us know the control connection to read/write
void SSV_SetupControlPipe(vfsfile_t *f)
{
pubsubserver_t *s = Sys_ForkServer();
if (!isDedicated)
Sys_Error("Subserver in non-dedicated server?");
if (controlconnection)
VFS_CLOSE(controlconnection);
controlconnection = f;
isClusterSlave = !!f;
}
static pubsubserver_t *MSV_StartSubServer(unsigned int id, const char *mapname)
{
vfsfile_t *s = Sys_ForkServer();
if (s)
MSV_Link_Server(s, id, mapname);
return s;
return MSV_Link_Server(s, id, mapname);
return NULL;
}
//server names documented at the start of this file
pubsubserver_t *MSV_FindSubServerName(const char *servername)
static pubsubserver_t *MSV_FindSubServerName(const char *servername)
{
pubsubserver_t *s;
unsigned int id;
@ -304,7 +453,7 @@ qboolean MSV_InstructSlave(unsigned int id, sizebuf_t *cmd)
if (!id)
{
for (s = subservers; s; s = s->next)
s->funcs.InstructSlave(s, cmd);
MSV_WriteSlave(s, cmd);
return subservers?true:false;
}
else
@ -312,7 +461,7 @@ qboolean MSV_InstructSlave(unsigned int id, sizebuf_t *cmd)
s = MSV_FindSubServer(id);
if (s)
{
s->funcs.InstructSlave(s, cmd);
MSV_WriteSlave(s, cmd);
return true;
}
}
@ -378,6 +527,10 @@ void SSV_PrintToMaster(char *s)
{
sizebuf_t send;
char send_buf[8192];
static qboolean norecurse;
if (norecurse)
return;
memset(&send, 0, sizeof(send));
send.data = send_buf;
send.maxsize = sizeof(send_buf);
@ -385,7 +538,9 @@ void SSV_PrintToMaster(char *s)
MSG_WriteByte(&send, ccmd_print);
MSG_WriteString(&send, s);
norecurse = true;
SSV_InstructMaster(&send);
norecurse = false;
}
void MSV_Status(void)
@ -422,6 +577,9 @@ static int MSV_SubConsole_LineBuffered(console_t *con, const char *utf8line)
Con_PrintCon(con, va("]%s\n", utf8line), PFS_FORCEUTF8|PFS_NONOTIFY);
if (*utf8line == '/')
utf8line++; //command, not text.
if (!strcmp(utf8line, "clear"))
{
Con_ClearCon(con);
@ -436,7 +594,7 @@ static int MSV_SubConsole_LineBuffered(console_t *con, const char *utf8line)
MSG_WriteString(&buf, utf8line); //FIXME: is utf-8 a problem?
buf.data[0] = buf.cursize & 0xff;
buf.data[1] = (buf.cursize>>8) & 0xff;
s->funcs.InstructSlave(s, &buf);
MSV_WriteSlave(s, &buf);
}
else
Con_Footerf(con, false, "< Unable to send >");
@ -602,7 +760,7 @@ qboolean MSV_ForwardToAutoServer(void)
MSG_WriteString(&buf, args);
buf.data[0] = buf.cursize & 0xff;
buf.data[1] = (buf.cursize>>8) & 0xff;
s->funcs.InstructSlave(s, &buf);
MSV_WriteSlave(s, &buf);
return true;
}
}
@ -702,7 +860,7 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
MSG_WriteByte(&send, ccmd_transferedplayer);
MSG_WriteLong(&send, s->id);
MSG_WriteLong(&send, plid);
pl->server->funcs.InstructSlave(pl->server, &send);
MSV_WriteSlave(pl->server, &send);
pl->server->activeplayers--;
}
pl->server = s;
@ -771,7 +929,7 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
MSG_WriteByte(&send, statsblobsize/4);
SZ_Write(&send, statsblob, statsblobsize&~3);
s->funcs.InstructSlave(s, &send);
MSV_WriteSlave(s, &send);
}
break;
@ -808,7 +966,7 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
while(c--)
MSG_WriteFloat(&send, MSG_ReadFloat());
toptr->funcs.InstructSlave(toptr, &send);
MSV_WriteSlave(toptr, &send);
s->transferingplayers--;
toptr->transferingplayers++;
@ -827,7 +985,7 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
MSG_WriteLong(&send, plid);
MSG_WriteString(&send, "");
s->funcs.InstructSlave(s, &send);
MSV_WriteSlave(s, &send);
}
}
break;
@ -868,7 +1026,7 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
toptr = MSV_FindSubServer(to);
if (toptr)
{
toptr->funcs.InstructSlave(toptr, &send);
MSV_WriteSlave(toptr, &send);
toptr->transferingplayers++;
}
}
@ -937,21 +1095,21 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
if (!*dest) //broadcast if no dest
{
for (s = subservers; s; s = s->next)
s->funcs.InstructSlave(s, &send);
MSV_WriteSlave(s, &send);
}
else if (*dest == '\\')
{
//send to a specific server (backslashes should not be valid in infostrings, and thus not in names.
//FIXME: broadcasting for now.
for (s = subservers; s; s = s->next)
s->funcs.InstructSlave(s, &send);
MSV_WriteSlave(s, &send);
}
else
{
//send it to the server that the player is currently on.
clusterplayer_t *pl = MSV_FindPlayerName(dest);
if (pl)
pl->server->funcs.InstructSlave(pl->server, &send);
MSV_WriteSlave(pl->server, &send);
else if (!pl && strncmp(cmd, "error:", 6))
{
//player not found. send it back to the sender, but add an error prefix.
@ -962,7 +1120,7 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
SZ_Write(&send, "error:", 6);
MSG_WriteString(&send, cmd);
MSG_WriteString(&send, info);
s->funcs.InstructSlave(s, &send);
MSV_WriteSlave(s, &send);
}
}
}
@ -976,7 +1134,57 @@ void MSV_PollSlaves(void)
{
pubsubserver_t **link, *s;
if (msv_loop_to_ss)
if (controlconnection)
{
static unsigned inbuffersize;
static qbyte inbuffer[8192];
qboolean error = false;
for (;;)
{
if (inbuffersize < 2)
{
int r = VFS_READ(controlconnection, inbuffer+inbuffersize, 2-inbuffersize);
if (r < 0)
error = true;
else
inbuffersize += r;
}
if (inbuffersize >= 2)
{
size_t size = inbuffer[0] | ((unsigned short)inbuffer[1]<<8);
int r;
if (size > sizeof(inbuffer) || size >= sizeof(net_message_buffer))
break; //error...
if (size > inbuffersize)
{
r = VFS_READ(controlconnection, inbuffer+inbuffersize, size-inbuffersize);
if (r < 0)
error = true;
else
inbuffersize += r;
}
if (inbuffersize < size)
break; //not complete yet.
net_message.cursize = size-2;
memcpy(net_message.data, inbuffer+2, net_message.cursize);
memmove(inbuffer, inbuffer+size, inbuffersize-size);
inbuffersize -= size;
MSG_BeginReading (msg_nullnetprim);
SSV_ReadFromControlServer();
}
else
break;
}
if (error)
{
SSV_SetupControlPipe(NULL);
inbuffersize = 0;
}
}
else if (msv_loop_to_ss)
{
unsigned short size;
while (VFS_READ(msv_loop_to_ss, &size, sizeof(size))>0)
@ -990,7 +1198,7 @@ void MSV_PollSlaves(void)
for (link = &subservers; (s=*link); )
{
switch(s->funcs.SubServerRead(s))
switch(MSV_SubServerRead(s))
{
case -1:
//error - server is dead and needs to be freed.
@ -1001,7 +1209,7 @@ void MSV_PollSlaves(void)
//no messages
link = &s->next;
break;
case 1:
default:
//got a message. read it and see if there's more.
MSV_ReadFromSubServer(s);
break;
@ -1015,8 +1223,10 @@ void SSV_InstructMaster(sizebuf_t *cmd)
cmd->data[1] = (cmd->cursize>>8) & 0xff;
if (msv_loop_from_ss)
VFS_WRITE(msv_loop_from_ss, cmd->data, cmd->cursize);
else
Sys_InstructMaster(cmd);
else if (controlconnection)
VFS_WRITE(controlconnection, cmd->data, cmd->cursize);
//FIXME: handle partial writes.
}
void SSV_ReadFromControlServer(void)
@ -1044,7 +1254,7 @@ void SSV_ReadFromControlServer(void)
{
cvar_t *var = Cvar_FindVar(MSG_ReadString());
const char *val = MSG_ReadString();
Con_Printf("Setting cvar \"%s\" to \"%s\"\n", var?var->name:"UNKNOWN", val);
Con_DPrintf("Setting cvar \"%s\" to \"%s\"\n", var?var->name:"UNKNOWN", val);
Cvar_Set(var, val);
}
break;
@ -1470,7 +1680,7 @@ qboolean MSV_ClusterLoginReply(netadr_t *legacyclientredirect, unsigned int serv
MSG_WriteByte(&send, statsblobsize/4);
SZ_Write(&send, statsblob, statsblobsize&~3);
s->funcs.InstructSlave(s, &send);
MSV_WriteSlave(s, &send);
if (serveraddr.type == NA_INVALID)
{

View File

@ -2228,7 +2228,7 @@ void SV_MVD_QTVReverse_f (void)
if (sv.state<ss_loading)
return;
f = FS_OpenTCP(ip, 27599);
f = FS_OpenTCP(ip, 27599, false);
if (!f)
return;

View File

@ -2523,8 +2523,13 @@ qboolean SV_Physics (void)
int maxtics;
double trueframetime = host_frametime;
double maxtic = sv_maxtic.value;
if (maxtic < sv_mintic.value)
maxtic = sv_mintic.value;
double mintic = sv_mintic.value;
extern cvar_t sv_nqplayerphysics;
if (sv_nqplayerphysics.ival)
if (mintic < 0.013)
mintic = 0.013; //NQ physics can't cope with low rates and just generally bugs out.
if (maxtic < mintic)
maxtic = mintic;
//keep gravity tracking the cvar properly
movevars.gravity = sv_gravity.value;
@ -2653,7 +2658,7 @@ qboolean SV_Physics (void)
sv.world.physicstime = sv.time;
break;
}
if (host_frametime <= 0 || host_frametime < sv_mintic.value)
if (host_frametime <= 0 || host_frametime < mintic)
break;
if (host_frametime > maxtic)
{

View File

@ -3609,16 +3609,28 @@ void SV_SendClientMessages (void)
}
else
{
extern cvar_t sv_nqplayerphysics;
if (c->nextservertimeupdate > pt + 0.1)
c->nextservertimeupdate = 0;
c->netchan.nqunreliableonly = false;
c->send_message = false;
//nq sends one packet only for each server physics frame
if (c->nextservertimeupdate < pt && c->state >= cs_connected)
if (sv_mintic.value || sv_nqplayerphysics.ival) //(nqplayerphysics forces 72hz when mintic )
{ //explicit packet/tick rate. don't spam faster/slower, clients don't like that too much.
if (c->nextservertimeupdate != pt && c->state >= cs_connected)
{
c->send_message = true;
c->nextservertimeupdate = pt;
}
}
else
{
c->send_message = true;
c->nextservertimeupdate = pt + 1.0/77;
if (c->nextservertimeupdate < pt && c->state >= cs_connected)
{
c->send_message = true;
c->nextservertimeupdate = pt + 1.0/77;
}
}
}
}

View File

@ -601,14 +601,6 @@ char *Sys_ConsoleInput (void)
static char text[256];
int len;
#ifdef SUBSERVERS
if (SSV_IsSubServer())
{
SSV_CheckFromMaster();
return NULL;
}
#endif
if (!stdin_ready || noconinput==true)
return NULL; // the select didn't say it was ready
stdin_ready = false;

View File

@ -695,14 +695,14 @@ void SVNQ_New_f (void)
//which isn't all that useful. so lets customise it to advertise properly, as well as provide gamedir and map (file)name info
if (protext2 & PEXT2_REPLACEMENTDELTAS)
{
Q_snprintfz (message, sizeof(message), "%c\n%s - "DISTRIBUTION" (FTENQ, %s) - %s", 2, gamedir,
build, mapname);
Q_snprintfz (message, sizeof(message), "%c\n"DISTRIBUTION" %s - %s - %s", 2,
build,gamedir, mapname);
}
else
{
Q_snprintfz (message, sizeof(message), "%c\n%s - "DISTRIBUTION" (%s%s%s, %s) - %s", 2, gamedir,
Q_snprintfz (message, sizeof(message), "%c\n"DISTRIBUTION" (%s%s%s, %s) - %s - %s", 2,
protoname,(protext1||(protext2&~PEXT2_VOICECHAT))?"+":"",(protext2&PEXT2_VOICECHAT)?"Voip":"",
build, mapname);
build,gamedir, mapname);
}
MSG_WriteByte (&host_client->netchan.message, svc_print);
MSG_WriteString (&host_client->netchan.message,message);

View File

@ -5,6 +5,7 @@
!!samps =REFLECT reflect=1
!!samps =RIPPLEMAP ripplemap=2
!!samps =DEPTH refractdepth=3
!!permu FOG
#include "sys/defs.h"
@ -87,9 +88,7 @@ void main (void)
}
#endif
#ifdef FRAGMENT_SHADER
#ifdef ALPHA
#include "sys/fog.h"
#endif
void main (void)
@ -178,6 +177,8 @@ void main (void)
vec4 ts = texture2D(s_diffuse, ntc);
vec4 surf = fog4blend(vec4(ts.rgb, float(ALPHA)*ts.a));
refr = mix(refr, surf.rgb, surf.a);
#else
refr = fog3(refr);
#endif
//done

View File

@ -1,5 +1,6 @@
!!permu FOG
!!samps 2
!!samps base=0, cloud=1
!!cvardf r_skyfog=0.5
#include "sys/fog.h"
//regular sky shader for scrolling q1 skies
@ -24,9 +25,14 @@ void main ()
dir.z *= 3.0;
dir.xy /= 0.5*length(dir);
tccoord = (dir.xy + e_time*0.03125);
vec3 solid = vec3(texture2D(s_t0, tccoord));
vec3 sky = vec3(texture2D(s_base, tccoord));
tccoord = (dir.xy + e_time*0.0625);
vec4 clouds = texture2D(s_t1, tccoord);
gl_FragColor = vec4(fog3((solid.rgb*(1.0-clouds.a)) + (clouds.a*clouds.rgb)), 1.0);
vec4 clouds = texture2D(s_cloud, tccoord);
sky = (sky.rgb*(1.0-clouds.a)) + (clouds.a*clouds.rgb);
#ifdef FOG
sky.rgb = mix(sky.rgb, w_fogcolour, float(r_skyfog)*w_fogalpha); //flat fog ignoring actual geometry
//sky = fog3(sky); //fog according to actual geometry
#endif
gl_FragColor = vec4(sky, 1.0);
}
#endif

View File

@ -35,6 +35,13 @@ void main ()
void main ()
{
vec4 skybox = textureCube(s_reflectcube, pos);
gl_FragColor = vec4(mix(skybox.rgb, fog3(skybox.rgb), float(r_skyfog)), 1.0);
//Fun question: should sky be fogged as if infinite, or as if an actual surface?
#if 1
skybox.rgb = mix(skybox.rgb, w_fogcolour_ float(r_skyfog)*w_fogalpha); //flat fog ignoring actual geometry
#else
skybox.rgb = mix(skybox.rgb, fog3(skybox.rgb), float(r_skyfog)); //fog in terms of actual geometry distance
#endif
gl_FragColor = skybox;
}
#endif

View File

@ -1,7 +1,7 @@
!!ver 100 450
!!permu TESS
!!permu DELUXE
!!permu FULLBRIGHT
!!permu FULLBRIGHT //lumas rather than no lightmaps
!!permu FOG
!!permu LIGHTSTYLED
!!permu BUMP
@ -18,7 +18,7 @@
//diffuse gives us alpha, and prevents dlight from bugging out when there's no diffuse.
!!samps =EIGHTBIT paletted 1
!!samps =SPECULAR specular
!!samps lightmap
!!samps !VERTEXLIT lightmap
!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3
!!samps =DELUXE deluxemap
!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3
@ -269,7 +269,7 @@ void main ()
//optional: round the lightmap coords to ensure all pixels within a texel have different lighting values either. it just looks wrong otherwise.
//don't bother if its lightstyled, such cases will have unpredictable correlations anyway.
//FIXME: this rounding is likely not correct with respect to software rendering. oh well.
#if __VERSION__ >= 130
#if __VERSION__ >= 130 && !defined(VERTEXLIT)
vec2 lmsize = vec2(textureSize(s_lightmap0, 0));
#else
#define lmsize vec2(128.0,2048.0)

View File

@ -1,6 +1,5 @@
!!ver 100 450
!!permu FOG
!!cvarf r_wateralpha
!!samps diffuse lightmap
#include "sys/defs.h"
@ -9,6 +8,7 @@
//this is expected to be moderately fast.
#include "sys/fog.h"
varying vec2 tc;
#ifdef LIT
varying vec2 lm0;
@ -27,12 +27,6 @@ void main ()
}
#endif
#ifdef FRAGMENT_SHADER
#ifndef ALPHA
uniform float cvar_r_wateralpha;
#define USEALPHA cvar_r_wateralpha
#else
#define USEALPHA float(ALPHA)
#endif
void main ()
{
vec2 ntc;
@ -44,6 +38,10 @@ void main ()
ts *= (texture2D(s_lightmap, lm0) * e_lmscale).rgb;
#endif
gl_FragColor = fog4blend(vec4(ts, USEALPHA) * e_colourident);
#ifdef ALPHA
gl_FragColor = fog4blend(vec4(ts, float(ALPHA)) * e_colourident);
#else
gl_FragColor = fog4(vec4(ts, 1.0) * e_colourident);
#endif
}
#endif

View File

@ -1461,6 +1461,39 @@ vk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t lay
case PTI_ASTC_12X12_HDR: format = VK_FORMAT_ASTC_12x12_UNORM_BLOCK; break;
#endif
#ifdef ASTC3D
case PTI_ASTC_3X3X3_HDR: //vulkan doesn't support these for some reason
case PTI_ASTC_4X3X3_HDR:
case PTI_ASTC_4X4X3_HDR:
case PTI_ASTC_4X4X4_HDR:
case PTI_ASTC_5X4X4_HDR:
case PTI_ASTC_5X5X4_HDR:
case PTI_ASTC_5X5X5_HDR:
case PTI_ASTC_6X5X5_HDR:
case PTI_ASTC_6X6X5_HDR:
case PTI_ASTC_6X6X6_HDR:
case PTI_ASTC_3X3X3_LDR:
case PTI_ASTC_4X3X3_LDR:
case PTI_ASTC_4X4X3_LDR:
case PTI_ASTC_4X4X4_LDR:
case PTI_ASTC_5X4X4_LDR:
case PTI_ASTC_5X5X4_LDR:
case PTI_ASTC_5X5X5_LDR:
case PTI_ASTC_6X5X5_LDR:
case PTI_ASTC_6X6X5_LDR:
case PTI_ASTC_6X6X6_LDR:
case PTI_ASTC_3X3X3_SRGB:
case PTI_ASTC_4X3X3_SRGB:
case PTI_ASTC_4X4X3_SRGB:
case PTI_ASTC_4X4X4_SRGB:
case PTI_ASTC_5X4X4_SRGB:
case PTI_ASTC_5X5X4_SRGB:
case PTI_ASTC_5X5X5_SRGB:
case PTI_ASTC_6X5X5_SRGB:
case PTI_ASTC_6X6X5_SRGB:
case PTI_ASTC_6X6X6_SRGB: break;
#endif
//depth formats
case PTI_DEPTH16: format = VK_FORMAT_D16_UNORM; break;
case PTI_DEPTH24: format = VK_FORMAT_X8_D24_UNORM_PACK32; break;
@ -1797,7 +1830,7 @@ qboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips)
VkCommandBuffer vkloadcmd;
vk_image_t target;
uint32_t i;
uint32_t blockwidth, blockheight;
uint32_t blockwidth, blockheight, blockdepth;
uint32_t blockbytes;
uint32_t layers;
uint32_t mipcount = mips->mipcount;
@ -1835,7 +1868,7 @@ qboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips)
}
}
Image_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight);
Image_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);
fence = VK_FencedBegin(VK_TextureLoaded, sizeof(*fence));
fence->mips = mipcount;
@ -1925,7 +1958,7 @@ 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;
uint32_t blocksdepth = (mips->mip[i].depth+1-1) / 1;
uint32_t blocksdepth = (mips->mip[i].depth+blockdepth-1) / blockdepth;
bci.size += blockswidth*blocksheight*blocksdepth*blockbytes;
}
bci.flags = 0;
@ -1961,7 +1994,7 @@ 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;
uint32_t blocksdepth = (mips->mip[i].depth+blockdepth-1) / blockdepth;
if (mips->mip[i].data)
memcpy((char*)mapdata + bci.size, (char*)mips->mip[i].data, blockswidth*blockbytes*blocksheight*blocksdepth);

View File

@ -530,7 +530,7 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna
qbyte *fdata;
size_t fsize;
int bb,bw,bh;
int bb,bw,bh,bd;
qboolean canktx = false;
uploadfmt_t targfmt = args->newpixelformat;
int d,l, layers, r;
@ -605,10 +605,15 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna
tmp.extrafree = NULL;
tmp.mipcount = 1;
Image_BlockSizeForEncoding(targfmt, &bb, &bw, &bh);
Image_BlockSizeForEncoding(targfmt, &bb, &bw, &bh, &bd);
Q_snprintfz(command+strlen(command), sizeof(command)-strlen(command), " \"%s\" \"%s\"", raw, comp);
if (targfmt >= PTI_ASTC_FIRST && targfmt <= PTI_ASTC_LAST)
Q_snprintfz(command+strlen(command), sizeof(command)-strlen(command), " %ix%i -exhaustive", bw, bh);
{
if (bd!=1)
Q_snprintfz(command+strlen(command), sizeof(command)-strlen(command), " %ix%ix%i -exhaustive", bw, bh, bd);
else
Q_snprintfz(command+strlen(command), sizeof(command)-strlen(command), " %ix%i -exhaustive", bw, bh);
}
if (targfmt >= PTI_ASTC_4X4_SRGB && targfmt <= PTI_ASTC_12X12_SRGB)
Q_strncatz(command, " -srgb", sizeof(command));
if (targfmt >= PTI_ASTC_4X4_HDR && targfmt <= PTI_ASTC_12X12_HDR)
@ -641,7 +646,7 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna
// Con_Printf("%s: Compressing %u mips\n", inname, mips->mipcount);
Image_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh);
Image_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh, &bd);
for (m = 0; m < mips->mipcount; m++)
{
qbyte *srcdata = mips->mip[m].data;
@ -731,8 +736,8 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna
if (mips->mipcount && targfmt >= PTI_BC1_RGB && targfmt <= PTI_BC7_RGBA_SRGB)
{ //d3d has some annoying limitations.
//do not warn for astc files, their block sizes are too weird.
Image_BlockSizeForEncoding(targfmt, &bb, &bw, &bh);
if (mips->mip[0].width%bw || mips->mip[0].height%bh)
Image_BlockSizeForEncoding(targfmt, &bb, &bw, &bh, &bd);
if (mips->mip[0].width%bw || mips->mip[0].height%bh || mips->mip[0].depth%bd)
Con_Printf("%s: mip0 of %i*%i is not a multiple of %i*%i (d3d warning)\n", inname, mips->mip[0].width, mips->mip[0].height, bw, bh);
}
@ -903,11 +908,11 @@ static struct pendingtextureinfo *ImgTool_Combine(struct opts_s *args, const cha
{
if (facetype[i] < 0)
{ //flip to match legacy skyboxes
unsigned bb,bw,bh;
unsigned bb,bw,bh,bd;
srcs[i] = tmpsrcs[-facetype[i]-1];
t = srcs[i].in;
Image_BlockSizeForEncoding(t->encoding, &bb,&bw,&bh);
if (bw == 1 && bh == 1)
Image_BlockSizeForEncoding(t->encoding, &bb,&bw,&bh,&bd);
if (bw == 1 && bh == 1 && bd == 1)
{
for (j = 0; j < t->mipcount; j++)
{
@ -1071,7 +1076,7 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in,
#endif
else
{
int bb,bw,bh;
int bb,bw,bh,bd;
if (in->type != PTI_2D)
Con_Printf("%s: Unable to write %s file to 2d image format\n", outname, imagetypename[in->type]);
@ -1093,7 +1098,7 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in,
0;
if (!outformats[in->encoding])
Image_ChangeFormat(in, outformats, PTI_INVALID, outname);
Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh);
Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh,&bd);
if (!Image_WritePNG(outname, FS_SYSTEM, 0, &in->mip[0].data, 1, in->mip[0].width*bb, in->mip[0].width, in->mip[0].height, in->encoding, false))
#endif
Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding));
@ -1115,7 +1120,7 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in,
0;
if (!outformats[in->encoding])
Image_ChangeFormat(in, outformats, PTI_INVALID, outname);
Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh);
Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh,&bd);
if (!WriteTGA(outname, FS_SYSTEM, in->mip[0].data, in->mip[0].width*bb, in->mip[0].width, in->mip[0].height, in->encoding))
Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding));
}
@ -1133,7 +1138,7 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in,
0;
if (!outformats[in->encoding])
Image_ChangeFormat(in, outformats, PTI_INVALID, outname);
Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh);
Image_BlockSizeForEncoding(in->encoding, &bb, &bw,&bh,&bd);
if (!WritePCXfile(outname, FS_SYSTEM, in->mip[0].data, in->mip[0].width, in->mip[0].height, in->mip[0].width*bb, host_basepal, false))
Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding));
}
@ -1166,7 +1171,7 @@ static struct pendingtextureinfo *ImgTool_DecodeMiptex(struct opts_s *args, mipt
struct pendingtextureinfo *out = Z_Malloc(sizeof(*out));
qbyte *newdata = NULL;
int neww=0, newh=0, sz;
unsigned int bw,bh,bb;
unsigned int bw,bh,bb,bd;
out->type = PTI_2D;
out->encoding = PTI_INVALID;
@ -1220,7 +1225,7 @@ static struct pendingtextureinfo *ImgTool_DecodeMiptex(struct opts_s *args, mipt
if (out->encoding != PTI_INVALID) //use the first format we support, allowing prioritisation.
continue;
Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh);
Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh, &bd);
w = data[ 8] | (data[ 9]<<8) | (data[10]<<16) | (data[11]<<24);
h = data[12] | (data[13]<<8) | (data[14]<<16) | (data[15]<<24);
for (csz = 16; w || h; w>>=1, h>>=1)
@ -1243,7 +1248,7 @@ static struct pendingtextureinfo *ImgTool_DecodeMiptex(struct opts_s *args, mipt
//only use our if there were no corrupt sections.
if (data == dataend && newdata && neww && newh)
{
Image_BlockSizeForEncoding(out->encoding, &bb, &bw, &bh);
Image_BlockSizeForEncoding(out->encoding, &bb, &bw, &bh, &bd);
for (out->mipcount = 0; out->mipcount < countof(out->mip) && neww && newh; out->mipcount++, neww>>=1, newh>>=1)
{
neww = max(1, neww);
@ -1254,6 +1259,7 @@ static struct pendingtextureinfo *ImgTool_DecodeMiptex(struct opts_s *args, mipt
out->mip[out->mipcount].datasize = bb;
out->mip[out->mipcount].datasize *= (out->mip[out->mipcount].width + bw-1)/bw;
out->mip[out->mipcount].datasize *= (out->mip[out->mipcount].height + bh-1)/bh;
out->mip[out->mipcount].datasize *= (out->mip[out->mipcount].depth + bd-1)/bd;
out->mip[out->mipcount].data = newdata;
newdata += out->mip[out->mipcount].datasize;
}
@ -1795,8 +1801,8 @@ static qboolean ImgTool_MipExport(struct opts_s *args, vfsfile_t *outfile, struc
if (args->width && args->height && in->mipcount >= 1)
{
qbyte *newimg;
unsigned int bb, bw, bh;
Image_BlockSizeForEncoding(in->encoding, &bb, &bw, &bh);
unsigned int bb, bw, bh, bd;
Image_BlockSizeForEncoding(in->encoding, &bb, &bw, &bh, &bd);
newimg = Image_ResampleTexture(in->encoding, in->mip[0].data, in->mip[0].width, in->mip[0].height, NULL, args->width, args->height);
if (newimg)
{
@ -1807,7 +1813,8 @@ static qboolean ImgTool_MipExport(struct opts_s *args, vfsfile_t *outfile, struc
in->mip[0].needfree = true;
in->mip[0].width = args->width;
in->mip[0].height = args->height;
in->mip[0].datasize = bb*((in->mip[0].width+bw-1)/bw)*((in->mip[0].height+bh-1)/bh);
in->mip[0].depth = 1;
in->mip[0].datasize = bb*((in->mip[0].width+bw-1)/bw)*((in->mip[0].height+bh-1)/bh)*((in->mip[0].depth+bd-1)/bd);
Image_GenerateMips(in, args->flags);
}
@ -2172,18 +2179,18 @@ showhelp:
Con_Printf("Supported compressed/interesting pixelformats are:\n");
for (f = 0; f < PTI_MAX; f++)
{
int bb,bw,bh;
Image_BlockSizeForEncoding(f, &bb,&bw,&bh);
int bb,bw,bh,bd;
Image_BlockSizeForEncoding(f, &bb,&bw,&bh,&bd);
if (f >= PTI_ASTC_FIRST && f <= PTI_ASTC_LAST)
{
if (f >= PTI_ASTC_4X4_SRGB)
continue;
Con_Printf(" --%-16s %5.3g-bpp (requires astcenc)\n", Image_FormatName(f), 8*(float)bb/(bw*bh));
Con_Printf(" --%-16s %5.3g-bpp (requires astcenc)\n", Image_FormatName(f), 8*(float)bb/(bw*bh*bd));
}
else if (f==PTI_BC1_RGB||f==PTI_BC1_RGBA||f==PTI_BC2_RGBA||f==PTI_BC3_RGBA||f==PTI_BC4_R||f==PTI_BC5_RG)
Con_Printf(" --%-16s %5.3g-bpp (requires nvcompress)\n", Image_FormatName(f), 8*(float)bb/(bw*bh));
Con_Printf(" --%-16s %5.3g-bpp (requires nvcompress)\n", Image_FormatName(f), 8*(float)bb/(bw*bh*bd));
else if (f==PTI_BC6_RGB_UFLOAT || f==PTI_BC6_RGB_SFLOAT || f==PTI_BC7_RGBA)
Con_Printf(" --%-16s %5.3g-bpp (requires nvcompress 2.1+)\n", Image_FormatName(f), 8*(float)bb/(bw*bh));
Con_Printf(" --%-16s %5.3g-bpp (requires nvcompress 2.1+)\n", Image_FormatName(f), 8*(float)bb/(bw*bh*bd));
else if ( f==PTI_RGBA16F ||
f==PTI_RGBA32F ||
f==PTI_E5BGR9 ||
@ -2202,9 +2209,9 @@ showhelp:
f==PTI_L8 ||
f==PTI_L8A8 ||
0)
Con_Printf(" --%-16s %5.3g-bpp\n", Image_FormatName(f), 8*(float)bb/(bw*bh));
Con_Printf(" --%-16s %5.3g-bpp\n", Image_FormatName(f), 8*(float)bb/(bw*bh*bd));
// else
// Con_DPrintf(" --%-16s %5.3g-bpp (unsupported)\n", Image_FormatName(f), 8*(float)bb/(bw*bh));
// Con_DPrintf(" --%-16s %5.3g-bpp (unsupported)\n", Image_FormatName(f), 8*(float)bb/(bw*bh*bd));
}
return EXIT_SUCCESS;
}

View File

@ -28,13 +28,13 @@ struct avaudioctx
//output audio
//we throw away data if the format changes. which is awkward, but gah.
int64_t samples_start;
int64_t samples_framestart;
int samples_channels;
int samples_speed;
int samples_width;
qaudiofmt_t samples_format;
qbyte *samples_buffer;
size_t samples_count;
size_t samples_max;
size_t samples_framecount;
size_t samples_maxbytes;
};
static void S_AV_Purge(sfx_t *s)
@ -61,127 +61,213 @@ static void S_AV_Purge(sfx_t *s)
activedecoders--;
memset(&s->decoder, 0, sizeof(s->decoder));
}
#define QAF_U8 0x81
#define QAF_S32 0x04
#ifndef MIXER_F32
#define QAF_F32 0x84
#endif
#define QAF_F64 0x88
static void S_AV_ReadFrame(struct avaudioctx *ctx)
{ //reads an audioframe and spits its data into the output sound file for the game engine to use.
int width = 2;
qaudiofmt_t outformat = QAF_S16, informat=QAF_S16;
int channels = ctx->pACodecCtx->channels;
int planes = 1, p;
unsigned int auddatasize = av_samples_get_buffer_size(NULL, ctx->pACodecCtx->channels, ctx->pAFrame->nb_samples, ctx->pACodecCtx->sample_fmt, 1);
void *auddata = ctx->pAFrame->data[0];
switch(ctx->pACodecCtx->sample_fmt)
{ //we don't support planar audio. we just treat it as mono instead.
default:
auddatasize = 0;
break;
case AV_SAMPLE_FMT_U8P:
auddatasize /= channels;
channels = 1;
planes = channels;
outformat = QAF_S8;
informat = QAF_U8;
break;
case AV_SAMPLE_FMT_U8:
width = 1;
planes = 1;
outformat = QAF_S8;
informat = QAF_U8;
break;
case AV_SAMPLE_FMT_S16P:
auddatasize /= channels;
channels = 1;
planes = channels;
outformat = QAF_S16;
informat = QAF_S16;
break;
case AV_SAMPLE_FMT_S16:
width = 2;
planes = 1;
outformat = QAF_S16;
informat = QAF_S16;
break;
case AV_SAMPLE_FMT_S32P:
planes = channels;
outformat = QAF_S16;
informat = QAF_S32;
break;
case AV_SAMPLE_FMT_S32:
planes = 1;
outformat = QAF_S16;
informat = QAF_S32;
break;
#ifdef MIXER_F32
case AV_SAMPLE_FMT_FLTP:
//FIXME: support float audio internally.
{
float *in[2] = {(float*)ctx->pAFrame->data[0],(float*)ctx->pAFrame->data[1]};
signed short *out = (void*)auddata;
int v;
unsigned int i, c;
unsigned int frames = ctx->pAFrame->nb_samples;
if (channels > 2)
channels = 2;
for (i = 0; i < frames; i++)
{
for (c = 0; c < channels; c++)
{
v = (short)(in[c][i]*32767);
if (v < -32767)
v = -32767;
else if (v > 32767)
v = 32767;
*out++ = v;
}
}
width = sizeof(*out);
auddatasize = frames*width*channels;
}
planes = channels;
outformat = QAF_F32;
informat = QAF_F32;
break;
case AV_SAMPLE_FMT_FLT:
//FIXME: support float audio internally.
{
float *in = (void*)auddata;
signed short *out = (void*)auddata;
int v;
unsigned int i;
for (i = 0; i < auddatasize/sizeof(*in); i++)
{
v = (short)(in[i]*32767);
if (v < -32767)
v = -32767;
else if (v > 32767)
v = 32767;
out[i] = v;
}
auddatasize/=2;
width = 2;
}
planes = 1;
outformat = QAF_F32;
informat = QAF_F32;
break;
case AV_SAMPLE_FMT_DBLP:
auddatasize /= channels;
channels = 1;
case AV_SAMPLE_FMT_DBL:
{
double *in = (double*)auddata;
signed short *out = (void*)auddata;
int v;
unsigned int i;
for (i = 0; i < auddatasize/sizeof(*in); i++)
{
v = (short)(in[i]*32767);
if (v < -32767)
v = -32767;
else if (v > 32767)
v = 32767;
out[i] = v;
}
auddatasize/=4;
width = 2;
}
planes = channels;
outformat = QAF_F32;
informat = QAF_F64;
break;
case AV_SAMPLE_FMT_DBL:
planes = 1;
outformat = QAF_F32;
informat = QAF_F64;
break;
#else
case AV_SAMPLE_FMT_FLTP:
planes = channels;
outformat = QAF_S16;
informat = QAF_F32;
break;
case AV_SAMPLE_FMT_FLT:
planes = 1;
outformat = QAF_S16;
informat = QAF_F32;
break;
case AV_SAMPLE_FMT_DBLP:
planes = channels;
outformat = QAF_S16;
informat = QAF_F64;
break;
case AV_SAMPLE_FMT_DBL:
planes = 1;
outformat = QAF_S16;
informat = QAF_F64;
break;
#endif
}
if (ctx->samples_channels != channels || ctx->samples_speed != ctx->pACodecCtx->sample_rate || ctx->samples_width != width)
if (ctx->samples_channels != channels || ctx->samples_speed != ctx->pACodecCtx->sample_rate || ctx->samples_format != outformat)
{ //something changed, update
ctx->samples_channels = channels;
ctx->samples_speed = ctx->pACodecCtx->sample_rate;
ctx->samples_width = width;
ctx->samples_format = outformat;
//and discard any decoded audio. this might loose some.
ctx->samples_start += ctx->samples_count;
ctx->samples_count = 0;
ctx->samples_framestart += ctx->samples_framecount;
ctx->samples_framecount = 0;
}
if (ctx->samples_max < (ctx->samples_count*ctx->samples_width*ctx->samples_channels)+auddatasize)
if (ctx->samples_maxbytes < (ctx->samples_framecount*QAF_BYTES(ctx->samples_format)*ctx->samples_channels)+auddatasize)
{
ctx->samples_max = (ctx->samples_count*ctx->samples_width*ctx->samples_channels)+auddatasize;
ctx->samples_max *= 2; //slop
ctx->samples_buffer = realloc(ctx->samples_buffer, ctx->samples_max);
}
if (width == 1)
{ //FTE uses signed 8bit audio. ffmpeg uses unsigned 8bit audio. *sigh*.
char *out = (char*)(ctx->samples_buffer + ctx->samples_count*(ctx->samples_width*ctx->samples_channels));
unsigned char *in = auddata;
int i;
for (i = 0; i < auddatasize; i++)
out[i] = in[i]-128;
ctx->samples_maxbytes = (ctx->samples_framecount*QAF_BYTES(ctx->samples_format)*ctx->samples_channels)+auddatasize;
ctx->samples_maxbytes *= 2; //slop
ctx->samples_buffer = realloc(ctx->samples_buffer, ctx->samples_maxbytes);
}
if (planes==1 && outformat != QAF_S8 && informat==outformat)
memcpy(ctx->samples_buffer + ctx->samples_framecount*(QAF_BYTES(ctx->samples_format)*ctx->samples_channels), ctx->pAFrame->data[0], auddatasize);
else
memcpy(ctx->samples_buffer + ctx->samples_count*(ctx->samples_width*ctx->samples_channels), auddata, auddatasize);
ctx->samples_count += auddatasize/(ctx->samples_width*ctx->samples_channels);
{
void *fte_restrict outv = (ctx->samples_buffer + ctx->samples_framecount*(QAF_BYTES(ctx->samples_format)*ctx->samples_channels));
size_t i, samples = auddatasize / (planes*QAF_BYTES(informat));
if (outformat == QAF_S8 && informat == QAF_U8)
{
char *out = outv;
for (p = 0; p < planes; p++, out++)
{
unsigned char *in = ctx->pAFrame->data[p];
for (i = 0; i < samples; i++)
out[i*planes] = in[i]-128; //convert from u8 to s8.
}
}
else if (outformat == QAF_S16 && informat == QAF_S16)
{
signed short *out = outv;
for (p = 0; p < planes; p++, out++)
{
signed short *in = (signed short *)ctx->pAFrame->data[p];
for (i = 0; i < samples; i++)
out[i*planes] = in[i]; //no conversion needed
}
}
else if (outformat == QAF_S16 && informat == QAF_S32)
{
signed short *out = outv;
for (p = 0; p < planes; p++, out++)
{
signed int *in = (signed int *)ctx->pAFrame->data[p];
for (i = 0; i < samples; i++)
out[i*planes] = in[i]>>16; //just use the MSBs, no clamping needed.
}
}
#ifdef MIXER_F32
else if (outformat == QAF_F32 && informat == QAF_F32)
{
float *out = outv;
for (p = 0; p < planes; p++, out++)
{
float *in = (float *)ctx->pAFrame->data[p];
for (i = 0; i < samples; i++)
out[i*planes] = in[i]; //no conversion needed.
}
}
else if (outformat == QAF_F32 && informat == QAF_F64)
{
float *out = outv;
for (p = 0; p < planes; p++, out++)
{
double *in = (double *)ctx->pAFrame->data[p];
for (i = 0; i < samples; i++)
out[i*planes] = in[i]; //no clamping needed.
}
}
#else
else if (outformat == QAF_S16 && informat == QAF_F32)
{
signed short *out = outv;
for (p = 0; p < planes; p++, out++)
{
float *in = (float *)ctx->pAFrame->data[p];
for (i = 0; i < samples; i++)
{
int v = in[i] * 32767;
if (v < -32768)
v = -32768;
if (v > 32767)
v = 32767;
out[i*planes] = v;
}
}
}
else if (outformat == QAF_S16 && informat == QAF_F64)
{
signed short *out = outv;
for (p = 0; p < planes; p++, out++)
{
double *in = (double *)ctx->pAFrame->data[p];
for (i = 0; i < samples; i++)
{
int v = in[i] * 32767;
if (v < -32768)
v = -32768;
if (v > 32767)
v = 32767;
out[i*planes] = v;
}
}
}
#endif
}
ctx->samples_framecount += auddatasize/(QAF_BYTES(informat)*ctx->samples_channels);
}
static sfxcache_t *S_AV_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int length)
{ //warning: can be called on a different thread.
@ -196,10 +282,10 @@ static sfxcache_t *S_AV_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start,
while (1)
{
if (start < ctx->samples_start)
if (start < ctx->samples_framestart)
break; //o.O rewind!
if (ctx->samples_start+ctx->samples_count > curtime)
if (ctx->samples_framestart+ctx->samples_framecount > curtime)
break; //no need yet.
#ifdef HAVE_DECOUPLED_API
@ -242,11 +328,11 @@ static sfxcache_t *S_AV_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start,
av_packet_unref(&packet);
}
buf->length = ctx->samples_count;
buf->length = ctx->samples_framecount;
buf->speed = ctx->samples_speed;
buf->width = ctx->samples_width;
buf->format = ctx->samples_format;
buf->numchannels = ctx->samples_channels;
buf->soundoffset = ctx->samples_start;
buf->soundoffset = ctx->samples_framestart;
buf->data = ctx->samples_buffer;
//if we couldn't return any new data, then we're at an eof, return NULL to signal that.
@ -267,7 +353,7 @@ static float S_AV_Query(struct sfx_s *sfx, struct sfxcache_s *buf, char *title,
buf->length = 0;
buf->numchannels = ctx->samples_channels;
buf->speed = ctx->samples_speed;
buf->width = ctx->samples_width;
buf->format = ctx->samples_format;
}
return ctx->pFormatCtx->duration / (float)AV_TIME_BASE;
}

View File

@ -232,7 +232,7 @@ void Mod_ExportIQM(char *fname, int flags, galiasinfo_t *mesh)
{ //pos3, quat4, scale3
float posquatscale[10]; //raw values, used to calibrate ranges
} *posedata = NULL, *pd; //per bone*joint
avec4_t *ivert;
vecV_t *ivert;
vec2_t *ist;
vec3_t *overt;
vec3_t *onorm = NULL;

View File

@ -16,7 +16,10 @@ vector mousefar;
float releasedmouse;
//#define WALLBROWSERS
#ifdef WALLBROWSERS
string pointedshadername;
#endif
vector pointedsurfacenormal;
entity pointedent;
float pointedsurface;
@ -73,7 +76,7 @@ void(vector mouse) editor_options_overlay =
else
txt = strcat(txt, " (OFF)");
}
drawstring(pos, txt, '8 8', (sel==i)?'0 0 1':'1 1 1', 1, 0);
drawstring(pos, txt, '8 8', (sel==i)?'0 0 1' :'1 1 1', 1, 0);
pos_y += 8;
}
};
@ -382,6 +385,7 @@ float (float event, float parama, float paramb) wrap_InputEvent =
return TRUE;
}
}
#ifdef WALLBROWSERS
else if (pointedshadername != "")
{
/* if (parama == K_MOUSE2)
@ -399,6 +403,7 @@ float (float event, float parama, float paramb) wrap_InputEvent =
return TRUE;
}
}
#endif
if (orig_input_event)
return orig_input_event(event, parama, paramb);
@ -484,6 +489,7 @@ void() CSQC_Input_Frame =
if (autocvar_ca_show) //when we're using the UI, don't send any attack or jump stuff. these are generally annoying modifiers or so
input_buttons = 0;
#ifdef WALLBROWSERS
if ((input_buttons & 1) && pointedshadername == "")
{
t = mousefar;
@ -584,6 +590,7 @@ void() CSQC_Input_Frame =
// pointparticles(particleeffectnum("te_spike"), xyz1 + dir1*f1 + dir2*f2, trace_plane_normal, 1);
}
}
#endif
};
/*