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 NAMES ossaudio
) )
IF(OSSAUDIO_LIBRARY) IF(OSSAUDIO_LIBRARY)
SET(FTE_LIBS ${FTE_LIBS} ${OSSAUDIO_LIBRARY}) SET(FTE_LIBS ${FTE_LIBS} ${OSSAUDIO_LIBRARY})
ENDIF() ENDIF()
#on linux, use wayland. #on linux, use wayland.
@ -307,13 +307,21 @@ ELSEIF(${UNIX}) #linux(ish)
WAYLAND_CLIENT_LIBRARY WAYLAND_CLIENT_LIBRARY
NAMES wayland-client libwayland-client 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) IF(WAYLAND_CLIENT_LIBRARY)
SET(FTE_DEFINES ${FTE_DEFINES};WAYLANDQUAKE;USE_EGL) SET(FTE_DEFINES ${FTE_DEFINES};WAYLANDQUAKE;USE_EGL)
SET(FTE_ARCH_FILES ${FTE_ARCH_FILES} SET(FTE_ARCH_FILES ${FTE_ARCH_FILES}
engine/gl/gl_vidwayland.c engine/gl/gl_vidwayland.c
) )
ELSE() ELSE()
MESSAGE(WARNING "Wayland library NOT available") MESSAGE(WARNING "Wayland library NOT available. X11 will live forever anyway.")
IF(NOT X11_FOUND) IF(NOT X11_FOUND)
MESSAGE(WARNING "No renderers supported!") MESSAGE(WARNING "No renderers supported!")
SET(FTE_NO_RENDERERS 1) SET(FTE_NO_RENDERERS 1)

View File

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

View File

@ -3828,7 +3828,7 @@ void CL_TransitionEntities (void)
frac = (servertime-packold->servertime)/(packnew->servertime-packold->servertime); frac = (servertime-packold->servertime)/(packnew->servertime-packold->servertime);
// if (!cl.paused) // 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); CL_TransitionPacketEntities(newff, packnew, packold, frac, servertime);

View File

@ -894,7 +894,7 @@ void CL_CheckForResend (void)
connectinfo.subprotocol = CPNQ_DP7; connectinfo.subprotocol = CPNQ_DP7;
} }
else if (!strcmp(lbp, "qss") || 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.protocol = CP_NETQUAKE;
connectinfo.subprotocol = CPNQ_FITZ666; connectinfo.subprotocol = CPNQ_FITZ666;
@ -5545,6 +5545,7 @@ done:
man->updateurl = Z_StrDup(f->fname); man->updateurl = Z_StrDup(f->fname);
// if (f->flags & HRF_DOWNLOADED) // if (f->flags & HRF_DOWNLOADED)
man->blockupdate = true; man->blockupdate = true;
//man->security = MANIFEST_SECURITY_DEFAULT;
BZ_Free(fdata); BZ_Free(fdata);
FS_ChangeGame(man, true, true); FS_ChangeGame(man, true, true);
} }
@ -6386,18 +6387,18 @@ void CL_StartCinematicOrMenu(void)
if (!sv_state && !cls.demoinfile && !cls.state && !*cls.servername) 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 #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(); M_Menu_Mods_f();
#endif #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; cls.demonum = 0;
CL_NextDemo(); 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 we're (now) meant to be using csqc for menus, make sure that its running.
if (!CSQC_UnconnectedInit()) if (!CSQC_UnconnectedInit())
M_ToggleMenu_f(); 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) if (cls.demoplayback == DPB_MVD || cls.demoplayback == DPB_EZTV)
{ {
/* extern int cls_lastto; extern int cls_lastto;
cl.players[cls_lastto].statsstr[stat]=value; //Z_Free(cl.players[cls_lastto].statsstr[stat]);
//cl.players[cls_lastto].statsstr[stat]=Z_StrDup(value);
for (pnum = 0; pnum < cl.splitclients; pnum++) for (pnum = 0; pnum < cl.splitclients; pnum++)
if (spec_track[pnum] == cls_lastto) if (cl.playerview[pnum].cam_spec_track == cls_lastto && cl.playerview[pnum].cam_state != CAM_FREECAM)
cl.statsstr[pnum][stat] = value;*/ {
if (cl.playerview[pnum].statsstr[stat])
Z_Free(cl.playerview[pnum].statsstr[stat]);
cl.playerview[pnum].statsstr[stat] = Z_StrDup(value);
}
} }
else else
{ {
if (cl.playerview[pnum].statsstr[stat]) if (cl.playerview[pnum].statsstr[stat])
Z_Free(cl.playerview[pnum].statsstr[stat]); Z_Free(cl.playerview[pnum].statsstr[stat]);
cl.playerview[pnum].statsstr[stat] = Z_Malloc(strlen(value)+1); cl.playerview[pnum].statsstr[stat] = Z_StrDup(value);
strcpy(cl.playerview[pnum].statsstr[stat], value);
} }
} }
/* /*

View File

@ -621,11 +621,12 @@ void CL_CalcClientTime(void)
extern float olddemotime; extern float olddemotime;
cl.servertime = olddemotime; cl.servertime = olddemotime;
} }
//q2 has no drifting. //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. //q3 always drifts (gamecode does snapshot selection).
//nq+qw code can drift //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!) //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 { //no drift logic
float f; float f;
f = cl.gametime - cl.oldgametime; f = cl.gametime - cl.oldgametime;
@ -671,6 +672,8 @@ void CL_CalcClientTime(void)
else else
{ {
cl.servertime -= 0.02*(max - cl.servertime); cl.servertime -= 0.02*(max - cl.servertime);
if (cl.servertime < cl.time)
cl.servertime = cl.time;
} }
} }
if (cl.servertime < min) 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; int remaining;
shader_t *pic; shader_t *pic;
int ch; int ch;
int mousex,mousey;
conchar_t *line_start[MAX_CPRINT_LINES]; conchar_t *line_start[MAX_CPRINT_LINES];
conchar_t *line_end[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, y, &left, &top);
Font_BeginString(font, rect->x+rect->width, rect->y+rect->height, &right, &bottom); 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); 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 else
x = left + (right - left - Font_LineWidth(line_start[l], line_end[l]))/2; 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]; remaining -= line_end[l]-line_start[l];
@ -2869,7 +2871,7 @@ void SCR_ScreenShot_Cubemap_f(void)
{{-90, 0, 0}, "_up"} {{-90, 0, 0}, "_up"}
}; };
const char *ext; const char *ext;
unsigned int bb, bw, bh; unsigned int bb, bw, bh, bd;
if (!cls.state || !cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED) if (!cls.state || !cl.worldmodel || cl.worldmodel->loadstate != MLS_LOADED)
{ {
@ -2917,8 +2919,8 @@ void SCR_ScreenShot_Cubemap_f(void)
break; break;
if (!bb) if (!bb)
{ {
Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh); Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh, &bd);
if (!bb || bw != 1 || bh != 1 || fbwidth != fbheight) if (!bb || bw != 1 || bh != 1 || bd != 1 || fbwidth != fbheight)
{ //erk, no block compression here... { //erk, no block compression here...
BZ_Free(facedata); BZ_Free(facedata);
break; //zomgwtfbbq break; //zomgwtfbbq
@ -2999,7 +3001,7 @@ void SCR_ScreenShot_Cubemap_f(void)
buffer = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride, &fmt, true, false); buffer = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride, &fmt, true, false);
if (buffer) if (buffer)
{ {
Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh); Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh, &bd);
if (sides[i].horizontalflip) if (sides[i].horizontalflip)
{ {
int y, x, p; int y, x, p;

View File

@ -206,6 +206,9 @@ static image_t *imagelist;
#ifdef DECOMPRESS_ASTC #ifdef DECOMPRESS_ASTC
#define ASTC_PUBLIC #define ASTC_PUBLIC
#ifdef ASTC3D
#define ASTC_WITH_3D
#endif
#include "image_astc.h" #include "image_astc.h"
#endif #endif
@ -1707,7 +1710,7 @@ int Image_WritePNG (const char *filename, enum fs_relative fsroot, int compressi
qbyte stereochunk = 0; //cross-eyed qbyte stereochunk = 0; //cross-eyed
png_unknown_chunk unknowns = {"sTER", &stereochunk, sizeof(stereochunk), PNG_HAVE_PLTE}; png_unknown_chunk unknowns = {"sTER", &stereochunk, sizeof(stereochunk), PNG_HAVE_PLTE};
int bw,bh,chanbits; int bw,bh,bd,chanbits;
qboolean havepad, bgr; qboolean havepad, bgr;
int colourtype; int colourtype;
@ -1776,7 +1779,7 @@ int Image_WritePNG (const char *filename, enum fs_relative fsroot, int compressi
default: default:
return false; return false;
} }
Image_BlockSizeForEncoding(fmt, &pxsize, &bw, &bh); Image_BlockSizeForEncoding(fmt, &pxsize, &bw, &bh, &bd);
if (!FS_NativePath(filename, fsroot, name, sizeof(name))) if (!FS_NativePath(filename, fsroot, name, sizeof(name)))
return false; return false;
@ -4311,7 +4314,7 @@ static qbyte *ReadXCFFile(const qbyte *filedata, size_t len, const char *fname,
{ {
size_t offs; size_t offs;
struct xcf_s ctx; 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]) if (len < 14 || strncmp(filedata, "gimp xcf ", 9) || filedata[13])
return NULL; return NULL;
memset(&ctx, 0, sizeof(ctx)); memset(&ctx, 0, sizeof(ctx));
@ -4361,7 +4364,7 @@ static qbyte *ReadXCFFile(const qbyte *filedata, size_t len, const char *fname,
//channels //channels
//without any layers, its fully transparent //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); ctx.flat = Z_Malloc(ctx.width*ctx.height*bb);
*format = ctx.outformat; *format = ctx.outformat;
*width = ctx.width; *width = ctx.width;
@ -4752,7 +4755,7 @@ typedef struct
} ktxheader_t; } ktxheader_t;
qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struct pendingtextureinfo *mips) 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; vfsfile_t *file;
ktxheader_t header = {{0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A}, 0x04030201/*endianness*/, 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*/, 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; return false;
} }
Image_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh); Image_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh, &bd);
switch(mips->encoding) 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_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_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; 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_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_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; 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 browbytes = bb * ((mips->mip[mipnum].width+bw-1)/bh);
unsigned int padbytes = (browbytes&3)?4-(browbytes&3):0; unsigned int padbytes = (browbytes&3)?4-(browbytes&3):0;
unsigned int brows = (mips->mip[mipnum].height+bh-1)/bh; 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) if (mips->mip[mipnum].datasize != browbytes*brows*blayers)
{ //should probably be a sys_error { //should probably be a sys_error
Con_Printf("WriteKTX mip %u missized\n", (unsigned)mipnum); 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; int encoding = TF_INVALID;
const qbyte *fileend = filedata + filesize; 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))) if (filesize < sizeof(ktxheader_t) || memcmp(filedata, magic, sizeof(magic)))
return NULL; //not a ktx file 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)) if (nummips * header.numberoffaces > countof(mips->mip))
nummips = countof(mips->mip) / header.numberoffaces; nummips = countof(mips->mip) / header.numberoffaces;
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight); Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);
w = header.pixelwidth; w = header.pixelwidth;
h = max(1, header.pixelheight); 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); browbytes = blockbytes * ((w+blockwidth-1)/blockwidth);
padbytes = (browbytes & 3)?4-(browbytes&3):0; 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) 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); 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 #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; int face;
for (face = 0; face < header.numberoffaces; 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) static struct pendingtextureinfo *Image_ReadASTCFile(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize)
{ {
struct pendingtextureinfo *mips; struct pendingtextureinfo *mips;
int encoding = PTI_INVALID, blockbytes, blockwidth, blockheight; int encoding = PTI_INVALID, blockbytes, blockwidth, blockheight, blockdepth;
static const struct { static const struct {
int w, h, d; int w, h, d;
int fmt; int fmt;
@ -5331,6 +5368,18 @@ static struct pendingtextureinfo *Image_ReadASTCFile(unsigned int flags, const c
{10,10,1,PTI_ASTC_10X10_LDR}, {10,10,1,PTI_ASTC_10X10_LDR},
{12,10,1,PTI_ASTC_12X10_LDR}, {12,10,1,PTI_ASTC_12X10_LDR},
{12,12,1,PTI_ASTC_12X12_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 i;
int size[3] = { int size[3] = {
@ -5347,8 +5396,8 @@ static struct pendingtextureinfo *Image_ReadASTCFile(unsigned int flags, const c
} }
if (!encoding) if (!encoding)
return NULL; //block size not known return NULL; //block size not known
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight); Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);
if (16+((size[0]+blockwidth-1)/blockwidth)*((size[1]+blockheight-1)/blockheight)*blockbytes != filesize) 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! return NULL; //err, not the right size!
mips = Z_Malloc(sizeof(*mips)); 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) static struct pendingtextureinfo *Image_ReadPKMFile(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize)
{ {
struct pendingtextureinfo *mips; struct pendingtextureinfo *mips;
unsigned int encoding, blockbytes, blockwidth, blockheight; unsigned int encoding, blockbytes, blockwidth, blockheight, blockdepth;
unsigned short ver, dfmt; unsigned short ver, dfmt;
unsigned short datawidth, dataheight; unsigned short datawidth, dataheight;
unsigned short imgwidth, imgheight; unsigned short imgwidth, imgheight;
@ -5424,7 +5473,7 @@ static struct pendingtextureinfo *Image_ReadPKMFile(unsigned int flags, const ch
else else
return NULL; 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) if (16+((datawidth+blockwidth-1)/blockwidth)*((dataheight+blockheight-1)/blockheight)*blockbytes != filesize)
return NULL; //err, not the right size! return NULL; //err, not the right size!
@ -5483,7 +5532,7 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch
int mipnum; int mipnum;
int datasize; int datasize;
unsigned int w, h, d; unsigned int w, h, d;
unsigned int blockwidth, blockheight, blockbytes; unsigned int blockwidth, blockheight, blockdepth, blockbytes;
struct pendingtextureinfo *mips; struct pendingtextureinfo *mips;
int encoding; int encoding;
int layers = 1, layer; int layers = 1, layer;
@ -5491,6 +5540,7 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch
ddsheader_t fmtheader; ddsheader_t fmtheader;
dds10header_t fmt10header; dds10header_t fmt10header;
qbyte *fileend = filedata + filesize;
if (filesize < sizeof(fmtheader) || *(int*)filedata != (('D'<<0)|('D'<<8)|('S'<<16)|(' '<<24))) if (filesize < sizeof(fmtheader) || *(int*)filedata != (('D'<<0)|('D'<<8)|('S'<<16)|(' '<<24)))
return NULL; 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) if ((fmtheader.ddsCaps[1] & 0x200) && (fmtheader.ddsCaps[1] & 0xfc00) != 0xfc00)
return NULL; //cubemap without all 6 faces defined. return NULL; //cubemap without all 6 faces defined.
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight); Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);
if (!blockbytes) if (!blockbytes)
return NULL; //werid/unsupported 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 (fmtheader.ddsCaps[1] & 0x200)
{ {
if (fmt10header.arraysize % 6) //weird number of faces. 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. { //can just use the data without copying.
for (mipnum = 0; mipnum < nummips; mipnum++) 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].data = filedata;
mips->mip[mipnum].datasize = datasize; mips->mip[mipnum].datasize = datasize;
@ -5829,13 +5900,19 @@ static struct pendingtextureinfo *Image_ReadDDSFile(unsigned int flags, const ch
d = max(1, d>>1); d = max(1, d>>1);
} }
mips->mipcount = mipnum; mips->mipcount = mipnum;
if (filedata > fileend)
{ //overflow... corrupt dds?
Z_Free(mips);
return NULL;
}
} }
else else
{ //we need to copy stuff in order to pack it properly. :( { //we need to copy stuff in order to pack it properly. :(
//allocate space and calc mip sizes //allocate space and calc mip sizes
for (mipnum = 0; mipnum < nummips; mipnum++) 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].data = BZ_Malloc(datasize);
mips->mip[mipnum].datasize = datasize; mips->mip[mipnum].datasize = datasize;
mips->mip[mipnum].width = w; 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++) for (mipnum = 0; mipnum < nummips; mipnum++)
{ {
datasize = mips->mip[mipnum].datasize/layers; 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); memcpy((qbyte*)mips->mip[mipnum].data+datasize*layer, filedata, datasize);
filedata += datasize; filedata += datasize;
} }
@ -5874,10 +5958,10 @@ qboolean Image_WriteDDSFile(const char *filename, enum fs_relative fsroot, struc
ddsheader_t h9={0}; ddsheader_t h9={0};
int *endian; int *endian;
unsigned int blockbytes, blockwidth, blockheight; unsigned int blockbytes, blockwidth, blockheight, blockdepth;
unsigned int arraysize; unsigned int arraysize;
Image_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight); Image_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);
h9.dwSize = sizeof(h9); h9.dwSize = sizeof(h9);
h9.ddpfPixelFormat.dwSize = sizeof(h9.ddpfPixelFormat); 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) if (blockwidth != 1 || blockheight != 1)
{ {
h9.dwFlags |= 0x80000; //LINEARSIZE 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 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_HDR: //hdr allows more endpoint modes.
case PTI_ASTC_12X12_LDR: h10.dxgiformat = 0xba/*DXGI_FORMAT_ASTC_12X12_UNORM*/; break; 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; 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_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; 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 #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) 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; int bw, bh, bd, blockbytes;
Image_BlockSizeForEncoding(fmt, &blockbytes, &bw, &bh); Image_BlockSizeForEncoding(fmt, &blockbytes, &bw, &bh, &bd);
ASTC_Decode_LDR8(in, out->v, stride, bw, bh); ASTC_Decode_LDR8(in, out->v, stride, 0/*w*h*/, bw, bh, bd);
} }
#endif #endif
#ifdef ASTC_WITH_HDR #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) 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; int bw, bh, bd, blockbytes;
Image_BlockSizeForEncoding(fmt, &blockbytes, &bw, &bh); Image_BlockSizeForEncoding(fmt, &blockbytes, &bw, &bh, &bd);
ASTC_Decode_HDR(in, out->v, stride, bw, bh); 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) /*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); 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) switch(encoding)
{ {
case PTI_RGB565: 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_SRGB:
case PTI_ASTC_12X12_LDR: w = 12; h = 12; b = 16; break; 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: case PTI_EMULATED:
#ifdef FTE_TARGET_WEB #ifdef FTE_TARGET_WEB
case PTI_WHOLEFILE: //UNKNOWN! case PTI_WHOLEFILE: //UNKNOWN!
@ -10524,6 +10676,7 @@ void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes,
*blockbytes = b; *blockbytes = b;
*blockwidth = w; *blockwidth = w;
*blockheight = h; *blockheight = h;
*blockdepth = d;
} }
qboolean Image_FormatHasAlpha(uploadfmt_t encoding) 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_HDR:
case PTI_ASTC_12X12_SRGB: case PTI_ASTC_12X12_SRGB:
case PTI_ASTC_12X12_LDR: 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; return true;
case PTI_EMULATED: 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_HDR: return "ASTC_12X12_HDR";
case PTI_ASTC_12X12_SRGB: return "ASTC_12X12_SRGB"; case PTI_ASTC_12X12_SRGB: return "ASTC_12X12_SRGB";
case PTI_ASTC_12X12_LDR: return "ASTC_12X12_LDR"; 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 #ifdef FTE_TARGET_WEB
case PTI_WHOLEFILE: return "Whole File"; 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 sizediff;
int rows, columns, layers; int rows, columns, layers;
unsigned int blockbytes, blockwidth, blockheight; unsigned int blockbytes, blockwidth, blockheight, blockdepth;
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight); 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); 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; 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 sizediff;
int rows, columns, layers; int rows, columns, layers;
unsigned int blockbytes, blockwidth, blockheight; unsigned int blockbytes, blockwidth, blockheight, blockdepth;
Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight); 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); 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) 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)); 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; rows *= blockheight;
columns = w/blockwidth; columns = w/blockwidth;
columns *= blockwidth; columns *= blockwidth;
layers = d; layers = d/blockdepth;
for (z = 0; z < layers; z++) layers *= blockdepth;
for (z = 0; z < layers; z+=blockdepth)
{ {
for (y = 0; y < rows; y+=blockheight, out += w*(blockheight-1)) 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. { //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. //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 //we need to manually decompress in order to correctly handle such images
int blockbytes, blockwidth, blockheight; int blockbytes, blockwidth, blockheight, blockdepth;
Image_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight); Image_BlockSizeForEncoding(mips->encoding, &blockbytes, &blockwidth, &blockheight, &blockdepth);
if (!(mips->mip[0].width % blockwidth) && !(mips->mip[0].height % blockheight)) if (!(mips->mip[0].width % blockwidth) && !(mips->mip[0].height % blockheight) && !(mips->mip[0].depth % blockdepth))
return; return;
//else encoding isn't supported for this size. fall through. //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; unsigned int *rgbadata = rawdata;
int i; int i;
qboolean valid; qboolean valid;
unsigned int bb, bw, bh; unsigned int bb, bw, bh, bd;
mips->mip[0].width = imgwidth; mips->mip[0].width = imgwidth;
mips->mip[0].height = imgheight; 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 if (mips->mip[0].width == imgwidth && mips->mip[0].height == imgheight) //make sure its okay
{ {
size_t sz = 0; 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) for (i = 0; i < countof(mips->mip) && (imgwidth || imgheight); i++, imgwidth>>=1, imgheight>>=1)
{ {
mips->mip[i].width = max(1,imgwidth); mips->mip[i].width = max(1,imgwidth);
mips->mip[i].height = max(1,imgheight); mips->mip[i].height = max(1,imgheight);
mips->mip[i].depth = 1; 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; mips->mip[i].needfree = false;
sz += mips->mip[i].datasize; 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_LDR:
case PTI_ASTC_12X12_SRGB: case PTI_ASTC_12X12_SRGB:
case PTI_ASTC_12X12_HDR: 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 #ifdef FTE_TARGET_WEB
case PTI_WHOLEFILE: case PTI_WHOLEFILE:
#endif #endif
@ -12238,8 +12488,8 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
} }
else else
mips->mip[0].data = NULL; mips->mip[0].data = NULL;
Image_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh); 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) * bb; 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) if (mips->type == PTI_3D)
{ {
@ -12247,7 +12497,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
mips->mip[0].data = NULL; mips->mip[0].data = NULL;
/*our 2d input image is interlaced as y0z0,y0z1,y1z0,y1z1 /*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)*/ 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 d, r;
int size = mips->mip[0].height; 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))) if ((data = ReadRawImageFile(buf, filesize, &width, &height, &format, true, fname)))
{ {
extern cvar_t vid_hardwaregamma; extern cvar_t vid_hardwaregamma;
int bb,bw,bh; int bb,bw,bh, bd;
Image_BlockSizeForEncoding(format, &bb, &bw, &bh); Image_BlockSizeForEncoding(format, &bb, &bw, &bh, &bd);
if (needsflipping && (bw!=1 || bh!=1)) if (needsflipping && (bw!=1 || bh!=1 || bd!=1))
/*can't do it*/; /*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) 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) { //(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; break;
default: default:
{ {
unsigned int bb, bw, bh; unsigned int bb, bw, bh, bd;
unsigned int lev; 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++) 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; break;
} }
@ -13727,9 +13977,9 @@ void Image_List_f(void)
if (tex->status == TEX_LOADED) if (tex->status == TEX_LOADED)
{ {
char *type; char *type;
unsigned int blockbytes, blockwidth, blockheight; unsigned int blockbytes, blockwidth, blockheight, blockdepth;
Image_BlockSizeForEncoding(tex->format, &blockbytes, &blockwidth, &blockheight); Image_BlockSizeForEncoding(tex->format, &blockbytes, &blockwidth, &blockheight, &blockdepth);
imgmem = blockbytes * (tex->width+blockwidth-1)/blockwidth * (tex->height+blockheight-1)/blockheight * tex->depth; imgmem = blockbytes * (tex->width+blockwidth-1)/blockwidth * (tex->height+blockheight-1)/blockheight * (tex->depth+blockdepth-1)/blockdepth;
switch((tex->flags & IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT) switch((tex->flags & IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT)
{ {
case PTI_2D: type = ""; break; case PTI_2D: type = ""; break;
@ -13777,7 +14027,7 @@ void Image_Formats_f(void)
{ {
size_t i; size_t i;
float bpp; float bpp;
int blockbytes, blockwidth, blockheight; int blockbytes, blockwidth, blockheight, blockdepth;
#ifdef GLQUAKE #ifdef GLQUAKE
if (qrenderer == QR_OPENGL) 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( " 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( " 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( " 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++) for (i = 0; i < PTI_MAX; i++)
{ {
switch(i) switch(i)
@ -13831,9 +14083,9 @@ void Image_Formats_f(void)
default: default:
break; break;
} }
Image_BlockSizeForEncoding(i, &blockbytes, &blockwidth, &blockheight); Image_BlockSizeForEncoding(i, &blockbytes, &blockwidth, &blockheight, &blockdepth);
bpp = blockbytes*8.0/(blockwidth*blockheight); bpp = blockbytes*8.0/(blockwidth*blockheight*blockdepth);
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); 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_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_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 #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 #endif
#ifdef ASTC_WITH_HDR #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 #endif
#ifdef ASTC_WITH_HDRTEST #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 #endif
@ -38,21 +39,24 @@
#if defined(ASTC_WITH_LDR) || defined(ASTC_WITH_HDR) #if defined(ASTC_WITH_LDR) || defined(ASTC_WITH_HDR)
#define ASTC_WITH_DECODE #define ASTC_WITH_DECODE
#endif #endif
enum enum astc_status_e
{ {
ASTC_OKAY, //valid blocks
ASTC_ERROR, //validation errors ASTC_OKAY, //we can decode at least part of this normally (hdr endpoints may still result in per-endpoint 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.
ASTC_VOID_LDR, //not an error - the block is a single LDR colour, with an RGBA16 colour in the last 8 bytes. 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 struct astc_block_info
{ {
unsigned char *in; //the 16 bytes of the block 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 dualplane; //two sets of weights instead of one.
unsigned char ccs; //second set applies to this component 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 unsigned short partindex; //used for deciding which partition each pixel belongs in
struct astc_part struct astc_part
{ {
char mode; //endpoint modes unsigned char mode; //endpoint modes
#ifdef ASTC_WITH_HDR #ifdef ASTC_WITH_HDR
char hdr; //endpoint colour mode - &1=rgb, &2=alpha unsigned char hdr; //endpoint colour mode - &1=rgb, &2=alpha
#endif #endif
int ep[2][4]; int ep[2][4];
} part[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>>(9-3))&(1<<3);//P
b->precision |= (s>>4)&1; //p0 b->precision |= (s>>4)&1; //p0
if (b->blocksize[2] != 1) if (b->blocksize[2] != 1)
{ //3d blocks have a different layout { //3d blocks have a different header layout
b->status = ASTC_UNSUPPORTED_FULL; #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 else
{ {
@ -1314,16 +1352,22 @@ static int ASTC_ChoosePartition(int seed, int x, int y, int z, int partitions, i
#ifdef ASTC_WITH_LDR #ifdef ASTC_WITH_LDR
//Spits out 8-bit RGBA data for a single block. Any HDR blocks will result in the error colour. //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. //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; struct astc_block_info b;
int x, y; int x, y;
int stride = pixstride*4; 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.in = in;
b.blocksize[0] = bw; b.blocksize[0] = bw;
b.blocksize[1] = bh; b.blocksize[1] = bh;
b.blocksize[2] = 1; b.blocksize[2] = bd;
ASTC_ReadBlockMode(&b); ASTC_ReadBlockMode(&b);
if (b.status == ASTC_VOID_LDR) 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) 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 smallblock = (b.blocksize[0]*b.blocksize[1]*b.blocksize[2])<31;
int ds = (1024+b.blocksize[0]/2)/(b.blocksize[0]-1); int fs, s, ds = (1024+b.blocksize[0]/2)/(b.blocksize[0]-1);
int dt = (1024+b.blocksize[1]/2)/(b.blocksize[1]-1); int ft, t, dt = (1024+b.blocksize[1]/2)/(b.blocksize[1]-1);
int planes = 1<<b.dualplane, wstride = b.wcount[0]*planes; #ifdef ASTC_WITH_3D
int s, t, v0, w, w00,w01,w10,w11; 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; struct astc_part *p;
//int dr = (1024+b.bd/2)/(b.bd-1);
#ifdef ASTC_WITH_HDR #ifdef ASTC_WITH_HDR
for (x = 0; x < b.partitions; x++) 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 #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) for (y = 0; y < bh; y++, out += stride)
{ {
t = ((dt*y)*(b.wcount[1]-1)+32)>>6; t = ((dt*y)*(b.wcount[1]-1)+32)>>6;
ft=t&0xf;
for (x = 0; x < bw; x++) for (x = 0; x < bw; x++)
{ {
p = &b.part[ASTC_ChoosePartition(b.partindex, x,y,0, b.partitions, smallblock)]; p = &b.part[ASTC_ChoosePartition(b.partindex, x,y,0, b.partitions, smallblock)];
s = ((ds*x)*(b.wcount[0]-1)+32)>>6; s = ((ds*x)*(b.wcount[0]-1)+32)>>6;
w11 = ((s&0xf)*(t&0xf)+8) >> 4; fs=s&0xf;
w10 = (t&0xf) - w11; #ifdef ASTC_WITH_3D
w01 = (s&0xf) - w11; if (bd != 1)
w00 = 16 - (s&0xf) - (t&0xf) + w11; { //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] + w = ( w00*b.weights[v0] +
w01*b.weights[v0+planes] + w01*b.weights[v0+s1] +
w10*b.weights[v0+wstride] + w10*b.weights[v0+s2] +
w11*b.weights[v0+planes+wstride] + 8) >> 4; 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)+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)+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; 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 { //dual planes has a second set of weights that override a single channel
v0++; v0++;
w = ( w00*b.weights[v0] + w = ( w00*b.weights[v0] +
w01*b.weights[v0+planes] + w01*b.weights[v0+s1] +
w10*b.weights[v0+wstride] + w10*b.weights[v0+s2] +
w11*b.weights[v0+planes+wstride] + 8) >> 4; 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; 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 else
{ { //error colour == magenta
for (y = 0; y < bh; y++, out += stride) #ifdef ASTC_WITH_3D
for (x = 0; x < bw; x++) for (z = 0; z < bd; z++, out += layerstride)
{ #endif
out[(x<<2)+0] = 0xff; for (y = 0; y < bh; y++, out += stride)
out[(x<<2)+1] = 0; for (x = 0; x < bw; x++)
out[(x<<2)+2] = 0xff; {
out[(x<<2)+3] = 0xff; out[(x<<2)+0] = 0xff;
} out[(x<<2)+1] = 0;
out[(x<<2)+2] = 0xff;
out[(x<<2)+3] = 0xff;
}
} }
} }
#endif #endif
@ -1457,15 +1557,22 @@ static unsigned short ASTC_GenHalffloat(int hdr, int rawval)
} }
//Spits out half-float RGBA data for a single block. //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 x, y;
int stride = pixstride*4; int stride = pixstride*4;
struct astc_block_info b; 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.in = in;
b.blocksize[0] = bw; b.blocksize[0] = bw;
b.blocksize[1] = bh; b.blocksize[1] = bh;
b.blocksize[2] = 1; b.blocksize[2] = bd;
ASTC_ReadBlockMode(&b); 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) 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 smallblock = (b.blocksize[0]*b.blocksize[1]*b.blocksize[2])<31;
int ds = (1024+b.blocksize[0]/2)/(b.blocksize[0]-1); int fs, s, ds = (1024+b.blocksize[0]/2)/(b.blocksize[0]-1);
int dt = (1024+b.blocksize[1]/2)/(b.blocksize[1]-1); int ft, t, dt = (1024+b.blocksize[1]/2)/(b.blocksize[1]-1);
int planes = 1<<b.dualplane, wstride = b.wcount[0]*planes; #ifdef ASTC_WITH_3D
int s, t, v0, w, w00,w01,w10,w11; 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; struct astc_part *p;
//int dr = (1024+b.bd/2)/(b.bd-1);
for (x = 0; x < b.partitions; x++) for (x = 0; x < b.partitions; x++)
{ //we need to do a little extra processing here { //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) for (y = 0; y < bh; y++, out += stride)
{ {
t = ((dt*y)*(b.wcount[1]-1)+32)>>6; t = ((dt*y)*(b.wcount[1]-1)+32)>>6;
ft=s&0xf;
for (x = 0; x < bw; x++) for (x = 0; x < bw; x++)
{ {
p = &b.part[ASTC_ChoosePartition(b.partindex, x,y,0, b.partitions, smallblock)]; p = &b.part[ASTC_ChoosePartition(b.partindex, x,y,0, b.partitions, smallblock)];
s = ((ds*x)*(b.wcount[0]-1)+32)>>6; s = ((ds*x)*(b.wcount[0]-1)+32)>>6;
w11 = ((s&0xf)*(t&0xf)+8) >> 4; fs=s&0xf;
w10 = (t&0xf) - w11; #ifdef ASTC_WITH_3D
w01 = (s&0xf) - w11; if (bd != 1)
w00 = 16 - (s&0xf) - (t&0xf) + w11; { //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] + w = ( w00*b.weights[v0] +
w01*b.weights[v0+planes] + w01*b.weights[v0+s1] +
w10*b.weights[v0+wstride] + w10*b.weights[v0+s2] +
w11*b.weights[v0+planes+wstride] + 8) >> 4; 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)+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)+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); 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 { //dual planes has a second set of weights that override a single channel
v0++; v0++;
w = ( w00*b.weights[v0] + w = ( w00*b.weights[v0] +
w01*b.weights[v0+planes] + w01*b.weights[v0+s1] +
w10*b.weights[v0+wstride] + w10*b.weights[v0+s2] +
w11*b.weights[v0+planes+wstride] + 8) >> 4; 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); 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 else
{ { //error colour == magenta
for (y = 0; y < bh; y++, out += stride) #ifdef ASTC_WITH_3D
for (x = 0; x < bw; x++) for (z = 0; z < bd; z++, out += layerstride)
{ #endif
out[(x<<2)+0] = 0;//0xf<<10; for (y = 0; y < bh; y++, out += stride)
out[(x<<2)+1] = 0; for (x = 0; x < bw; x++)
out[(x<<2)+2] = 0;//0xf<<10; {
out[(x<<2)+3] = 0xf<<10; out[(x<<2)+0] = 0xf<<10;
} out[(x<<2)+1] = 0;
out[(x<<2)+2] = 0xf<<10;
out[(x<<2)+3] = 0xf<<10;
}
} }
} }
#endif #endif

View File

@ -330,7 +330,9 @@ static void J_KillAll(void)
#endif #endif
#if SDL_MAJOR_VERSION >= 2 #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) switch(sdlkey)
{ {
@ -740,8 +742,10 @@ static unsigned int tbl_sdltoquakemouse[] =
K_MOUSE1, K_MOUSE1,
K_MOUSE3, K_MOUSE3,
K_MOUSE2, K_MOUSE2,
#if SDL_MAJOR_VERSION < 2
K_MWHEELUP, K_MWHEELUP,
K_MWHEELDOWN, K_MWHEELDOWN,
#endif
K_MOUSE4, K_MOUSE4,
K_MOUSE5, K_MOUSE5,
K_MOUSE6, K_MOUSE6,
@ -915,11 +919,38 @@ void Sys_SendKeyEvents(void)
IN_MouseMove(event.motion.which, false, event.motion.xrel, event.motion.yrel, 0, 0); IN_MouseMove(event.motion.which, false, event.motion.xrel, event.motion.yrel, 0, 0);
break; 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_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONUP:
#if SDL_MAJOR_VERSION >= 2 #if SDL_MAJOR_VERSION >= 2
if (event.button.which == SDL_TOUCH_MOUSEID) 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 #endif
//Hmm. SDL allows for 255 buttons, but only defines 5... //Hmm. SDL allows for 255 buttons, but only defines 5...
if (event.button.button > sizeof(tbl_sdltoquakemouse)/sizeof(tbl_sdltoquakemouse[0])) 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. 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. 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; 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"); cvar_t con_echochat = CVAR("con_echochat", "0");
extern cvar_t cl_chatmode; extern cvar_t cl_chatmode;
@ -659,14 +656,6 @@ int Con_ExecuteLine(console_t *con, const char *line)
return true; 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) qboolean Key_GetConsoleSelectionBox(console_t *con, int *sx, int *sy, int *ex, int *ey)
{ {
*sx = *sy = *ex = *ey = 0; *sx = *sy = *ex = *ey = 0;
@ -2866,13 +2855,12 @@ void Key_Init (void)
// //
// register our functions // 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 ("in_bind",Key_Bind_f);
Cmd_AddCommand ("bindlevel",Key_Bind_f); Cmd_AddCommand ("bindlevel",Key_Bind_f);
Cmd_AddCommandAD ("unbind",Key_Unbind_f, Key_Bind_c, NULL); 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"); Cvar_Register (&con_echochat, "Console variables");
} }

View File

@ -207,6 +207,7 @@ static qboolean pkg_updating; //when flagged, further changes are blocked until
#else #else
static const qboolean pkg_updating = false; static const qboolean pkg_updating = false;
#endif #endif
static qboolean pm_packagesinstalled;
//FIXME: these are allocated for the life of the exe. changing basedir should purge the list. //FIXME: these are allocated for the life of the exe. changing basedir should purge the list.
static int numdownloadablelists = 0; static int numdownloadablelists = 0;
@ -2459,7 +2460,15 @@ static void PM_PackageEnabled(package_t *p)
continue; continue;
COM_FileExtension(dep->name, ext, sizeof(ext)); COM_FileExtension(dep->name, ext, sizeof(ext));
if (!stricmp(ext, "pak") || !stricmp(ext, "pk3")) 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 #ifdef PLUGINS
if ((p->flags & DPF_PLUGIN) && !Q_strncasecmp(dep->name, PLUGINPREFIX, strlen(PLUGINPREFIX))) if ((p->flags & DPF_PLUGIN) && !Q_strncasecmp(dep->name, PLUGINPREFIX, strlen(PLUGINPREFIX)))
Cmd_ExecuteString(va("plug_load %s\n", dep->name), RESTRICT_LOCAL); 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); static void PM_StartADownload(void);
//callback from PM_StartADownload typedef struct
static void PM_Download_Got(struct dl_download *dl)
{ {
char native[MAX_OSPATH];
qboolean successful = dl->status == DL_FINISHED;
package_t *p; package_t *p;
char *tempname = dl->user_ctx; qboolean successful;
const enum fs_relative temproot = dl->user_num; 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) for (p = availablepackages; p ; p=p->next)
{ {
if (p->download == dl) if (p == info->p)
break; break;
} }
pm_packagesinstalled=true;
if (dl->file)
{
if (!VFS_CLOSE(dl->file))
successful = false;
dl->file = NULL;
}
else
successful = false;
if (p) if (p)
{ {
@ -2566,7 +2577,7 @@ static void PM_Download_Got(struct dl_download *dl)
if (!successful) 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); FS_Remove (tempname, temproot);
Z_Free(tempname); Z_Free(tempname);
PM_StartADownload(); 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); Con_Printf("menu_download: %s has no filename info\n", p->name);
} }
else 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); FS_Remove (tempname, temproot);
Z_Free(tempname); Z_Free(tempname);
PM_StartADownload(); 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) static char *PM_GetTempName(package_t *p)
{ {
@ -2956,6 +2998,16 @@ int PM_IsApplying(qboolean listsonly)
} }
#ifdef WEBCLIENT #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 //looks for the next package that needs downloading, and grabs it
static void PM_StartADownload(void) static void PM_StartADownload(void)
{ {
@ -3070,7 +3122,7 @@ static void PM_StartADownload(void)
if (tmpfile) 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) if (!p->download)
Con_Printf("Unable to download %s\n", p->name); 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 //clear the updating flag once there's no more activity needed
pkg_updating = downloading; pkg_updating = downloading;
} }
@ -3244,8 +3299,12 @@ void PM_ApplyChanges(void)
//and flag any new/updated ones for a download //and flag any new/updated ones for a download
for (p = availablepackages; p ; p=p->next) for (p = availablepackages; p ; p=p->next)
{ {
if ((p->flags&DPF_ALLMARKED) && !(p->flags&DPF_ENABLED) && !p->download) if (!p->download)
p->trymirrors = ~0u; 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. PM_StartADownload(); //and try to do those downloads.
#else #else
@ -4127,6 +4186,7 @@ typedef struct {
char pathprefix[MAX_QPATH]; char pathprefix[MAX_QPATH];
int downloadablessequence; int downloadablessequence;
char titletext[128]; char titletext[128];
char applymessage[128]; //so we can change its text to give it focus
qboolean populated; qboolean populated;
} dlmenu_t; } dlmenu_t;
@ -4646,6 +4706,13 @@ static void MD_Download_UpdateStatus(struct emenu_s *m)
else else
Q_snprintfz(info->titletext, sizeof(info->titletext), "Downloads (+%u -%u)", addpackages, rempackages); 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) if (!info->populated)
{ {
for (i = 0; i < numdownloadablelists; i++) for (i = 0; i < numdownloadablelists; i++)
@ -4660,7 +4727,7 @@ static void MD_Download_UpdateStatus(struct emenu_s *m)
info->populated = true; info->populated = true;
MC_AddFrameStart(m, 48); MC_AddFrameStart(m, 48);
y = 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->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)."; 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; y+=8;

View File

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

View File

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

View File

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

View File

@ -365,8 +365,8 @@ typedef enum
WG_LOADER = 1, WG_LOADER = 1,
WG_COUNT = 2 //main and loaders WG_COUNT = 2 //main and loaders
} wgroup_t; } 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_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); 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); qboolean COM_HasWork(void);
void COM_WorkerFullSync(void); void COM_WorkerFullSync(void);
void COM_DestroyWorkerThread(void); void COM_DestroyWorkerThread(void);

View File

@ -500,7 +500,7 @@ apic_t *R2D_LoadAtlasedPic(const char *name)
} }
if (!atlas.tex) 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) if (!atlas.shader)
{ {
atlas.shader = R_RegisterShader(va("fte_atlas%i", atlasid), SUF_NONE, 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) 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); Image_Upload(tid, rtfmt, NULL, NULL, width, height, imageflags);
tid->width = width; tid->width = width;
tid->height = height; 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++) for (i=0 ; i<size*3 ; i++)
blocklights[i] = r_fullbright.value*255*256; 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) 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*/ /*no samples, but map is otherwise lit = pure black*/
for (i=0 ; i<size*3 ; i++) for (i=0 ; i<size*3 ; i++)
blocklights[i] = 0; blocklights[i] = 0;
surf->cached_light[0] = 0; surf->cached_light[0] = d_lightstylevalue[0];
surf->cached_colour[0] = 0; surf->cached_colour[0] = cl_lightstyle[0].colourkey;
} }
else 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. { //r_fullbright is meant to be a scaler.
for (i=0 ; i<size ; i++) for (i=0 ; i<size ; i++)
blocklights[i] = r_fullbright.value*255*256; blocklights[i] = r_fullbright.value*255*256;
surf->cached_light[0] = d_lightstylevalue[0]; if (!surf->samples)
surf->cached_colour[0] = cl_lightstyle[0].colourkey; {
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) else if (!currentmodel->lightdata)
{ //no scalers here. { //no scalers here.
@ -1951,8 +1975,8 @@ void Surf_RenderDynamicLightmaps (msurface_t *fa)
// check for lightmap modification // check for lightmap modification
if (!fa->samples) if (!fa->samples)
{ {
if (fa->cached_light[0] != 0 if (fa->cached_light[0] != d_lightstylevalue[0]
|| fa->cached_colour[0] != 0) || fa->cached_colour[0] != cl_lightstyle[0].colourkey)
goto dynamic; goto dynamic;
} }
else else
@ -1994,8 +2018,8 @@ static void Surf_RenderDynamicLightmaps_Worker (model_t *wmodel, msurface_t *fa,
// check for lightmap modification // check for lightmap modification
if (!fa->samples) if (!fa->samples)
{ {
if (fa->cached_light[0] != 0 if (fa->cached_light[0] != d_lightstylevalue[0]
|| fa->cached_colour[0] != 0) || fa->cached_colour[0] != cl_lightstyle[0].colourkey)
goto dynamic; goto dynamic;
} }
else 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); pvs = es->wmodel->funcs.ClusterPVS(es->wmodel, es->cluster[1], &es->pvs, PVM_MERGE);
} }
else 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 defined(Q2BSPS) || defined(Q3BSPS)
if (es->wmodel->fromgame == fg_quake2 || es->wmodel->fromgame == fg_quake3) if (es->wmodel->fromgame == fg_quake2 || es->wmodel->fromgame == fg_quake3)
@ -3361,19 +3385,39 @@ void Surf_DrawWorld (void)
#ifdef Q1BSPS #ifdef Q1BSPS
else if (currentmodel->fromgame == fg_quake || currentmodel->fromgame == fg_halflife) else if (currentmodel->fromgame == fg_quake || currentmodel->fromgame == fg_halflife)
{ {
int i = cl_max_lightstyles; if (!webogenerating)
if (webostate && !webogenerating) {
for (i = 0; i < cl_max_lightstyles; i++) qboolean gennew = false;
if (!webostate)
gennew = true; //generate an initial one, if we can.
if (!gennew && webostate)
{ {
if (webostate->lightstylevalues[i] != d_lightstylevalue[i]) int i = cl_max_lightstyles;
break; 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)
{ if (!gennew && webostate && (webostate->cluster[0] != r_viewcluster || webostate->cluster[1] != r_viewcluster2))
} {
else if (webostate->pvs.buffersize != currentmodel->pvsbytes || r_viewcluster2 != -1)
{ gennew = true; //o.O
if (!webogenerating) 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; int i;
if (!currentmodel->numbatches) if (!currentmodel->numbatches)
@ -3820,8 +3864,8 @@ int Surf_NewLightmaps(int count, int width, int height, uploadfmt_t fmt, qboolea
int first = numlightmaps; int first = numlightmaps;
int i; int i;
unsigned int pixbytes, pixw, pixh; unsigned int pixbytes, pixw, pixh, pixd;
unsigned int dpixbytes, dpixw, dpixh; unsigned int dpixbytes, dpixw, dpixh, dpixd;
uploadfmt_t dfmt; uploadfmt_t dfmt;
if (!count) 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"); Con_Print("WARNING: Deluxemapping with odd number of lightmaps\n");
} }
Image_BlockSizeForEncoding(fmt, &pixbytes, &pixw, &pixh); Image_BlockSizeForEncoding(fmt, &pixbytes, &pixw, &pixh, &pixd);
if (pixw != 1 || pixh != 1) if (pixw != 1 || pixh != 1 || pixd != 1)
return -1; //compressed formats are unsupported return -1; //compressed formats are unsupported
dfmt = PTI_A2BGR10; //favour this one, because it tends to be slightly faster. dfmt = PTI_A2BGR10; //favour this one, because it tends to be slightly faster.
if (!sh_config.texfmt[dfmt]) 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; dfmt = PTI_RGBX8;
if (!sh_config.texfmt[dfmt]) if (!sh_config.texfmt[dfmt])
dfmt = PTI_RGB8; 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); Sys_LockMutex(com_resourcemutex);

View File

@ -478,7 +478,7 @@ void Image_Shutdown(void);
void Image_PrintInputFormatVersions(void); //for version info void Image_PrintInputFormatVersions(void); //for version info
qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struct pendingtextureinfo *mips); 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); 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); const char *Image_FormatName(uploadfmt_t encoding);
qboolean Image_FormatHasAlpha(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); 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_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_t r_fastturb = CVARF ("r_fastturb", "0",
CVAR_SHADERSYSTEM); 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_t r_fastsky = CVARF ("r_fastsky", "0",
CVAR_ARCHIVE); CVAR_ARCHIVE);
cvar_t r_fastskycolour = CVARF ("r_fastskycolour", "0", cvar_t r_fastskycolour = CVARF ("r_fastskycolour", "0",
@ -981,6 +982,7 @@ void Renderer_Init(void)
Cvar_Register (&r_nolightdir, GRAPHICALNICETIES); Cvar_Register (&r_nolightdir, GRAPHICALNICETIES);
Cvar_Register (&r_fastturb, GRAPHICALNICETIES); Cvar_Register (&r_fastturb, GRAPHICALNICETIES);
Cvar_Register (&r_skycloudalpha, GRAPHICALNICETIES);
Cvar_Register (&r_fastsky, GRAPHICALNICETIES); Cvar_Register (&r_fastsky, GRAPHICALNICETIES);
Cvar_Register (&r_fastskycolour, GRAPHICALNICETIES); Cvar_Register (&r_fastskycolour, GRAPHICALNICETIES);
Cvar_Register (&r_wateralpha, 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, /*8bpp*/ //useful for normalmaps (calculate blue)
PTI_EAC_RG11_SNORM, /*8bpp*/ //useful for normalmaps (calculate blue) PTI_EAC_RG11_SNORM, /*8bpp*/ //useful for normalmaps (calculate blue)
//astc... zomg. //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_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_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. 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_10X10_LDR, /*1.28*/
PTI_ASTC_12X10_LDR, /*1.07*/ PTI_ASTC_12X10_LDR, /*1.07*/
PTI_ASTC_12X12_LDR, /*0.89*/ 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_4X4_SRGB,
PTI_ASTC_5X4_SRGB, PTI_ASTC_5X4_SRGB,
PTI_ASTC_5X5_SRGB, PTI_ASTC_5X5_SRGB,
@ -189,6 +203,18 @@ typedef enum uploadfmt
PTI_ASTC_10X10_SRGB, PTI_ASTC_10X10_SRGB,
PTI_ASTC_12X10_SRGB, PTI_ASTC_12X10_SRGB,
PTI_ASTC_12X12_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_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_5X4_HDR,
PTI_ASTC_5X5_HDR, PTI_ASTC_5X5_HDR,
@ -203,8 +229,21 @@ typedef enum uploadfmt
PTI_ASTC_10X10_HDR, PTI_ASTC_10X10_HDR,
PTI_ASTC_12X10_HDR, PTI_ASTC_12X10_HDR,
PTI_ASTC_12X12_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 #define PTI_ASTC_LAST PTI_ASTC_12X12_HDR
#endif
//depth formats //depth formats
PTI_DEPTH16, PTI_DEPTH16,

View File

@ -432,16 +432,16 @@ static qboolean OpenAL_LoadCache(oalinfo_t *oali, unsigned int *bufptr, sfxcache
{ {
unsigned int fmt; unsigned int fmt;
unsigned int size; unsigned int size;
switch(sc->width) switch(sc->format)
{ {
#ifdef FTE_TARGET_WEB #ifdef FTE_TARGET_WEB
case 0: case QAF_BLOB:
palGenBuffers(1, bufptr); palGenBuffers(1, bufptr);
emscriptenfte_al_loadaudiofile(*bufptr, sc->data, sc->length); emscriptenfte_al_loadaudiofile(*bufptr, sc->data, sc->length);
//alIsBuffer will report false until success or failure... //alIsBuffer will report false until success or failure...
return true; //but we do have a 'proper' reference to the buffer. return true; //but we do have a 'proper' reference to the buffer.
#endif #endif
case 1: case QAF_S8:
if (sc->numchannels == 2) if (sc->numchannels == 2)
{ {
fmt = AL_FORMAT_STEREO8; fmt = AL_FORMAT_STEREO8;
@ -453,7 +453,7 @@ static qboolean OpenAL_LoadCache(oalinfo_t *oali, unsigned int *bufptr, sfxcache
size = sc->length*1; size = sc->length*1;
} }
break; break;
case 2: case QAF_S16:
if (sc->numchannels == 2) if (sc->numchannels == 2)
{ {
fmt = AL_FORMAT_STEREO16; fmt = AL_FORMAT_STEREO16;
@ -466,7 +466,7 @@ static qboolean OpenAL_LoadCache(oalinfo_t *oali, unsigned int *bufptr, sfxcache
} }
break; break;
#ifdef MIXER_F32 #ifdef MIXER_F32
case 4: case QAF_F32:
if (!oali->canfloataudio) if (!oali->canfloataudio)
return false; return false;
if (sc->numchannels == 2) if (sc->numchannels == 2)
@ -497,69 +497,70 @@ static qboolean OpenAL_LoadCache(oalinfo_t *oali, unsigned int *bufptr, sfxcache
} }
else if (volume != 1) else if (volume != 1)
{ {
if (sc->width == 1) switch(sc->format)
{ {
unsigned char *tmp = malloc(size); case QAF_S8:
char *src = sc->data;
int i;
for (i = 0; i < size; i++)
{ {
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); break;
free(tmp); case QAF_S16:
}
else if (sc->width == 2)
{
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. 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); break;
free(tmp);
}
#ifdef MIXER_F32 #ifdef MIXER_F32
else if (sc->width == 4) case QAF_F32:
{
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 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); break;
free(tmp);
}
#endif #endif
}
} }
else else
{ {
if (sc->width == 1) switch(sc->format)
{ {
unsigned char *tmp = malloc(size); case QAF_S8:
char *src = sc->data;
int i;
for (i = 0; i < size; i++)
{ {
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); break;
free(tmp); //case QAF_U8:
} case QAF_S16:
else if (sc->width == 2 || sc->width == 4) //case QAF_S32:
{ #ifdef MIXER_F32
#if 0 case QAF_F32:
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);
#endif #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 else
{ {
offset = pos - sbuf.soundoffset; offset = pos - sbuf.soundoffset;
sbuf.data += offset * sc->width*sc->numchannels; sbuf.data += offset * QAF_BYTES(sc->format)*sc->numchannels;
sbuf.length -= offset; sbuf.length -= offset;
} }
sbuf.soundoffset = 0; 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 { //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; sfxcache_t silence;
silence.speed = snd_speed; silence.speed = snd_speed;
silence.width = 2; silence.format = QAF_S16;
silence.numchannels = 1; silence.numchannels = 1;
silence.data = NULL; silence.data = NULL;
silence.length = 0.1 * silence.speed; 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 { //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; sfxcache_t silence;
silence.speed = snd_speed; silence.speed = snd_speed;
silence.width = 2; silence.format = QAF_S16;
silence.numchannels = 1; silence.numchannels = 1;
silence.data = NULL; silence.data = NULL;
silence.length = 0.1 * silence.speed; silence.length = 0.1 * silence.speed;

View File

@ -3166,18 +3166,25 @@ float S_GetChannelLevel(int entnum, int entchannel)
{ {
spos -= scache->soundoffset; spos -= scache->soundoffset;
spos *= scache->numchannels; spos *= scache->numchannels;
switch(scache->width) switch(scache->format)
{ {
case 1: case QAF_S8:
for (j = 0; j < scache->numchannels; j++) //average the channels for (j = 0; j < scache->numchannels; j++) //average the channels
result += abs(((signed char*)scache->data)[spos+j]); result += abs(((signed char*)scache->data)[spos+j]);
result /= scache->numchannels*127.0; result /= scache->numchannels*127.0;
break; break;
case 2: case QAF_S16:
for (j = 0; j < scache->numchannels; j++) //average the channels for (j = 0; j < scache->numchannels; j++) //average the channels
result += abs(((signed short*)scache->data)[spos+j]); result += abs(((signed short*)scache->data)[spos+j]);
result /= scache->numchannels*32767.0; result /= scache->numchannels*32767.0;
break; 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 else
@ -4124,14 +4131,14 @@ void S_SoundList_f(void)
Con_Printf("?( ) : %s\n", sfx->name); Con_Printf("?( ) : %s\n", sfx->name);
continue; 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; duration = (sc->soundoffset+sc->length) / sc->speed;
total += size; total += size;
if (sfx->loopstart >= 0) if (sfx->loopstart >= 0)
Con_Printf ("L"); Con_Printf ("L");
else else
Con_Printf (" "); 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); Con_Printf ("Total resident: %i\n", total);
@ -4179,7 +4186,7 @@ typedef struct {
sfx_t *sfx; sfx_t *sfx;
int numchannels; int numchannels;
int width; qaudiofmt_t format;
int length; int length;
void *data; void *data;
} streaming_t; } 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->numchannels = s->numchannels;
buf->soundoffset = 0; buf->soundoffset = 0;
buf->speed = snd_speed; buf->speed = snd_speed;
buf->width = s->width; buf->format = s->format;
} }
if (start >= s->length) if (start >= s->length)
return NULL; //eof... 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 //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; soundcardinfo_t *si;
int i; 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->sfx->loadstate = SLS_LOADED;
s->numchannels = channels; s->numchannels = channels;
s->width = width; s->format = format;
s->data = NULL; s->data = NULL;
s->length = 0; s->length = 0;
@ -4302,9 +4309,9 @@ void S_RawAudio(int sourceid, qbyte *data, int speed, int samples, int channels,
} }
S_LockMixer(); 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->numchannels = channels;
s->length = 0; s->length = 0;
Con_Printf("Restarting raw stream\n"); 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); newcache = BZ_Malloc((spare+outsamples) * (s->numchannels) * QAF_BYTES(s->format));
memcpy(newcache, (qbyte*)s->data + prepadl * (s->numchannels) * s->width, spare * (s->numchannels) * s->width); memcpy(newcache, (qbyte*)s->data + prepadl * (s->numchannels) * QAF_BYTES(s->format), spare * (s->numchannels) * QAF_BYTES(s->format));
BZ_Free(s->data); BZ_Free(s->data);
s->data = newcache; 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; 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, SND_ResampleStream(data,
speed, speed,
width, format,
channels, channels,
samples, samples,
outpos, outpos,
snd_speed, snd_speed,
s->width, s->format,
s->numchannels, s->numchannels,
snd_linearresample_stream.ival); snd_linearresample_stream.ival);
} }

View File

@ -28,7 +28,7 @@ typedef struct
{ {
int format; int format;
int rate; int rate;
int width; int bitwidth;
int numchannels; int numchannels;
int loopstart; int loopstart;
int samples; int samples;
@ -295,7 +295,7 @@ qbyte *S_Alloc (int size);
// SND_ResampleStream: takes a sound stream and converts with given parameters. Limited to // 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. // 8-16-bit signed conversions and mono-to-mono/stereo-to-stereo conversions.
// Not an in-place algorithm. // 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; double scale;
signed char *in8 = (signed char *)in; 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) if (insamps <= 0)
return; 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; return;
} }
if (inchannels == 1 && outchannels == 1) if (inchannels == 1 && outchannels == 1)
{ {
if (inwidth == 1) if (informat == QAF_S8)
{ {
if (outwidth == 1) if (outformat == QAF_S8)
{ {
if (inrate < outrate) // upsample if (inrate < outrate) // upsample
{ {
@ -336,7 +336,7 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
} }
return; return;
} }
else else if (outformat == QAF_S16)
{ {
if (inrate == outrate) // quick convert if (inrate == outrate) // quick convert
QUICKCONVERT(in8, insamps, out16, 8, 0) QUICKCONVERT(in8, insamps, out16, 8, 0)
@ -357,9 +357,9 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
return; return;
} }
} }
else // 16-bit else if (informat == QAF_S16) // 16-bit
{ {
if (outwidth == 2) if (outformat == QAF_S16)
{ {
if (inrate < outrate) // upsample if (inrate < outrate) // upsample
{ {
@ -377,7 +377,7 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
} }
return; return;
} }
else else if (outformat == QAF_S8)
{ {
if (inrate == outrate) // quick convert if (inrate == outrate) // quick convert
QUICKCONVERT(in16, insamps, out8, 0, 8) 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) else if (outchannels == 2 && inchannels == 2)
{ {
if (inwidth == 1) if (informat == QAF_S8)
{ {
if (outwidth == 1) if (outformat == QAF_S8)
{ {
if (inrate < outrate) // upsample if (inrate < outrate) // upsample
{ {
@ -419,6 +419,7 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
else else
STANDARDRESCALESTEREO(in8, inrate, insamps, out8, outrate, 0, 0) STANDARDRESCALESTEREO(in8, inrate, insamps, out8, outrate, 0, 0)
} }
return;
} }
else 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 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) STANDARDRESCALESTEREO(in16, inrate, insamps, out16, outrate, 0, 0)
} }
} }
else else if (outformat == QAF_S8)
{ {
if (inrate == outrate) // quick convert if (inrate == outrate) // quick convert
{ {
@ -489,9 +490,9 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
#if 0 #if 0
else if (outchannels == 1 && inchannels == 2) else if (outchannels == 1 && inchannels == 2)
{ {
if (inwidth == 1) if (informat == QAF_S8)
{ {
if (outwidth == 1) if (outformat == QAF_S8)
{ {
if (inrate < outrate) // upsample if (inrate < outrate) // upsample
{ {
@ -503,7 +504,7 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
else // downsample else // downsample
STANDARDRESCALESTEREOTOMONO(in8, inrate, insamps, out8, outrate, 0, 0) STANDARDRESCALESTEREOTOMONO(in8, inrate, insamps, out8, outrate, 0, 0)
} }
else else if (outformat == QAF_S16)
{ {
if (inrate == outrate) // quick convert if (inrate == outrate) // quick convert
QUICKCONVERTSTEREOTOMONO(in8, insamps, out16, 8, 0) 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) 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 if (inrate < outrate) // upsample
{ {
@ -532,7 +533,7 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
else // downsample else // downsample
STANDARDRESCALESTEREOTOMONO(in16, inrate, insamps, out16, outrate, 0, 0) STANDARDRESCALESTEREOTOMONO(in16, inrate, insamps, out16, outrate, 0, 0)
} }
else else if (outformat == QAF_S8)
{ {
if (inrate == outrate) // quick convert if (inrate == outrate) // quick convert
QUICKCONVERTSTEREOTOMONO(in16, insamps, out8, 0, 8) QUICKCONVERTSTEREOTOMONO(in16, insamps, out8, 0, 8)
@ -556,7 +557,7 @@ void SND_ResampleStream (void *in, int inrate, int inwidth, int inchannels, int
ResampleSfx 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_linearresample;
extern cvar_t snd_loadasstereo; extern cvar_t snd_loadasstereo;
@ -564,17 +565,17 @@ static qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, int inwidth
sfxcache_t *sc; sfxcache_t *sc;
int outsamps; int outsamps;
int len; int len;
int outwidth; qaudiofmt_t outformat;
scale = snd_speed / (double)inrate; scale = snd_speed / (double)inrate;
outsamps = insamps * scale; outsamps = insamps * scale;
if (loadas8bit.ival < 0) if (loadas8bit.ival < 0)
outwidth = 2; outformat = QAF_S16;
else if (loadas8bit.ival) else if (loadas8bit.ival)
outwidth = 1; outformat = QAF_S8;
else else
outwidth = inwidth; outformat = informat;
len = outsamps * outwidth * inchannels; len = outsamps * QAF_BYTES(outformat) * inchannels;
sfx->decoder.buf = sc = BZ_Malloc(sizeof(sfxcache_t) + len); sfx->decoder.buf = sc = BZ_Malloc(sizeof(sfxcache_t) + len);
if (!sc) if (!sc)
@ -583,7 +584,7 @@ static qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, int inwidth
} }
sc->numchannels = inchannels; sc->numchannels = inchannels;
sc->width = outwidth; sc->format = outformat;
sc->speed = snd_speed; sc->speed = snd_speed;
sc->length = outsamps; sc->length = outsamps;
sc->soundoffset = 0; sc->soundoffset = 0;
@ -595,12 +596,12 @@ static qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, int inwidth
SND_ResampleStream (data, SND_ResampleStream (data,
inrate, inrate,
inwidth, informat,
inchannels, inchannels,
insamps, insamps,
sc->data, sc->data,
sc->speed, sc->speed,
sc->width, sc->format,
sc->numchannels, sc->numchannels,
snd_linearresample.ival); snd_linearresample.ival);
@ -611,12 +612,12 @@ static qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, int inwidth
nc->data = (qbyte*)(nc+1); nc->data = (qbyte*)(nc+1);
SND_ResampleStream (sc->data, SND_ResampleStream (sc->data,
sc->speed, sc->speed,
sc->width, sc->format,
sc->numchannels, sc->numchannels,
outsamps, outsamps,
nc->data, nc->data,
nc->speed*2, nc->speed*2,
nc->width, nc->format,
nc->numchannels, nc->numchannels,
false); false);
nc->numchannels *= 2; nc->numchannels *= 2;
@ -738,22 +739,10 @@ static qboolean QDECL S_LoadDoomSound (sfx_t *s, qbyte *data, size_t datalen, in
} }
#endif #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) static qboolean QDECL S_LoadWavSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed, qboolean forcedecode)
{ {
wavinfo_t info; wavinfo_t info;
qaudiofmt_t format;
if (datalen < 4 || strncmp(data, "RIFF", 4)) if (datalen < 4 || strncmp(data, "RIFF", 4))
return false; return false;
@ -766,22 +755,62 @@ static qboolean QDECL S_LoadWavSound (sfx_t *s, qbyte *data, size_t datalen, int
return false; return false;
} }
if (info.format == 1 && info.width == 1) //unsigned bytes if (info.format == 1 && info.bitwidth == 8) //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
{ {
S_ShortedLittleFloats(data + info.dataofs, info.samples*info.numchannels); COM_CharBias(data + info.dataofs, info.samples*info.numchannels);
info.width = 2; 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 else
{ {
s->loadstate = SLS_FAILED; s->loadstate = SLS_FAILED;
switch(info.format) switch(info.format)
{ {
case 1/*WAVE_FORMAT_PCM*/: 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 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 7/*WAVE_FORMAT_MULAW*/: Con_Printf ("%s uses unsupported mu-law format.\n", s->name); break;
case 0xfffe/*WAVE_FORMAT_EXTENSIBLE*/: 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 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); 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.numchannels = GetLittleShort(&ctx);
info.rate = GetLittleLong(&ctx); info.rate = GetLittleLong(&ctx);
ctx.data_p += 4+2; ctx.data_p += 4+2;
info.width = GetLittleShort(&ctx) / 8; info.bitwidth = GetLittleShort(&ctx);
// get cue chunk // get cue chunk
chunklen = FindChunk(&ctx, "cue "); chunklen = FindChunk(&ctx, "cue ");
@ -1209,7 +1238,7 @@ static wavinfo_t GetWavinfo (char *name, qbyte *wav, int wavlength)
} }
ctx.data_p += 8; ctx.data_p += 8;
samples = chunklen / info.width /info.numchannels; samples = (chunklen<<3) / info.bitwidth / info.numchannels;
if (info.samples) if (info.samples)
{ {

View File

@ -311,8 +311,9 @@ void S_PaintChannels(soundcardinfo_t *sc, int endtime)
continue; continue;
} }
if (scache->width == 1) switch(scache->format)
{ {
case QAF_S8:
if (scache->numchannels==2) if (scache->numchannels==2)
SND_PaintChannel8_O2I2(ch, scache, ltime-sc->paintedtime, count, rate); SND_PaintChannel8_O2I2(ch, scache, ltime-sc->paintedtime, count, rate);
else if (sc->sn.numchannels <= 2) 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); SND_PaintChannel8_O6I1(ch, scache, count, rate);
else else
SND_PaintChannel8_O8I1(ch, scache, count, rate); SND_PaintChannel8_O8I1(ch, scache, count, rate);
} break;
else if (scache->width == 2) case QAF_S16:
{
if (scache->numchannels==2) if (scache->numchannels==2)
SND_PaintChannel16_O2I2(ch, scache, ltime-sc->paintedtime, count, rate); SND_PaintChannel16_O2I2(ch, scache, ltime-sc->paintedtime, count, rate);
else if (sc->sn.numchannels <= 2) 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); SND_PaintChannel16_O6I1(ch, scache, count, rate);
else else
SND_PaintChannel16_O8I1(ch, scache, count, rate); SND_PaintChannel16_O8I1(ch, scache, count, rate);
} break;
#ifdef MIXER_F32 #ifdef MIXER_F32
else if (scache->width == 4) case QAF_F32:
{
if (scache->numchannels==2) if (scache->numchannels==2)
SND_PaintChannel32F_O2I2(ch, scache, ltime-sc->paintedtime, count, rate); SND_PaintChannel32F_O2I2(ch, scache, ltime-sc->paintedtime, count, rate);
else if (sc->sn.numchannels <= 2) 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); SND_PaintChannel32F_O6I1(ch, scache, count, rate);
else else
SND_PaintChannel32F_O8I1(ch, scache, count, rate); SND_PaintChannel32F_O8I1(ch, scache, count, rate);
} break;
#endif #endif
}
ltime += count; ltime += count;
ch->pos += rate * 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->length = dec->pcmtotal;
buf->numchannels = dec->srcchannels; buf->numchannels = dec->srcchannels;
buf->speed = dec->srcspeed; buf->speed = dec->srcspeed;
buf->width = 2; buf->format = QAF_S16;
} }
if (name) 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->length = dec->decodedbytecount / (2 * dec->srcchannels);
buf->numchannels = dec->srcchannels; buf->numchannels = dec->srcchannels;
buf->speed = snd_speed; buf->speed = snd_speed;
buf->width = 2; buf->format = QAF_S16;
} }
return buf; return buf;
} }

View File

@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef __SOUND__ #ifndef __SOUND__
#define __SOUND__ #define __SOUND__
//#define MIXER_F32
#define MAXSOUNDCHANNELS 8 //on a per device basis #define MAXSOUNDCHANNELS 8 //on a per device basis
//pitch/rate changes require that we track stuff with subsample precision. //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 int loopstart; //-1 or sample index to begin looping at once the sample ends
} sfx_t; } 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 !!! // !!! if this is changed, it much be changed in asm_i386.h too !!!
typedef struct sfxcache_s typedef struct sfxcache_s
{ {
usamplepos_t length; //sample count usamplepos_t length; //sample count
unsigned int speed; unsigned int speed;
unsigned int width; qaudiofmt_t format;
unsigned int numchannels; unsigned int numchannels;
usamplepos_t soundoffset; //byte index into the sound usamplepos_t soundoffset; //byte index into the sound
qbyte *data; // variable sized 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 // picks a channel based on priorities, empty slots, number of channels
channel_t *SND_PickChannel(soundcardinfo_t *sc, int entnum, int entchannel); 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) // restart entire sound subsystem (doesn't flush old sounds, so make sure that happens)
void S_DoRestart (qboolean onlyifneeded); void S_DoRestart (qboolean onlyifneeded);
@ -258,7 +274,7 @@ void S_DoRestart (qboolean onlyifneeded);
void S_Restart_f (void); void S_Restart_f (void);
//plays streaming audio //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); void CLVC_Poll (void);

View File

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

View File

@ -868,7 +868,8 @@ int QDECL main(int argc, char **argv)
sleeptime = Host_Frame (time); sleeptime = Host_Frame (time);
oldtime = newtime; oldtime = newtime;
Sys_Sleep(sleeptime); if (sleeptime)
Sys_Sleep(sleeptime);
} }
} }
@ -1068,17 +1069,14 @@ qboolean Sys_RunInstaller(void)
#endif #endif
#ifdef HAVEAUTOUPDATE #ifdef HAVEAUTOUPDATE
//legacy, so old build can still deal with updates properly //returns true if we could sucessfull overwrite the engine binary.
void Sys_SetUpdatedBinary(const char *fname) qboolean Sys_SetUpdatedBinary(const char *fname)
{ {
return false;
} }
//says whether the system code is able to invoke new binaries properly //says whether the system code is able/allowed to overwrite itself.
qboolean Sys_EngineCanUpdate(void) //(ie: return false if we don't know the binary name or if its write-protected etc)
{ qboolean Sys_EngineMayUpdate(void)
return false; //nope, nothing here
}
//invoke the given system-path binary
qboolean Sys_EngineWasUpdated(char *newbinary)
{ {
return false; //sorry 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_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_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_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_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."); 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); 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 *QDECL FS_OpenVFS(const char *filename, const char *mode, enum fs_relative relativeto);
vfsfile_t *FS_OpenTemp(void); 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, ...); 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_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_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_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. GAMEDIR_SPECIAL=GAMEDIR_USEBASEDIR|GAMEDIR_STEAMGAME, //if one of these flags, then the gamedir cannot be simply concatenated to the basedir/homedir.
} flags; } flags;

View File

@ -617,7 +617,10 @@ static qboolean FS_Manifest_ParseTokens(ftemanifest_t *man)
else if (!Q_strcasecmp(cmd, "mainconfig")) else if (!Q_strcasecmp(cmd, "mainconfig"))
{ {
Z_Free(man->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")) else if (!Q_strcasecmp(cmd, "defaultexec"))
{ {
@ -672,6 +675,11 @@ static qboolean FS_Manifest_ParseTokens(ftemanifest_t *man)
if (!Q_strcasecmp(cmd, "basegame")) if (!Q_strcasecmp(cmd, "basegame"))
man->gamepath[i].flags |= GAMEDIR_BASEGAME; man->gamepath[i].flags |= GAMEDIR_BASEGAME;
if (*newdir == '/')
{
newdir++;
man->gamepath[i].flags |= GAMEDIR_QSHACK;
}
if (*newdir == '*') if (*newdir == '*')
{ //*dir makes the dir 'private' and not networked. { //*dir makes the dir 'private' and not networked.
newdir++; newdir++;
@ -1567,7 +1575,7 @@ fail:
else else
Con_Printf("Failed\n"); 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) 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. 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; searchpathfuncs_t *pak;
int j; int j;
char ext[8]; char ext[8];
if (!f)
return NULL;
COM_FileExtension(pakname, ext, sizeof(ext)); COM_FileExtension(pakname, ext, sizeof(ext));
for (j = 0; j < sizeof(searchpathformats)/sizeof(searchpathformats[0]); j++) 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; flocation_t loc;
wildpaks_t wp; wildpaks_t wp;
filelist_t list = {0}; filelist_t list = {0};
qboolean qshack = (pflags&SPF_QSHACK);
pflags &= ~SPF_QSHACK;
Q_strncpyz(logicalpaths, logicalpath, sizeof(logicalpaths)); Q_strncpyz(logicalpaths, logicalpath, sizeof(logicalpaths));
FS_CleanDir(logicalpaths, sizeof(logicalpaths)); FS_CleanDir(logicalpaths, sizeof(logicalpaths));
@ -3005,37 +3017,50 @@ static void FS_AddDataFiles(searchpath_t **oldpaths, const char *purepath, const
continue; continue;
if (loadstuff & (1<<j)) if (loadstuff & (1<<j))
{ {
qboolean okay = true;
const char *extension = searchpathformats[j].extension; const char *extension = searchpathformats[j].extension;
//first load all the numbered pak files //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); snprintf (pakfile, sizeof(pakfile), "pak%i.%s", i, extension);
fs_finds++; fs_finds++;
if (!search->handle->FindFile(search->handle, &loc, pakfile, NULL)) 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 (!Q_strcasecmp(existing->logicalpath, logicalfile)) //assumption: first member of structure is a char array snprintf (logicalfile, sizeof(logicalfile), "%spak%i.%s", logicalpaths, i, extension);
break; //already loaded (base paths?) 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); handle = FS_GetOldPath(oldpaths, logicalfile, &keptflags);
if (!handle) if (!handle)
{ handle = FS_OpenPackByExtension(VFSOS_Open(pakfile, "rb"), NULL, pakfile, pakfile);
vfs = search->handle->OpenVFS(search->handle, &loc, "rb"); if (handle) //logically should have SPF_EXPLICIT set, but that would give it a worse gamedir depth
if (!vfs) FS_AddPathHandle(oldpaths, "", pakfile, handle, "", SPF_COPYPROTECTED|SPF_PRIVATE, (unsigned int)-1);
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);
} }
} }
} }
@ -3098,7 +3123,7 @@ static searchpath_t *FS_AddPathHandle(searchpath_t **oldpaths, const char *purep
//temp packages also do not nest //temp packages also do not nest
// if (!(flags & SPF_TEMPORARY)) // 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) 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 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 #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...*/ /*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. //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" #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*/ /*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. //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"*/}, {"-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-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 //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... //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 //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 //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. //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; fl |= SPF_WRITABLE;
if (fs_manifest->gamepath[i].flags&GAMEDIR_PRIVATE) if (fs_manifest->gamepath[i].flags&GAMEDIR_PRIVATE)
fl |= SPF_PRIVATE; fl |= SPF_PRIVATE;
if (fs_manifest->gamepath[i].flags&GAMEDIR_QSHACK)
fl |= SPF_QSHACK;
if (fs_manifest->gamepath[i].flags&GAMEDIR_USEBASEDIR) 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. { //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) while(oldpaths)
{ {
fs_restarts++;
next = oldpaths->next; next = oldpaths->next;
Con_DPrintf("%s is no longer needed\n", oldpaths->logicalpath); 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); FS_FLocateFile("gfx/palette.lmp", 0, &paletteloc2);
if (paletteloc.search != paletteloc2.search) if (paletteloc.search != paletteloc2.search)
Cbuf_AddText("vid_reload\n", RESTRICT_LOCAL); Cbuf_AddText("vid_reload\n", RESTRICT_LOCAL);
} }
static void FS_ReloadPackFiles_f(void) static void FS_ReloadPackFiles_f(void)
@ -5006,6 +5034,7 @@ static ftemanifest_t *FS_GenerateLegacyManifest(int game, const char *basedir)
{ {
ftemanifest_t *man; ftemanifest_t *man;
size_t j; size_t j;
const char *cexec;
if (gamemode_info[game].manifestfile) if (gamemode_info[game].manifestfile)
man = FS_Manifest_ReadMem(NULL, basedir, 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); 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); FS_Manifest_ParseTokens(man);
} }
@ -5379,7 +5414,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
#ifdef HAVE_CLIENT #ifdef HAVE_CLIENT
qboolean allowvidrestart = true; qboolean allowvidrestart = true;
char *vidfile[] = {"gfx.wad", "gfx/conback.lmp", //misc stuff 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)]; searchpathfuncs_t *vidpath[countof(vidfile)];
#endif #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_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_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_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); qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, int *crc, unsigned int flags);
#ifdef AVAIL_XZDEC #ifdef AVAIL_XZDEC

View File

@ -1291,7 +1291,9 @@ const dtlsfuncs_t *SSPI_DTLS_InitClient(void)
#endif #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) enum hashvalidation_e SSPI_VerifyHash(qbyte *hashdata, size_t hashsize, const char *authority, qbyte *signdata, size_t signsize)
{ {
NTSTATUS status; NTSTATUS status;
@ -1303,8 +1305,8 @@ enum hashvalidation_e SSPI_VerifyHash(qbyte *hashdata, size_t hashsize, const ch
size_t dersize; 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 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 BOOL (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 *pCertFreeCertificateContext) (PCCERT_CONTEXT pCertContext);
static dllhandle_t *crypt32; static dllhandle_t *crypt32;
static dllfunction_t crypt32funcs[] = { static dllfunction_t crypt32funcs[] = {
{(void**)&pCertCreateContext, "CertCreateContext"}, {(void**)&pCertCreateContext, "CertCreateContext"},

View File

@ -2305,6 +2305,8 @@ vfsfile_t *FS_OpenSSL(const char *peername, vfsfile_t *source, qboolean isserver
if (!f) if (!f)
f = SSPI_OpenVFS(hostname, source, isserver); f = SSPI_OpenVFS(hostname, source, isserver);
#endif #endif
if (!f) //it all failed.
VFS_CLOSE(source);
return f; return f;
} }
int TLS_GetChannelBinding(vfsfile_t *stream, qbyte *data, size_t *datasize) 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; qboolean sendingweirdness = false;
char arg[WCATTR_COUNT][64]; char arg[WCATTR_COUNT][64];
if (!net_enable_http.ival && !net_enable_websockets.ival && !net_enable_rtcbroker.ival) 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 //we need to respond, firefox will create 10 different connections if we just close it
@ -5278,6 +5281,25 @@ static enum{
if (headerscomplete) 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 #ifdef MVD_RECORDING
//for QTV connections, we just need the method and a blank line. our qtv parser will parse the actual headers. //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)) if (!Q_strncasecmp(st->inbuffer, "QTV", 3))
@ -8453,6 +8475,23 @@ void NET_Init (void)
#if defined(HAVE_CLIENT)||defined(HAVE_SERVER) #if defined(HAVE_CLIENT)||defined(HAVE_SERVER)
Net_Master_Init(); Net_Master_Init();
#endif #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 #ifdef HAVE_CLIENT
void NET_CloseClient(void) void NET_CloseClient(void)
@ -8949,12 +8988,23 @@ vfsfile_t *FS_WrapTCPSocket(SOCKET sock, qboolean conpending, const char *peerna
return &newf->funcs; 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}; netadr_t adr = {0};
if (NET_StringToAdr(name, defaultport, &adr)) 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 else
return NULL; return NULL;

View File

@ -453,6 +453,6 @@ int TLS_GetChannelBinding(vfsfile_t *stream, qbyte *data, size_t *datasize); //d
#ifdef HAVE_PACKET #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). 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 #endif
vfsfile_t *FS_OpenTCP(const char *name, int defaultport); vfsfile_t *FS_OpenTCP(const char *name, int defaultport, qboolean assumetls);
#endif //NETINC_INCLUDED #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) static qhandle_t QDECL Plug_Net_TCPConnect(const char *remoteip, int remoteport)
{ {
int handle; int handle;
vfsfile_t *stream = FS_OpenTCP(remoteip, remoteport); vfsfile_t *stream = FS_OpenTCP(remoteip, remoteport, false);
if (!currentplug || !stream) if (!currentplug || !stream)
return -1; return -1;
handle = Plug_NewStreamHandle(STREAM_VFS); 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_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_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_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_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_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."); 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_brokenfloatconvert, cvargroup_progs);
Cvar_Register (&pr_tempstringcount, cvargroup_progs); Cvar_Register (&pr_tempstringcount, cvargroup_progs);
Cvar_Register (&pr_tempstringsize, cvargroup_progs); Cvar_Register (&pr_tempstringsize, cvargroup_progs);
Cvar_Register (&pr_gc_threaded, cvargroup_progs);
#ifdef WEBCLIENT #ifdef WEBCLIENT
Cvar_Register (&pr_enable_uriget, cvargroup_progs); Cvar_Register (&pr_enable_uriget, cvargroup_progs);
#endif #endif
@ -1222,7 +1224,7 @@ void QCBUILTIN PF_findchainflags (pubprogfuncs_t *prinst, struct globalvars_s *p
int s; int s;
wedict_t *ent, *chain; wedict_t *ent, *chain;
chain = (wedict_t *) *prinst->parms->sv_edicts; chain = (wedict_t *) *prinst->parms->edicts;
ff = G_INT(OFS_PARM0)+prinst->fieldadjust; ff = G_INT(OFS_PARM0)+prinst->fieldadjust;
s = G_FLOAT(OFS_PARM1); s = G_FLOAT(OFS_PARM1);
@ -1231,7 +1233,7 @@ void QCBUILTIN PF_findchainflags (pubprogfuncs_t *prinst, struct globalvars_s *p
else else
cf = &((comentvars_t*)NULL)->chain - (int*)NULL; 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); ent = WEDICT_NUM_PB(prinst, i);
if (ED_ISFREE(ent)) if (ED_ISFREE(ent))
@ -1253,7 +1255,7 @@ void QCBUILTIN PF_findchainfloat (pubprogfuncs_t *prinst, struct globalvars_s *p
float s; float s;
wedict_t *ent, *chain; wedict_t *ent, *chain;
chain = (wedict_t *) *prinst->parms->sv_edicts; chain = (wedict_t *) *prinst->parms->edicts;
ff = G_INT(OFS_PARM0)+prinst->fieldadjust; ff = G_INT(OFS_PARM0)+prinst->fieldadjust;
s = G_FLOAT(OFS_PARM1); s = G_FLOAT(OFS_PARM1);
@ -1262,7 +1264,7 @@ void QCBUILTIN PF_findchainfloat (pubprogfuncs_t *prinst, struct globalvars_s *p
else else
cf = &((comentvars_t*)NULL)->chain - (int*)NULL; 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); ent = WEDICT_NUM_PB(prinst, i);
if (ED_ISFREE(ent)) if (ED_ISFREE(ent))
@ -1286,7 +1288,7 @@ void QCBUILTIN PF_findchain (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo
string_t t; string_t t;
wedict_t *ent, *chain; wedict_t *ent, *chain;
chain = (wedict_t *) *prinst->parms->sv_edicts; chain = (wedict_t *) *prinst->parms->edicts;
ff = G_INT(OFS_PARM0)+prinst->fieldadjust; ff = G_INT(OFS_PARM0)+prinst->fieldadjust;
s = PR_GetStringOfs(prinst, OFS_PARM1); s = PR_GetStringOfs(prinst, OFS_PARM1);
@ -1295,7 +1297,7 @@ void QCBUILTIN PF_findchain (pubprogfuncs_t *prinst, struct globalvars_s *pr_glo
else else
cf = &((comentvars_t*)NULL)->chain - (int*)NULL; 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); ent = WEDICT_NUM_PB(prinst, i);
if (ED_ISFREE(ent)) 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; f = G_INT(OFS_PARM1)+prinst->fieldadjust;
s = G_FLOAT(OFS_PARM2); 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); ed = WEDICT_NUM_PB(prinst, e);
if (ED_ISFREE(ed)) 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 //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; f = G_INT(OFS_PARM1)+prinst->fieldadjust;
s = G_INT(OFS_PARM2); 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); ed = WEDICT_NUM_PB(prinst, e);
if (ED_ISFREE(ed)) 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; // 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; return;
} }
for (e++ ; e < *prinst->parms->sv_num_edicts ; e++) for (e++ ; e < *prinst->parms->num_edicts ; e++)
{ {
ed = WEDICT_NUM_PB(prinst, e); ed = WEDICT_NUM_PB(prinst, e);
if (ED_ISFREE(ed)) 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 //Finding
@ -3263,9 +3265,9 @@ void QCBUILTIN PF_nextent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globa
while (1) while (1)
{ {
i++; 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; return;
} }
ent = WEDICT_NUM_PB(prinst, i); 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_tempstringcount;
extern cvar_t pr_enable_profiling; extern cvar_t pr_enable_profiling;
extern cvar_t pr_fixbrokenqccarrays; extern cvar_t pr_fixbrokenqccarrays;
extern cvar_t pr_gc_threaded;
extern int qcinput_scan; extern int qcinput_scan;
extern int qcinput_unicode; extern int qcinput_unicode;

View File

@ -276,82 +276,71 @@ void Sys_Sleep (double seconds)
#include <errno.h> #include <errno.h>
#include <sys/wait.h> #include <sys/wait.h>
typedef struct slaveserver_s typedef struct
{ {
pubsubserver_t pub; vfsfile_t pub;
int inpipe; int inpipe;
int outpipe; int outpipe;
pid_t pid; //so we don't end up with zombie processes pid_t pid; //so we don't end up with zombie processes
qbyte inbuffer[2048];
int inbufsize;
} linsubserver_t; } 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. linsubserver_t *s = (linsubserver_t*)file;
//FIXME: merge buffering logic with SSV_InstructMaster, and allow for failure if full ssize_t avail = read(s->inpipe, buffer, bytestoread);
linsubserver_t *s = (linsubserver_t*)ps; if (!avail)
if (s->outpipe == -1) return -1; //EOF
return; //it already died. if (avail < 0)
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)
{ {
ssize_t avail = read(s->inpipe, s->inbuffer+s->inbufsize, sizeof(s->inbuffer)-s->inbufsize); int e = errno;
if (!avail) if (e == EAGAIN || e == EWOULDBLOCK || e == EINTR)
{ //eof return 0; //no data available
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");
}
else 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); perror("subserver read");
net_message.cursize = len-2; return -1; //some sort of error
memmove(s->inbuffer, s->inbuffer+len, s->inbufsize - len);
s->inbufsize -= len;
MSG_BeginReading (msg_nullnetprim);
return 1;
} }
} }
else if (s->inpipe == -1) return avail;
return -1; }
return 0; 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 #ifdef SQL
#include "sv_sql.h" #include "sv_sql.h"
#endif #endif
pubsubserver_t *Sys_ForkServer(void) vfsfile_t *Sys_ForkServer(void)
{ {
#ifdef SERVERONLY #ifdef SERVERONLY
// extern jmp_buf host_abort; // extern jmp_buf host_abort;
@ -473,23 +462,29 @@ pubsubserver_t *Sys_ForkServer(void)
close(toslave[0]); close(toslave[0]);
ctx->outpipe = toslave[1]; ctx->outpipe = toslave[1];
ctx->pub.funcs.InstructSlave = Sys_InstructSlave; ctx->pub.ReadBytes = Sys_MSV_ReadBytes;
ctx->pub.funcs.SubServerRead = Sys_SubServerRead; ctx->pub.WriteBytes = Sys_MSV_WriteBytes;
ctx->pub.Close = Sys_MSV_Close;
return &ctx->pub; 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;
} }
static int QDECL Sys_StdinRead (struct vfsfile_s *file, void *buffer, int bytestoread)
void SSV_CheckFromMaster(void)
{ {
static char inbuffer[1024]; ssize_t r;
static int inbufsize;
#if defined(__linux__) && defined(_DEBUG) #if defined(__linux__) && defined(_DEBUG)
int fl = fcntl (STDIN_FILENO, F_GETFL, 0); int fl = fcntl (STDIN_FILENO, F_GETFL, 0);
if (!(fl & FNDELAY)) if (!(fl & FNDELAY))
@ -499,53 +494,28 @@ void SSV_CheckFromMaster(void)
} }
#endif #endif
for(;;) r = read(STDIN_FILENO, buffer, bytestoread);
if (r == 0 && bytestoread)
return -1; //eof
if (r < 0)
{ {
if(inbufsize >= 2) int e = errno;
{ if (e == EINTR || e == EAGAIN || e == EWOULDBLOCK)
unsigned short len = inbuffer[0] | (inbuffer[1]<<8); return 0;
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;
}
} }
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 #endif

View File

@ -460,66 +460,51 @@ void Sys_DestroyConditional(void *condv)
#endif #endif
#ifdef SUBSERVERS #ifdef SUBSERVERS
typedef struct slaveserver_s typedef struct
{ {
pubsubserver_t pub; vfsfile_t pub;
HANDLE inpipe; HANDLE inpipe;
HANDLE outpipe; HANDLE outpipe;
qbyte inbuffer[2048];
int inbufsize;
} winsubserver_t; } winsubserver_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
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)
{ {
winsubserver_t *s = (winsubserver_t*)file;
DWORD avail; 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)) if (!PeekNamedPipe(s->inpipe, NULL, 0, NULL, &avail, NULL))
return -1; //EOF
if (avail)
{ {
CloseHandle(s->inpipe); if (avail > bytestoread)
CloseHandle(s->outpipe); avail = bytestoread;
Con_Printf("%i:%s has died\n", s->pub.id, s->pub.name); if (ReadFile(s->inpipe, buffer, avail, &avail, NULL))
return -1; return avail;
}
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;
}
} }
return 0; 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 exename[256];
wchar_t curdir[256]; wchar_t curdir[256];
@ -560,8 +545,9 @@ pubsubserver_t *Sys_ForkServer(void)
CloseHandle(startinfo.hStdOutput); CloseHandle(startinfo.hStdOutput);
CloseHandle(startinfo.hStdInput); CloseHandle(startinfo.hStdInput);
ctx->pub.funcs.InstructSlave = Sys_InstructSlave; ctx->pub.ReadBytes = Sys_MSV_ReadBytes;
ctx->pub.funcs.SubServerRead = Sys_SubServerRead; ctx->pub.WriteBytes = Sys_MSV_WriteBytes;
ctx->pub.Close = Sys_MSV_Close;
return &ctx->pub; 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_12X10_HDR, GL_COMPRESSED_RGBA_ASTC_12x10_KHR);
glfmtb(PTI_ASTC_12X12_HDR, GL_COMPRESSED_RGBA_ASTC_12x12_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; uploadfmt_t encoding = mips->encoding;
qboolean compress; qboolean compress;
qboolean storage = true; qboolean storage = true;
unsigned int bb, bw, bh; unsigned int bb, bw, bh, bd;
int levels = 0, genlevels; int levels = 0, genlevels;
int ttype = (tex->flags & IF_TEXTYPEMASK)>>IF_TEXTYPESHIFT; 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); 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) switch(bb)
{ {
case 3: case 3:

View File

@ -2682,6 +2682,8 @@ int Font_LineBreaks(conchar_t *start, conchar_t *end, int maxpixelwidth, int max
for (;;) for (;;)
{ {
if (l >= end)
break;
n = Font_Decode(l, &codeflags, &codepoint); n = Font_Decode(l, &codeflags, &codepoint);
if (!(codeflags & CON_HIDDEN) && (codepoint != ' ')) if (!(codeflags & CON_HIDDEN) && (codepoint != ' '))
break; 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) for (; extofs < miptexsize; extofs += extsize)
{ {
size_t sz, w, h; size_t sz, w, h;
unsigned int bb,bw,bh; unsigned int bb,bw,bh,bd;
int mip; int mip;
qbyte *extdata = (void*)((qbyte*)mt+extofs); qbyte *extdata = (void*)((qbyte*)mt+extofs);
char *extfmt = (char*)(extdata+4); 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 //alternative textures are usually compressed
//this means we insist on a FULL mip chain //this means we insist on a FULL mip chain
//npot mips are explicitly round-down (but don't drop to 0 with non-square). //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); 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); 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) 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; int i,j;
float f; float f;
static int fbmodcount;
//if (r_lightstylescale.value > 2) //if (r_lightstylescale.value > 2)
@ -121,7 +122,12 @@ void R_AnimateLight (void)
i = (int)f; i = (int)f;
f -= i; //this can require updates at 1000 times a second.. Depends on your framerate of course 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; int v1, v2, vd;
if (!cl_lightstyle[j].length) if (!cl_lightstyle[j].length)
@ -148,6 +154,18 @@ void R_AnimateLight (void)
else else
d_lightstylevalue[j] = (v1*(1-f) + v2*(f))*22*r_lightstylescale.value; 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"); var = Cvar_Get(namebuf, valuebuf, CVAR_SHADERSYSTEM, "GLSL Variables");
if (var) if (var)
{ {
var->flags |= CVAR_SHADERSYSTEM;
if (srgb) if (srgb)
{ {
if (type == '4') if (type == '4')
@ -6459,7 +6460,7 @@ char *Shader_DefaultBSPWater(parsestate_t *ps, const char *shortname, char *buff
return ( return (
"{\n" "{\n"
"{\n" "{\n"
// "program defaultfill\n" //"program defaultfill\n"
"map $whiteimage\n" "map $whiteimage\n"
"rgbgen srgb $r_fastturbcolour\n" "rgbgen srgb $r_fastturbcolour\n"
"}\n" "}\n"
@ -6485,7 +6486,7 @@ char *Shader_DefaultBSPWater(parsestate_t *ps, const char *shortname, char *buff
"}\n" "}\n"
"surfaceparm hasdiffuse\n" "surfaceparm hasdiffuse\n"
"}\n" "}\n"
, explicitalpha?"":va("#ALPHA=%g",alpha), alpha, alpha); , (explicitalpha||alpha==1)?"":va("#ALPHA=%g",alpha), alpha, alpha);
return buffer; return buffer;
case 2: //refraction of the underwater surface, with a fresnel case 2: //refraction of the underwater surface, with a fresnel
return ( return (

View File

@ -4176,8 +4176,8 @@ qboolean Sh_StencilShadowsActive(void)
{ {
#if defined(RTLIGHTS) && !defined(SERVERONLY) #if defined(RTLIGHTS) && !defined(SERVERONLY)
//if shadowmapping is forced on all lights then we don't need special depth stuff //if shadowmapping is forced on all lights then we don't need special depth stuff
// if (r_shadow_shadowmapping.ival) if (r_shadow_shadowmapping.ival)
// return false; return false;
if (isDedicated) if (isDedicated)
return false; return false;
return (r_shadow_realtime_dlight.ival && r_shadow_realtime_dlight_shadows.ival) || 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 vec3 v_eyepos;"
"uniform vec4 w_fog[2];\n" "uniform vec4 w_fog[2];\n"
"#define w_fogcolour w_fog[0].rgb\n" "#define w_fogcolour w_fog[0].rgb\n"
"#ifdef FOG\n"
"#define w_fogalpha w_fog[0].a\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_fogdensity w_fog[1].x\n"
"#define w_fogdepthbias w_fog[1].y\n" "#define w_fogdepthbias w_fog[1].y\n"
"uniform vec4 w_user[16];\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" "return vec4(fog3(regularcolour.rgb), 1.0) * regularcolour.a;\n"
"}\n" "}\n"
"vec4 fog4additive(in vec4 regularcolour)" "vec4 fog4additive(in vec4 regularcolour)"
"{" "{" //fog function for additive blends
"float z = w_fogdensity * gl_FragCoord.z / gl_FragCoord.w;\n" "float z = w_fogdensity * gl_FragCoord.z / gl_FragCoord.w;\n"
"z = max(0.0,z-w_fogdepthbias);\n" "z = max(0.0,z-w_fogdepthbias);\n"
"#if #include \"cvar/r_fog_exp2\"\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" "return regularcolour * vec4(fac, fac, fac, 1.0);\n"
"}\n" "}\n"
"vec4 fog4blend(in vec4 regularcolour)" "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" "float z = w_fogdensity * gl_FragCoord.z / gl_FragCoord.w;\n"
"z = max(0.0,z-w_fogdepthbias);\n" "z = max(0.0,z-w_fogdepthbias);\n"
"#if #include \"cvar/r_fog_exp2\"\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" "return regularcolour * vec4(1.0, 1.0, 1.0, fac);\n"
"}\n" "}\n"
"#else\n" "#else\n"
"#define w_fogalpha 0.0\n"
/*don't use macros for this - mesa bugs out*/ /*don't use macros for this - mesa bugs out*/
"vec3 fog3(in vec3 regularcolour) { return regularcolour; }\n" "vec3 fog3(in vec3 regularcolour) { return regularcolour; }\n"
"vec3 fog3additive(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 gl_skyboxdist;
extern cvar_t r_fastsky; extern cvar_t r_fastsky;
extern cvar_t r_fastskycolour; extern cvar_t r_fastskycolour;
extern cvar_t r_skycloudalpha;
static shader_t *forcedsky; static shader_t *forcedsky;
static shader_t *skyboxface; 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) if (fmt & PTI_FULLMIPCHAIN)
{ //input is expected to make sense... { //input is expected to make sense...
qbyte *front, *back; qbyte *front, *back;
unsigned int bb, bw, bh; unsigned int bb, bw, bh, bd;
unsigned int w, h, y; unsigned int w, h, y;
fmt = fmt&~PTI_FULLMIPCHAIN; fmt = fmt&~PTI_FULLMIPCHAIN;
Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh); Image_BlockSizeForEncoding(fmt, &bb, &bw, &bh, &bd);
w = (width+bw-1)/bw; w = (width+bw-1)/bw;
h = (height+bh-1)/bh; h = (height+bh-1)/bh;
//d = (depth+bd-1)/bd;
back = BZ_Malloc(bb*w*2*h); back = BZ_Malloc(bb*w*2*h);
front = back + bb*w*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)[1] = g/(width*height);
((qbyte *)&transpix)[2] = b/(width*height); ((qbyte *)&transpix)[2] = b/(width*height);
((qbyte *)&transpix)[3] = 0; ((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 (i=0 ; i<height ; i++)
for (j=0 ; j<width ; j++) 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_12x10_KHR 0x93DC
#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD
#endif #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 #ifndef GL_ARB_pixel_buffer_object
#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB #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 =REFLECT reflect=1\n"
"!!samps =RIPPLEMAP ripplemap=2\n" "!!samps =RIPPLEMAP ripplemap=2\n"
"!!samps =DEPTH refractdepth=3\n" "!!samps =DEPTH refractdepth=3\n"
"!!permu FOG\n"
"#include \"sys/defs.h\"\n" "#include \"sys/defs.h\"\n"
@ -445,9 +446,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"}\n" "}\n"
"#endif\n" "#endif\n"
"#ifdef FRAGMENT_SHADER\n" "#ifdef FRAGMENT_SHADER\n"
"#ifdef ALPHA\n"
"#include \"sys/fog.h\"\n" "#include \"sys/fog.h\"\n"
"#endif\n"
"void main (void)\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 ts = texture2D(s_diffuse, ntc);\n"
"vec4 surf = fog4blend(vec4(ts.rgb, float(ALPHA)*ts.a));\n" "vec4 surf = fog4blend(vec4(ts.rgb, float(ALPHA)*ts.a));\n"
"refr = mix(refr, surf.rgb, surf.a);\n" "refr = mix(refr, surf.rgb, surf.a);\n"
"#else\n"
"refr = fog3(refr); \n"
"#endif\n" "#endif\n"
//done //done
@ -4159,7 +4160,8 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
#ifdef GLQUAKE #ifdef GLQUAKE
{QR_OPENGL, 110, "defaultsky", {QR_OPENGL, 110, "defaultsky",
"!!permu FOG\n" "!!permu FOG\n"
"!!samps 2\n" "!!samps base=0, cloud=1\n"
"!!cvardf r_skyfog=0.5\n"
"#include \"sys/fog.h\"\n" "#include \"sys/fog.h\"\n"
//regular sky shader for scrolling q1 skies //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.z *= 3.0;\n"
"dir.xy /= 0.5*length(dir);\n" "dir.xy /= 0.5*length(dir);\n"
"tccoord = (dir.xy + e_time*0.03125);\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" "tccoord = (dir.xy + e_time*0.0625);\n"
"vec4 clouds = texture2D(s_t1, tccoord);\n" "vec4 clouds = texture2D(s_cloud, tccoord);\n"
"gl_FragColor = vec4(fog3((solid.rgb*(1.0-clouds.a)) + (clouds.a*clouds.rgb)), 1.0);\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" "}\n"
"#endif\n" "#endif\n"
}, },
@ -4643,7 +4650,14 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"void main ()\n" "void main ()\n"
"{\n" "{\n"
"vec4 skybox = textureCube(s_reflectcube, pos);\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" "}\n"
"#endif\n" "#endif\n"
}, },
@ -5617,7 +5631,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"!!ver 100 450\n" "!!ver 100 450\n"
"!!permu TESS\n" "!!permu TESS\n"
"!!permu DELUXE\n" "!!permu DELUXE\n"
"!!permu FULLBRIGHT\n" "!!permu FULLBRIGHT //lumas rather than no lightmaps\n"
"!!permu FOG\n" "!!permu FOG\n"
"!!permu LIGHTSTYLED\n" "!!permu LIGHTSTYLED\n"
"!!permu BUMP\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. //diffuse gives us alpha, and prevents dlight from bugging out when there's no diffuse.
"!!samps =EIGHTBIT paletted 1\n" "!!samps =EIGHTBIT paletted 1\n"
"!!samps =SPECULAR specular\n" "!!samps =SPECULAR specular\n"
"!!samps lightmap\n" "!!samps !VERTEXLIT lightmap\n"
"!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3\n" "!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3\n"
"!!samps =DELUXE deluxemap\n" "!!samps =DELUXE deluxemap\n"
"!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3\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. //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. //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. //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" "vec2 lmsize = vec2(textureSize(s_lightmap0, 0));\n"
"#else\n" "#else\n"
"#define lmsize vec2(128.0,2048.0)\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", {QR_OPENGL, 110, "defaultwarp",
"!!ver 100 450\n" "!!ver 100 450\n"
"!!permu FOG\n" "!!permu FOG\n"
"!!cvarf r_wateralpha\n"
"!!samps diffuse lightmap\n" "!!samps diffuse lightmap\n"
"#include \"sys/defs.h\"\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. //this is expected to be moderately fast.
"#include \"sys/fog.h\"\n" "#include \"sys/fog.h\"\n"
"varying vec2 tc;\n" "varying vec2 tc;\n"
"#ifdef LIT\n" "#ifdef LIT\n"
"varying vec2 lm0;\n" "varying vec2 lm0;\n"
@ -7048,12 +7062,6 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"}\n" "}\n"
"#endif\n" "#endif\n"
"#ifdef FRAGMENT_SHADER\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" "void main ()\n"
"{\n" "{\n"
"vec2 ntc;\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" "ts *= (texture2D(s_lightmap, lm0) * e_lmscale).rgb;\n"
"#endif\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" "}\n"
"#endif\n" "#endif\n"
}, },

View File

@ -1116,15 +1116,86 @@ void QCBUILTIN PF_memsetval (pubprogfuncs_t *inst, struct globalvars_s *globals)
//#define GCTIMINGS //#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 #ifdef THREADEDGC
#include "quakedef.h" #include "quakedef.h"
struct qcgccontext_s struct qcgccontext_s
{ {
int done; 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! 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 tempstr_t **tempstrings;//so we don't get confused over temps added while marking
size_t memsize; 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) void PR_QCGC_Thread(void *ctx, void *data, size_t a, size_t b)
{ {
struct qcgccontext_s *gc = ctx; struct qcgccontext_s *gc = ctx;
unsigned int p, r_d; progfuncs_t *progfuncs = gc->progfuncs;
char *marked, *t; smallbool *marked;
unsigned int *str;
size_t numtemps = gc->numtemps;
#ifdef GCTIMINGS #ifdef GCTIMINGS
unsigned int r_l;
double starttime, markedtime, endtime; double starttime, markedtime, endtime;
starttime = Sys_DoubleTime(); starttime = Sys_DoubleTime();
#endif #endif
marked = PR_QCGC_Mark(gc->amem, gc->memsize, gc->maxtemps);
marked = malloc(sizeof(*marked) * numtemps);
memset(marked, 0, sizeof(*marked) * numtemps);
//mark everything the qc has access to, even if it isn't even a string!
//note that I did try specifically checking only data explicitly marked as a string type, but that was:
//a) a smidge slower (lots of extra loops and conditions I guess)
//b) doesn't work with pointers/structs (yes, we assume it'll all be aligned).
//c) both methods got the same number of false positives in my test (2, probably dead strunzoned references)
for (str = gc->amem, p = 0; p < gc->memsize; p+=sizeof(*str), str++)
{
if ((*str & STRING_SPECMASK) == STRING_TEMP)
{
unsigned int idx = *str &~ STRING_SPECMASK;
if (idx < numtemps)
marked[idx] = true;
}
}
#ifdef GCTIMINGS #ifdef GCTIMINGS
markedtime = Sys_DoubleTime(); markedtime = Sys_DoubleTime();
#endif #endif
gc->clearedtemps = PR_QCGC_Sweep(progfuncs, marked, gc->tempstrings, gc->maxtemps);
//sweep
#ifdef GCTIMINGS
r_l = 0;
#endif
r_d = 0;
for (p = 0; p < numtemps; p++)
{
if (marked[p])
{
#ifdef GCTIMINGS
r_l++;
#endif
}
else
break;
}
// prinst.nexttempstring = p;
for (; p < numtemps; p++)
{
if (marked[p])
{ //still live...
#ifdef GCTIMINGS
r_l++;
#endif
}
else if (gc->tempstrings[p])
{ //not marked, but was valid at the time our snapshot was taken
r_d++;
//FIXME: Security Race: its possible for a mod to do weird manipulations to access the tempstring while we're still freeing it, allowing it to read something outside of its sandbox.
//one option would be to have the main thread bounce it back to the worker after its complete, so we can actually free the memory only after main thread has acknowledged that its tempstrings are nulled.
gc->prinst.tempstrings[p] = NULL;
gc->externs->memfree(gc->tempstrings[p]);
}
}
gc->clearedtemps = r_d;
free(marked);
#ifdef GCTIMINGS #ifdef GCTIMINGS
endtime = Sys_DoubleTime(); 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 #endif
COM_InsertWork(WG_MAIN, PR_QCGC_Done, gc, NULL, 0, 0); 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) static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, unsigned int len)
{ {
progfuncs_t *fte_restrict progfuncs = (progfuncs_t *)ppf; progfuncs_t *progfuncs = (progfuncs_t *)ppf;
tempstr_t **ntable;
int newmax;
int i; int i;
if (!str) if (!str)
@ -1230,21 +1249,16 @@ static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str,
if (prinst.livetemps == prinst.maxtempstrings) 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. //need to wait for the gc to finish, otherwise it might be wiping freed strings that we're still using.
while (prinst.gccontext) while (prinst.gccontext)
{ {
COM_WorkerPartialSync(prinst.gccontext, &prinst.gccontext->done, false); COM_WorkerPartialSync(prinst.gccontext, &prinst.gccontext->done, false);
PR_RunGC(progfuncs); PR_RunGC(progfuncs);
} }
#endif
newmax = prinst.maxtempstrings*2 + 1024; PR_ExpandTempStrings(progfuncs, 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;
} }
for (i = prinst.nexttempstring; i < prinst.maxtempstrings && prinst.tempstrings[i]; i++) 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); 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) if (!prinst.gccontext)
#endif
{ {
if (prinst.livetemps < prinst.maxtempstrings/2 || prinst.nexttempstring < prinst.maxtempstrings/2) if (prinst.livetemps < prinst.maxtempstrings/2 || prinst.nexttempstring < prinst.maxtempstrings/2)
{ //don't bother yet { //don't bother yet
return false; return;
} }
else #ifdef THREADEDGC
if (externs->usethreadedgc)
{ {
#ifdef GCTIMINGS #ifdef GCTIMINGS
double starttime = Sys_DoubleTime(), endtime; double starttime = Sys_DoubleTime(), endtime;
@ -1286,18 +1303,30 @@ pbool PR_RunGC (progfuncs_t *progfuncs)
gc->memsize = prinst.addressableused; gc->memsize = prinst.addressableused;
memcpy(gc->amem, prinst.addressablehunk, 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); 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 #ifdef GCTIMINGS
endtime = Sys_DoubleTime(); endtime = Sys_DoubleTime();
gc->externs->Printf("preparetime=%f\n", (endtime - starttime)); gc->externs->Printf("preparetime=%f\n", (endtime - starttime));
#endif #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) else if (prinst.gccontext->done)
{ {
prinst.livetemps -= prinst.gccontext->clearedtemps; 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 over half the (max)strings are still live, just increase the max so we are not spamming collections
if (prinst.livetemps >= prinst.maxtempstrings/2) if (prinst.livetemps >= prinst.maxtempstrings/2)
{ PR_ExpandTempStrings(progfuncs, prinst.maxtempstrings * 2);
unsigned int newmax = prinst.maxtempstrings * 2;
tempstr_t **ntable = progfuncs->funcs.parms->memalloc(sizeof(char*) * newmax);
memcpy(ntable, prinst.tempstrings, sizeof(char*) * prinst.maxtempstrings);
memset(ntable+prinst.maxtempstrings, 0, sizeof(char*) * (newmax-prinst.maxtempstrings));
prinst.maxtempstrings = newmax;
if (prinst.tempstrings)
progfuncs->funcs.parms->memfree(prinst.tempstrings);
prinst.tempstrings = ntable;
}
return false;
} }
return true; //running... #endif
} }
static void PR_FreeAllTemps (progfuncs_t *progfuncs) static void PR_FreeAllTemps (progfuncs_t *progfuncs)
{ {
unsigned int i; unsigned int i;
#ifdef THREADEDGC
while (prinst.gccontext) while (prinst.gccontext)
{ {
COM_WorkerPartialSync(prinst.gccontext, &prinst.gccontext->done, false); COM_WorkerPartialSync(prinst.gccontext, &prinst.gccontext->done, false);
PR_RunGC(progfuncs); PR_RunGC(progfuncs);
} }
#endif
for (i = 0; i < prinst.maxtempstrings; i++) for (i = 0; i < prinst.maxtempstrings; i++)
{ {
externs->memfree(prinst.tempstrings[i]); externs->memfree(prinst.tempstrings[i]);
@ -1337,151 +1358,11 @@ static void PR_FreeAllTemps (progfuncs_t *progfuncs)
prinst.maxtempstrings = 0; prinst.maxtempstrings = 0;
prinst.nexttempstring = 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 #else
static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, unsigned int len) static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str, unsigned int len)
{ {
progfuncs_t *progfuncs = (progfuncs_t*)ppf; progfuncs_t *progfuncs = (progfuncs_t*)ppf;
tempstr_t **ntable; tempstr_t **ntable, *n;
int newmax; int newmax;
int i; int i;
@ -1505,9 +1386,10 @@ static string_t PDECL PR_AllocTempStringLen (pubprogfuncs_t *ppf, char **str,
prinst.numtempstrings++; prinst.numtempstrings++;
prinst.tempstrings[i] = progfuncs->funcs.parms->memalloc(sizeof(tempstr_t) - sizeof(((tempstr_t*)NULL)->value) + len); n = progfuncs->funcs.parms->memalloc(sizeof(tempstr_t) - sizeof(((tempstr_t*)NULL)->value) + len);
prinst.tempstrings[i]->size = len; n->size = len;
*str = prinst.tempstrings[i]->value; *str = n->value;
prinst.tempstrings[i] = n; //doesn't have its value yet...
return (string_t)((unsigned int)i | STRING_TEMP); 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 #define printf NULL //should be some null wrapper instead
#endif #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; //progfuncs_t *progfuncs = NULL;
#undef memfree #undef memfree
#undef prinst #undef prinst
@ -1853,17 +1688,13 @@ pubprogfuncs_t * PDECL InitProgs(progexterns_t *ext)
progfuncs_t *funcs; progfuncs_t *funcs;
if (!ext) if (!ext)
ext = &defexterns;
else
{ {
int i; static progexterns_t defexterns;
if (ext->progsversion > PROGSTRUCT_VERSION) ext = &defexterns;
return NULL; }
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 memalloc
#undef pr_progstate #undef pr_progstate
#undef pr_argc #undef pr_argc
@ -1875,6 +1706,24 @@ pubprogfuncs_t * PDECL InitProgs(progexterns_t *ext)
funcs->funcs.parms = 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(); SetEndian();
return &funcs->funcs; 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 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; char *s;
int i; int i;
ddef16_t *def16; ddef16_t *def16;
ddef32_t *def32; ddef32_t *def32, def32tmp;
void *val; void *val;
static char line[128]; static char line[128];
@ -900,41 +901,57 @@ char *PR_GlobalString (progfuncs_t *progfuncs, int ofs)
{ {
case PST_DEFAULT: case PST_DEFAULT:
case PST_KKQWSV: case PST_KKQWSV:
val = (void *)&pr_globals[ofs];
def16 = ED_GlobalAtOfs16(progfuncs, ofs); def16 = ED_GlobalAtOfs16(progfuncs, ofs);
if (!def16) if (def16)
sprintf (line,"%i(?""?""?)", ofs);
else
{ {
s = PR_ValueString (progfuncs, def16->type, val, false); def32 = &def32tmp;
sprintf (line,"%i(%s)%s", ofs, def16->s_name+progfuncs->funcs.stringtable, s); def32->ofs = def16->ofs;
def32->type = def16->type;
def32->s_name = def16->s_name;
} }
else
i = strlen(line); def32 = NULL;
for ( ; i<20 ; i++) break;
strcat (line," ");
strcat (line," ");
return line;
case PST_QTEST: case PST_QTEST:
case PST_FTE32: case PST_FTE32:
val = (void *)&pr_globals[ofs];
def32 = ED_GlobalAtOfs32(progfuncs, ofs); def32 = ED_GlobalAtOfs32(progfuncs, ofs);
if (!def32) break;
sprintf (line,"%i(?""?""?)", ofs); default:
else externs->Sys_Error("Bad struct type in PR_GlobalString");
{ return "";
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;
} }
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) 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) #if !defined(MINIMAL) && !defined(OMIT_QCC)
#define TYPEHINT(a) (pr_opcodes[op].type_##a)
if ( (unsigned)op < OP_NUMOPS) if ( (unsigned)op < OP_NUMOPS)
{ {
int i; int i;
@ -134,23 +135,27 @@ static void PR_PrintStatement (progfuncs_t *progfuncs, int statementnum)
#endif #endif
externs->Printf ("op%3i ", op); externs->Printf ("op%3i ", op);
#ifndef TYPEHINT
#define TYPEHINT(a) NULL
#endif
if (op == OP_IF_F || op == OP_IFNOT_F) 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) else if (op == OP_GOTO)
{ {
externs->Printf ("branch %i",arg[0]); externs->Printf ("branch %i",arg[0]);
} }
else if ( (unsigned)(op - OP_STORE_F) < 6) 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])); externs->Printf ("%s", PR_GlobalStringNoContents(progfuncs, arg[1]));
} }
else else
{ {
if (arg[0]) if (arg[0])
externs->Printf ("%s",PR_GlobalString(progfuncs, arg[0])); externs->Printf ("%s",PR_GlobalString(progfuncs, arg[0], TYPEHINT(a)));
if (arg[1]) if (arg[1])
externs->Printf ("%s",PR_GlobalString(progfuncs, arg[1])); externs->Printf ("%s",PR_GlobalString(progfuncs, arg[1], TYPEHINT(b)));
if (arg[2]) if (arg[2])
externs->Printf ("%s", PR_GlobalStringNoContents(progfuncs, 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) 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); outlen -= strlen(out);
out += strlen(out); out += strlen(out);
} }
else if (op == OP_GOTO) 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); outlen -= strlen(out);
out += strlen(out); out += strlen(out);
} }
else if ( (unsigned)(op - OP_STORE_F) < 6) 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); outlen -= strlen(out);
out += strlen(out); out += strlen(out);
QC_snprintfz (out, outlen, "%s", PR_GlobalStringNoContents(progfuncs, arg[1])); 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]) 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); outlen -= strlen(out);
out += strlen(out); out += strlen(out);
} }
if (arg[1]) 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); outlen -= strlen(out);
out += strlen(out); out += strlen(out);
} }

View File

@ -86,29 +86,28 @@ typedef struct
prclocks_t timestamp; prclocks_t timestamp;
} prstack_t; } prstack_t;
#if defined(QCGC) && defined(MULTITHREAD)
#define THREADEDGC
#endif
typedef struct typedef struct
{ {
unsigned int size; unsigned int size; //size of the data.
char value[4]; char value[4]; //contents of the tempstring (or really any binary data - but not tempstring references because we don't mark these!).
} tempstr_t; } tempstr_t;
#if defined(QCGC) && defined(MULTITHREAD)
// #define THREADEDGC
#endif
//FIXME: the defines hidden inside this structure are evil. //FIXME: the defines hidden inside this structure are evil.
typedef struct prinst_s typedef struct prinst_s
{ {
//temp strings are GCed, and can be created by engine, builtins, or just by ent parsing code. //temp strings are GCed, and can be created by engine, builtins, or just by ent parsing code.
tempstr_t **tempstrings; tempstr_t **tempstrings;
unsigned int maxtempstrings; unsigned int maxtempstrings;
#ifdef THREADEDGC #if defined(QCGC)
unsigned int nexttempstring; unsigned int nexttempstring;
unsigned int livetemps; //increased on alloc, decremented after sweep unsigned int livetemps; //increased on alloc, decremented after sweep
struct qcgccontext_s *gccontext; #ifdef THREADEDGC
#elif defined(QCGC) struct qcgccontext_s *gccontext;
unsigned int numtempstrings; #endif
unsigned int nexttempstring;
#else #else
unsigned int numtempstrings; unsigned int numtempstrings;
unsigned int numtempstringsstack; unsigned int numtempstringsstack;
@ -235,8 +234,8 @@ extern QCC_opcode_t pr_opcodes[]; // sized by initialization
#define min(a,b) ((a) < (b) ? (a) : (b)) #define min(a,b) ((a) < (b) ? (a) : (b))
#endif #endif
#define sv_num_edicts (*externs->sv_num_edicts) #define sv_num_edicts (*externs->num_edicts)
#define sv_edicts (*externs->sv_edicts) #define sv_edicts (*externs->edicts)
#define PR_DPrintf externs->DPrintf #define PR_DPrintf externs->DPrintf
//#define printf syntax error //#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); 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); 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); 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); 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 // 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); string_t PDECL PR_StringToProgs (pubprogfuncs_t *inst, const char *str);
const char *ASMCALL PR_StringToNative (pubprogfuncs_t *inst, string_t 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, struct QCC_type_s **typehint);
char *PR_GlobalString (progfuncs_t *progfuncs, int ofs);
char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs); char *PR_GlobalStringNoContents (progfuncs_t *progfuncs, int ofs);
pbool CompileFile(progfuncs_t *progfuncs, const char *filename); 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. 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. pbool usethreadedgc;
unsigned int *sv_num_edicts; //pointer to the engine's edict count.
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 int edictsize; //size of edict_t
void *user; /*contains the owner's world reference in FTE*/ 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.autocompile = PR_COMPILEIGNORE;//PR_COMPILECHANGED;//enum {PR_NOCOMPILE, PR_COMPILENEXIST, PR_COMPILECHANGED, PR_COMPILEALWAYS} autocompile;
svprogparms.gametime = &sv.time; svprogparms.gametime = &sv.time;
#ifdef MULTITHREAD
svprogparms.usethreadedgc = pr_gc_threaded.ival;
#endif
svprogparms.sv_edicts = (edict_t**)&sv.world.edicts; svprogparms.edicts = (edict_t**)&sv.world.edicts;
svprogparms.sv_num_edicts = &sv.world.num_edicts; svprogparms.num_edicts = &sv.world.num_edicts;
svprogparms.useeditor = QCEditor; 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'"}, {"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.")}, {"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 comfieldfloat(name,desc) {#name, ".float", FL, D(desc)},
#define comfieldint(name,desc) {#name, ".int", FL, D(desc)}, #define comfieldint(name,desc) {#name, ".int", FL, D(desc)},
#define comfieldvector(name,desc) {#name, ".vector", 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 #ifdef SUBSERVERS
//cluster stuff //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_UpdateAddresses(void);
void SSV_InitiatePlayerTransfer(client_t *cl, const char *newserver); void SSV_InitiatePlayerTransfer(client_t *cl, const char *newserver);
void SSV_InstructMaster(sizebuf_t *cmd); 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_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 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 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 #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. // 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). // 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 SUBSERVERS
#ifdef SQL #ifdef SQL
#include "sv_sql.h" #include "sv_sql.h"
#endif #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; extern cvar_t sv_serverip;
void VARGS SV_RejectMessage(enum serverprotocols_e protocol, char *format, ...); void VARGS SV_RejectMessage(enum serverprotocols_e protocol, char *format, ...);
@ -61,8 +90,51 @@ typedef struct {
static pubsubserver_t *subservers; static pubsubserver_t *subservers;
static link_t clusterplayers; static link_t clusterplayers;
qboolean isClusterSlave; qboolean isClusterSlave;
static vfsfile_t *controlconnection = NULL;
static unsigned int nextserverid; 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) static clusterplayer_t *MSV_FindPlayerId(unsigned int playerid)
{ {
link_t *l; link_t *l;
@ -116,6 +188,8 @@ static void MSV_ServerCrashed(pubsubserver_t *server)
} }
} }
if (server->stream)
VFS_CLOSE(server->stream);
Z_Free(server); Z_Free(server);
} }
@ -131,29 +205,6 @@ pubsubserver_t *MSV_FindSubServer(unsigned int id)
return NULL; 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) static void MSV_SendCvars(pubsubserver_t *s)
{ {
extern cvar_t skill, sv_nqplayerphysics, sv_pure, sv_minpitch, sv_maxpitch; 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_WriteByte(&send, ccmd_setcvar);
MSG_WriteString(&send, cvars[v]->name); MSG_WriteString(&send, cvars[v]->name);
MSG_WriteString(&send, cvars[v]->string); 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; sizebuf_t send;
char send_buf[1024]; char send_buf[1024];
if (!id) if (!id)
{ {
do id = ++nextserverid; while(MSV_FindSubServer(id)); do id = ++nextserverid; while(MSV_FindSubServer(id));
} }
s = Z_Malloc(sizeof(*s));
s->stream = stream;
s->id = id; s->id = id;
s->next = subservers; s->next = subservers;
subservers = s; 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_WriteByte(&send, ccmd_acceptserver);
MSG_WriteLong(&send, s->id); MSG_WriteLong(&send, s->id);
MSG_WriteString(&send, s->name); 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); pubsubserver_t *s = MSV_FindSubServer(svs.clusterserverid);
if (s) if (s)
return s; return s;
@ -223,27 +361,38 @@ pubsubserver_t *MSV_Loop_GetLocalServer(void)
msv_loop_to_ss = VFSPIPE_Open(1, false); msv_loop_to_ss = VFSPIPE_Open(1, false);
msv_loop_from_ss = VFSPIPE_Open(1, false); msv_loop_from_ss = VFSPIPE_Open(1, false);
s = Z_Malloc(sizeof(*s)); f = Z_Malloc(sizeof(*f));
s->funcs.InstructSlave = MSV_Loop_Instruct; f->ReadBytes = MSV_Loop_Read;
s->funcs.SubServerRead = 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)); Q_strncpyz(s->name, sv.mapname, sizeof(s->name));
svs.clusterserverid = s->id; svs.clusterserverid = s->id;
return s; return s;
} }
//called at startup to let us know the control connection to read/write
pubsubserver_t *MSV_StartSubServer(unsigned int id, const char *mapname) 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) if (s)
MSV_Link_Server(s, id, mapname); return MSV_Link_Server(s, id, mapname);
return s; return NULL;
} }
//server names documented at the start of this file //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; pubsubserver_t *s;
unsigned int id; unsigned int id;
@ -304,7 +453,7 @@ qboolean MSV_InstructSlave(unsigned int id, sizebuf_t *cmd)
if (!id) if (!id)
{ {
for (s = subservers; s; s = s->next) for (s = subservers; s; s = s->next)
s->funcs.InstructSlave(s, cmd); MSV_WriteSlave(s, cmd);
return subservers?true:false; return subservers?true:false;
} }
else else
@ -312,7 +461,7 @@ qboolean MSV_InstructSlave(unsigned int id, sizebuf_t *cmd)
s = MSV_FindSubServer(id); s = MSV_FindSubServer(id);
if (s) if (s)
{ {
s->funcs.InstructSlave(s, cmd); MSV_WriteSlave(s, cmd);
return true; return true;
} }
} }
@ -378,6 +527,10 @@ void SSV_PrintToMaster(char *s)
{ {
sizebuf_t send; sizebuf_t send;
char send_buf[8192]; char send_buf[8192];
static qboolean norecurse;
if (norecurse)
return;
memset(&send, 0, sizeof(send)); memset(&send, 0, sizeof(send));
send.data = send_buf; send.data = send_buf;
send.maxsize = sizeof(send_buf); send.maxsize = sizeof(send_buf);
@ -385,7 +538,9 @@ void SSV_PrintToMaster(char *s)
MSG_WriteByte(&send, ccmd_print); MSG_WriteByte(&send, ccmd_print);
MSG_WriteString(&send, s); MSG_WriteString(&send, s);
norecurse = true;
SSV_InstructMaster(&send); SSV_InstructMaster(&send);
norecurse = false;
} }
void MSV_Status(void) 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); Con_PrintCon(con, va("]%s\n", utf8line), PFS_FORCEUTF8|PFS_NONOTIFY);
if (*utf8line == '/')
utf8line++; //command, not text.
if (!strcmp(utf8line, "clear")) if (!strcmp(utf8line, "clear"))
{ {
Con_ClearCon(con); 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? MSG_WriteString(&buf, utf8line); //FIXME: is utf-8 a problem?
buf.data[0] = buf.cursize & 0xff; buf.data[0] = buf.cursize & 0xff;
buf.data[1] = (buf.cursize>>8) & 0xff; buf.data[1] = (buf.cursize>>8) & 0xff;
s->funcs.InstructSlave(s, &buf); MSV_WriteSlave(s, &buf);
} }
else else
Con_Footerf(con, false, "< Unable to send >"); Con_Footerf(con, false, "< Unable to send >");
@ -602,7 +760,7 @@ qboolean MSV_ForwardToAutoServer(void)
MSG_WriteString(&buf, args); MSG_WriteString(&buf, args);
buf.data[0] = buf.cursize & 0xff; buf.data[0] = buf.cursize & 0xff;
buf.data[1] = (buf.cursize>>8) & 0xff; buf.data[1] = (buf.cursize>>8) & 0xff;
s->funcs.InstructSlave(s, &buf); MSV_WriteSlave(s, &buf);
return true; return true;
} }
} }
@ -702,7 +860,7 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
MSG_WriteByte(&send, ccmd_transferedplayer); MSG_WriteByte(&send, ccmd_transferedplayer);
MSG_WriteLong(&send, s->id); MSG_WriteLong(&send, s->id);
MSG_WriteLong(&send, plid); MSG_WriteLong(&send, plid);
pl->server->funcs.InstructSlave(pl->server, &send); MSV_WriteSlave(pl->server, &send);
pl->server->activeplayers--; pl->server->activeplayers--;
} }
pl->server = s; pl->server = s;
@ -771,7 +929,7 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
MSG_WriteByte(&send, statsblobsize/4); MSG_WriteByte(&send, statsblobsize/4);
SZ_Write(&send, statsblob, statsblobsize&~3); SZ_Write(&send, statsblob, statsblobsize&~3);
s->funcs.InstructSlave(s, &send); MSV_WriteSlave(s, &send);
} }
break; break;
@ -808,7 +966,7 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
while(c--) while(c--)
MSG_WriteFloat(&send, MSG_ReadFloat()); MSG_WriteFloat(&send, MSG_ReadFloat());
toptr->funcs.InstructSlave(toptr, &send); MSV_WriteSlave(toptr, &send);
s->transferingplayers--; s->transferingplayers--;
toptr->transferingplayers++; toptr->transferingplayers++;
@ -827,7 +985,7 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
MSG_WriteLong(&send, plid); MSG_WriteLong(&send, plid);
MSG_WriteString(&send, ""); MSG_WriteString(&send, "");
s->funcs.InstructSlave(s, &send); MSV_WriteSlave(s, &send);
} }
} }
break; break;
@ -868,7 +1026,7 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
toptr = MSV_FindSubServer(to); toptr = MSV_FindSubServer(to);
if (toptr) if (toptr)
{ {
toptr->funcs.InstructSlave(toptr, &send); MSV_WriteSlave(toptr, &send);
toptr->transferingplayers++; toptr->transferingplayers++;
} }
} }
@ -937,21 +1095,21 @@ void MSV_ReadFromSubServer(pubsubserver_t *s)
if (!*dest) //broadcast if no dest if (!*dest) //broadcast if no dest
{ {
for (s = subservers; s; s = s->next) for (s = subservers; s; s = s->next)
s->funcs.InstructSlave(s, &send); MSV_WriteSlave(s, &send);
} }
else if (*dest == '\\') else if (*dest == '\\')
{ {
//send to a specific server (backslashes should not be valid in infostrings, and thus not in names. //send to a specific server (backslashes should not be valid in infostrings, and thus not in names.
//FIXME: broadcasting for now. //FIXME: broadcasting for now.
for (s = subservers; s; s = s->next) for (s = subservers; s; s = s->next)
s->funcs.InstructSlave(s, &send); MSV_WriteSlave(s, &send);
} }
else else
{ {
//send it to the server that the player is currently on. //send it to the server that the player is currently on.
clusterplayer_t *pl = MSV_FindPlayerName(dest); clusterplayer_t *pl = MSV_FindPlayerName(dest);
if (pl) if (pl)
pl->server->funcs.InstructSlave(pl->server, &send); MSV_WriteSlave(pl->server, &send);
else if (!pl && strncmp(cmd, "error:", 6)) else if (!pl && strncmp(cmd, "error:", 6))
{ {
//player not found. send it back to the sender, but add an error prefix. //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); SZ_Write(&send, "error:", 6);
MSG_WriteString(&send, cmd); MSG_WriteString(&send, cmd);
MSG_WriteString(&send, info); 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; 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; unsigned short size;
while (VFS_READ(msv_loop_to_ss, &size, sizeof(size))>0) while (VFS_READ(msv_loop_to_ss, &size, sizeof(size))>0)
@ -990,7 +1198,7 @@ void MSV_PollSlaves(void)
for (link = &subservers; (s=*link); ) for (link = &subservers; (s=*link); )
{ {
switch(s->funcs.SubServerRead(s)) switch(MSV_SubServerRead(s))
{ {
case -1: case -1:
//error - server is dead and needs to be freed. //error - server is dead and needs to be freed.
@ -1001,7 +1209,7 @@ void MSV_PollSlaves(void)
//no messages //no messages
link = &s->next; link = &s->next;
break; break;
case 1: default:
//got a message. read it and see if there's more. //got a message. read it and see if there's more.
MSV_ReadFromSubServer(s); MSV_ReadFromSubServer(s);
break; break;
@ -1015,8 +1223,10 @@ void SSV_InstructMaster(sizebuf_t *cmd)
cmd->data[1] = (cmd->cursize>>8) & 0xff; cmd->data[1] = (cmd->cursize>>8) & 0xff;
if (msv_loop_from_ss) if (msv_loop_from_ss)
VFS_WRITE(msv_loop_from_ss, cmd->data, cmd->cursize); VFS_WRITE(msv_loop_from_ss, cmd->data, cmd->cursize);
else else if (controlconnection)
Sys_InstructMaster(cmd); VFS_WRITE(controlconnection, cmd->data, cmd->cursize);
//FIXME: handle partial writes.
} }
void SSV_ReadFromControlServer(void) void SSV_ReadFromControlServer(void)
@ -1044,7 +1254,7 @@ void SSV_ReadFromControlServer(void)
{ {
cvar_t *var = Cvar_FindVar(MSG_ReadString()); cvar_t *var = Cvar_FindVar(MSG_ReadString());
const char *val = 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); Cvar_Set(var, val);
} }
break; break;
@ -1470,7 +1680,7 @@ qboolean MSV_ClusterLoginReply(netadr_t *legacyclientredirect, unsigned int serv
MSG_WriteByte(&send, statsblobsize/4); MSG_WriteByte(&send, statsblobsize/4);
SZ_Write(&send, statsblob, statsblobsize&~3); SZ_Write(&send, statsblob, statsblobsize&~3);
s->funcs.InstructSlave(s, &send); MSV_WriteSlave(s, &send);
if (serveraddr.type == NA_INVALID) if (serveraddr.type == NA_INVALID)
{ {

View File

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

View File

@ -2523,8 +2523,13 @@ qboolean SV_Physics (void)
int maxtics; int maxtics;
double trueframetime = host_frametime; double trueframetime = host_frametime;
double maxtic = sv_maxtic.value; double maxtic = sv_maxtic.value;
if (maxtic < sv_mintic.value) double mintic = sv_mintic.value;
maxtic = 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 //keep gravity tracking the cvar properly
movevars.gravity = sv_gravity.value; movevars.gravity = sv_gravity.value;
@ -2653,7 +2658,7 @@ qboolean SV_Physics (void)
sv.world.physicstime = sv.time; sv.world.physicstime = sv.time;
break; break;
} }
if (host_frametime <= 0 || host_frametime < sv_mintic.value) if (host_frametime <= 0 || host_frametime < mintic)
break; break;
if (host_frametime > maxtic) if (host_frametime > maxtic)
{ {

View File

@ -3609,16 +3609,28 @@ void SV_SendClientMessages (void)
} }
else else
{ {
extern cvar_t sv_nqplayerphysics;
if (c->nextservertimeupdate > pt + 0.1) if (c->nextservertimeupdate > pt + 0.1)
c->nextservertimeupdate = 0; c->nextservertimeupdate = 0;
c->netchan.nqunreliableonly = false; c->netchan.nqunreliableonly = false;
c->send_message = false; c->send_message = false;
//nq sends one packet only for each server physics frame //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; if (c->nextservertimeupdate < pt && c->state >= cs_connected)
c->nextservertimeupdate = pt + 1.0/77; {
c->send_message = true;
c->nextservertimeupdate = pt + 1.0/77;
}
} }
} }
} }

View File

@ -601,14 +601,6 @@ char *Sys_ConsoleInput (void)
static char text[256]; static char text[256];
int len; int len;
#ifdef SUBSERVERS
if (SSV_IsSubServer())
{
SSV_CheckFromMaster();
return NULL;
}
#endif
if (!stdin_ready || noconinput==true) if (!stdin_ready || noconinput==true)
return NULL; // the select didn't say it was ready return NULL; // the select didn't say it was ready
stdin_ready = false; 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 //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) if (protext2 & PEXT2_REPLACEMENTDELTAS)
{ {
Q_snprintfz (message, sizeof(message), "%c\n%s - "DISTRIBUTION" (FTENQ, %s) - %s", 2, gamedir, Q_snprintfz (message, sizeof(message), "%c\n"DISTRIBUTION" %s - %s - %s", 2,
build, mapname); build,gamedir, mapname);
} }
else 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":"", protoname,(protext1||(protext2&~PEXT2_VOICECHAT))?"+":"",(protext2&PEXT2_VOICECHAT)?"Voip":"",
build, mapname); build,gamedir, mapname);
} }
MSG_WriteByte (&host_client->netchan.message, svc_print); MSG_WriteByte (&host_client->netchan.message, svc_print);
MSG_WriteString (&host_client->netchan.message,message); MSG_WriteString (&host_client->netchan.message,message);

View File

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

View File

@ -1,5 +1,6 @@
!!permu FOG !!permu FOG
!!samps 2 !!samps base=0, cloud=1
!!cvardf r_skyfog=0.5
#include "sys/fog.h" #include "sys/fog.h"
//regular sky shader for scrolling q1 skies //regular sky shader for scrolling q1 skies
@ -24,9 +25,14 @@ void main ()
dir.z *= 3.0; dir.z *= 3.0;
dir.xy /= 0.5*length(dir); dir.xy /= 0.5*length(dir);
tccoord = (dir.xy + e_time*0.03125); 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); tccoord = (dir.xy + e_time*0.0625);
vec4 clouds = texture2D(s_t1, tccoord); vec4 clouds = texture2D(s_cloud, tccoord);
gl_FragColor = vec4(fog3((solid.rgb*(1.0-clouds.a)) + (clouds.a*clouds.rgb)), 1.0); 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 #endif

View File

@ -35,6 +35,13 @@ void main ()
void main () void main ()
{ {
vec4 skybox = textureCube(s_reflectcube, pos); 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 #endif

View File

@ -1,7 +1,7 @@
!!ver 100 450 !!ver 100 450
!!permu TESS !!permu TESS
!!permu DELUXE !!permu DELUXE
!!permu FULLBRIGHT !!permu FULLBRIGHT //lumas rather than no lightmaps
!!permu FOG !!permu FOG
!!permu LIGHTSTYLED !!permu LIGHTSTYLED
!!permu BUMP !!permu BUMP
@ -18,7 +18,7 @@
//diffuse gives us alpha, and prevents dlight from bugging out when there's no diffuse. //diffuse gives us alpha, and prevents dlight from bugging out when there's no diffuse.
!!samps =EIGHTBIT paletted 1 !!samps =EIGHTBIT paletted 1
!!samps =SPECULAR specular !!samps =SPECULAR specular
!!samps lightmap !!samps !VERTEXLIT lightmap
!!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3 !!samps =LIGHTSTYLED lightmap1 lightmap2 lightmap3
!!samps =DELUXE deluxemap !!samps =DELUXE deluxemap
!!samps =LIGHTSTYLED =DELUXE deluxemap1 deluxemap2 deluxemap3 !!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. //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. //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. //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)); vec2 lmsize = vec2(textureSize(s_lightmap0, 0));
#else #else
#define lmsize vec2(128.0,2048.0) #define lmsize vec2(128.0,2048.0)

View File

@ -1,6 +1,5 @@
!!ver 100 450 !!ver 100 450
!!permu FOG !!permu FOG
!!cvarf r_wateralpha
!!samps diffuse lightmap !!samps diffuse lightmap
#include "sys/defs.h" #include "sys/defs.h"
@ -9,6 +8,7 @@
//this is expected to be moderately fast. //this is expected to be moderately fast.
#include "sys/fog.h" #include "sys/fog.h"
varying vec2 tc; varying vec2 tc;
#ifdef LIT #ifdef LIT
varying vec2 lm0; varying vec2 lm0;
@ -27,12 +27,6 @@ void main ()
} }
#endif #endif
#ifdef FRAGMENT_SHADER #ifdef FRAGMENT_SHADER
#ifndef ALPHA
uniform float cvar_r_wateralpha;
#define USEALPHA cvar_r_wateralpha
#else
#define USEALPHA float(ALPHA)
#endif
void main () void main ()
{ {
vec2 ntc; vec2 ntc;
@ -44,6 +38,10 @@ void main ()
ts *= (texture2D(s_lightmap, lm0) * e_lmscale).rgb; ts *= (texture2D(s_lightmap, lm0) * e_lmscale).rgb;
#endif #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 #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; case PTI_ASTC_12X12_HDR: format = VK_FORMAT_ASTC_12x12_UNORM_BLOCK; break;
#endif #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 //depth formats
case PTI_DEPTH16: format = VK_FORMAT_D16_UNORM; break; case PTI_DEPTH16: format = VK_FORMAT_D16_UNORM; break;
case PTI_DEPTH24: format = VK_FORMAT_X8_D24_UNORM_PACK32; 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; VkCommandBuffer vkloadcmd;
vk_image_t target; vk_image_t target;
uint32_t i; uint32_t i;
uint32_t blockwidth, blockheight; uint32_t blockwidth, blockheight, blockdepth;
uint32_t blockbytes; uint32_t blockbytes;
uint32_t layers; uint32_t layers;
uint32_t mipcount = mips->mipcount; 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 = VK_FencedBegin(VK_TextureLoaded, sizeof(*fence));
fence->mips = mipcount; 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 blockswidth = (mips->mip[i].width+blockwidth-1) / blockwidth;
uint32_t blocksheight = (mips->mip[i].height+blockheight-1) / blockheight; 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.size += blockswidth*blocksheight*blocksdepth*blockbytes;
} }
bci.flags = 0; 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. //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 blockswidth = (mips->mip[i].width+blockwidth-1) / blockwidth;
uint32_t blocksheight = (mips->mip[i].height+blockheight-1) / blockheight; 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) if (mips->mip[i].data)
memcpy((char*)mapdata + bci.size, (char*)mips->mip[i].data, blockswidth*blockbytes*blocksheight*blocksdepth); 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; qbyte *fdata;
size_t fsize; size_t fsize;
int bb,bw,bh; int bb,bw,bh,bd;
qboolean canktx = false; qboolean canktx = false;
uploadfmt_t targfmt = args->newpixelformat; uploadfmt_t targfmt = args->newpixelformat;
int d,l, layers, r; int d,l, layers, r;
@ -605,10 +605,15 @@ static qboolean ImgTool_ConvertPixelFormat(struct opts_s *args, const char *inna
tmp.extrafree = NULL; tmp.extrafree = NULL;
tmp.mipcount = 1; 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); Q_snprintfz(command+strlen(command), sizeof(command)-strlen(command), " \"%s\" \"%s\"", raw, comp);
if (targfmt >= PTI_ASTC_FIRST && targfmt <= PTI_ASTC_LAST) 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) if (targfmt >= PTI_ASTC_4X4_SRGB && targfmt <= PTI_ASTC_12X12_SRGB)
Q_strncatz(command, " -srgb", sizeof(command)); Q_strncatz(command, " -srgb", sizeof(command));
if (targfmt >= PTI_ASTC_4X4_HDR && targfmt <= PTI_ASTC_12X12_HDR) 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); // 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++) for (m = 0; m < mips->mipcount; m++)
{ {
qbyte *srcdata = mips->mip[m].data; 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) if (mips->mipcount && targfmt >= PTI_BC1_RGB && targfmt <= PTI_BC7_RGBA_SRGB)
{ //d3d has some annoying limitations. { //d3d has some annoying limitations.
//do not warn for astc files, their block sizes are too weird. //do not warn for astc files, their block sizes are too weird.
Image_BlockSizeForEncoding(targfmt, &bb, &bw, &bh); Image_BlockSizeForEncoding(targfmt, &bb, &bw, &bh, &bd);
if (mips->mip[0].width%bw || mips->mip[0].height%bh) 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); 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) if (facetype[i] < 0)
{ //flip to match legacy skyboxes { //flip to match legacy skyboxes
unsigned bb,bw,bh; unsigned bb,bw,bh,bd;
srcs[i] = tmpsrcs[-facetype[i]-1]; srcs[i] = tmpsrcs[-facetype[i]-1];
t = srcs[i].in; t = srcs[i].in;
Image_BlockSizeForEncoding(t->encoding, &bb,&bw,&bh); Image_BlockSizeForEncoding(t->encoding, &bb,&bw,&bh,&bd);
if (bw == 1 && bh == 1) if (bw == 1 && bh == 1 && bd == 1)
{ {
for (j = 0; j < t->mipcount; j++) for (j = 0; j < t->mipcount; j++)
{ {
@ -1071,7 +1076,7 @@ static void ImgTool_Convert(struct opts_s *args, struct pendingtextureinfo *in,
#endif #endif
else else
{ {
int bb,bw,bh; int bb,bw,bh,bd;
if (in->type != PTI_2D) if (in->type != PTI_2D)
Con_Printf("%s: Unable to write %s file to 2d image format\n", outname, imagetypename[in->type]); 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; 0;
if (!outformats[in->encoding]) if (!outformats[in->encoding])
Image_ChangeFormat(in, outformats, PTI_INVALID, outname); 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)) 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 #endif
Con_Printf("%s(%s): Write failed\n", outname, Image_FormatName(in->encoding)); 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; 0;
if (!outformats[in->encoding]) if (!outformats[in->encoding])
Image_ChangeFormat(in, outformats, PTI_INVALID, outname); 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)) 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)); 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; 0;
if (!outformats[in->encoding]) if (!outformats[in->encoding])
Image_ChangeFormat(in, outformats, PTI_INVALID, outname); 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)) 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)); 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)); struct pendingtextureinfo *out = Z_Malloc(sizeof(*out));
qbyte *newdata = NULL; qbyte *newdata = NULL;
int neww=0, newh=0, sz; int neww=0, newh=0, sz;
unsigned int bw,bh,bb; unsigned int bw,bh,bb,bd;
out->type = PTI_2D; out->type = PTI_2D;
out->encoding = PTI_INVALID; 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. if (out->encoding != PTI_INVALID) //use the first format we support, allowing prioritisation.
continue; 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); w = data[ 8] | (data[ 9]<<8) | (data[10]<<16) | (data[11]<<24);
h = data[12] | (data[13]<<8) | (data[14]<<16) | (data[15]<<24); h = data[12] | (data[13]<<8) | (data[14]<<16) | (data[15]<<24);
for (csz = 16; w || h; w>>=1, h>>=1) 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. //only use our if there were no corrupt sections.
if (data == dataend && newdata && neww && newh) 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) for (out->mipcount = 0; out->mipcount < countof(out->mip) && neww && newh; out->mipcount++, neww>>=1, newh>>=1)
{ {
neww = max(1, neww); 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 = bb;
out->mip[out->mipcount].datasize *= (out->mip[out->mipcount].width + bw-1)/bw; 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].height + bh-1)/bh;
out->mip[out->mipcount].datasize *= (out->mip[out->mipcount].depth + bd-1)/bd;
out->mip[out->mipcount].data = newdata; out->mip[out->mipcount].data = newdata;
newdata += out->mip[out->mipcount].datasize; 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) if (args->width && args->height && in->mipcount >= 1)
{ {
qbyte *newimg; qbyte *newimg;
unsigned int bb, bw, bh; unsigned int bb, bw, bh, bd;
Image_BlockSizeForEncoding(in->encoding, &bb, &bw, &bh); 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); newimg = Image_ResampleTexture(in->encoding, in->mip[0].data, in->mip[0].width, in->mip[0].height, NULL, args->width, args->height);
if (newimg) 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].needfree = true;
in->mip[0].width = args->width; in->mip[0].width = args->width;
in->mip[0].height = args->height; 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); Image_GenerateMips(in, args->flags);
} }
@ -2172,18 +2179,18 @@ showhelp:
Con_Printf("Supported compressed/interesting pixelformats are:\n"); Con_Printf("Supported compressed/interesting pixelformats are:\n");
for (f = 0; f < PTI_MAX; f++) for (f = 0; f < PTI_MAX; f++)
{ {
int bb,bw,bh; int bb,bw,bh,bd;
Image_BlockSizeForEncoding(f, &bb,&bw,&bh); Image_BlockSizeForEncoding(f, &bb,&bw,&bh,&bd);
if (f >= PTI_ASTC_FIRST && f <= PTI_ASTC_LAST) if (f >= PTI_ASTC_FIRST && f <= PTI_ASTC_LAST)
{ {
if (f >= PTI_ASTC_4X4_SRGB) if (f >= PTI_ASTC_4X4_SRGB)
continue; 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) 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) 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 || else if ( f==PTI_RGBA16F ||
f==PTI_RGBA32F || f==PTI_RGBA32F ||
f==PTI_E5BGR9 || f==PTI_E5BGR9 ||
@ -2202,9 +2209,9 @@ showhelp:
f==PTI_L8 || f==PTI_L8 ||
f==PTI_L8A8 || f==PTI_L8A8 ||
0) 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 // 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; return EXIT_SUCCESS;
} }

View File

@ -28,13 +28,13 @@ struct avaudioctx
//output audio //output audio
//we throw away data if the format changes. which is awkward, but gah. //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_channels;
int samples_speed; int samples_speed;
int samples_width; qaudiofmt_t samples_format;
qbyte *samples_buffer; qbyte *samples_buffer;
size_t samples_count; size_t samples_framecount;
size_t samples_max; size_t samples_maxbytes;
}; };
static void S_AV_Purge(sfx_t *s) static void S_AV_Purge(sfx_t *s)
@ -61,127 +61,213 @@ static void S_AV_Purge(sfx_t *s)
activedecoders--; activedecoders--;
memset(&s->decoder, 0, sizeof(s->decoder)); 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) 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. { //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 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); 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) switch(ctx->pACodecCtx->sample_fmt)
{ //we don't support planar audio. we just treat it as mono instead. { //we don't support planar audio. we just treat it as mono instead.
default: default:
auddatasize = 0; auddatasize = 0;
break; break;
case AV_SAMPLE_FMT_U8P: case AV_SAMPLE_FMT_U8P:
auddatasize /= channels; planes = channels;
channels = 1; outformat = QAF_S8;
informat = QAF_U8;
break;
case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8:
width = 1; planes = 1;
outformat = QAF_S8;
informat = QAF_U8;
break; break;
case AV_SAMPLE_FMT_S16P: case AV_SAMPLE_FMT_S16P:
auddatasize /= channels; planes = channels;
channels = 1; outformat = QAF_S16;
informat = QAF_S16;
break;
case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S16:
width = 2; planes = 1;
outformat = QAF_S16;
informat = QAF_S16;
break; 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: case AV_SAMPLE_FMT_FLTP:
//FIXME: support float audio internally. planes = channels;
{ outformat = QAF_F32;
float *in[2] = {(float*)ctx->pAFrame->data[0],(float*)ctx->pAFrame->data[1]}; informat = QAF_F32;
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;
}
break; break;
case AV_SAMPLE_FMT_FLT: case AV_SAMPLE_FMT_FLT:
//FIXME: support float audio internally. planes = 1;
{ outformat = QAF_F32;
float *in = (void*)auddata; informat = QAF_F32;
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;
}
break; break;
case AV_SAMPLE_FMT_DBLP: case AV_SAMPLE_FMT_DBLP:
auddatasize /= channels; planes = channels;
channels = 1; outformat = QAF_F32;
case AV_SAMPLE_FMT_DBL: informat = QAF_F64;
{
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;
}
break; 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 { //something changed, update
ctx->samples_channels = channels; ctx->samples_channels = channels;
ctx->samples_speed = ctx->pACodecCtx->sample_rate; ctx->samples_speed = ctx->pACodecCtx->sample_rate;
ctx->samples_width = width; ctx->samples_format = outformat;
//and discard any decoded audio. this might loose some. //and discard any decoded audio. this might loose some.
ctx->samples_start += ctx->samples_count; ctx->samples_framestart += ctx->samples_framecount;
ctx->samples_count = 0; 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_maxbytes = (ctx->samples_framecount*QAF_BYTES(ctx->samples_format)*ctx->samples_channels)+auddatasize;
ctx->samples_max *= 2; //slop ctx->samples_maxbytes *= 2; //slop
ctx->samples_buffer = realloc(ctx->samples_buffer, ctx->samples_max); ctx->samples_buffer = realloc(ctx->samples_buffer, ctx->samples_maxbytes);
}
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;
} }
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 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) 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. { //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) while (1)
{ {
if (start < ctx->samples_start) if (start < ctx->samples_framestart)
break; //o.O rewind! break; //o.O rewind!
if (ctx->samples_start+ctx->samples_count > curtime) if (ctx->samples_framestart+ctx->samples_framecount > curtime)
break; //no need yet. break; //no need yet.
#ifdef HAVE_DECOUPLED_API #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); av_packet_unref(&packet);
} }
buf->length = ctx->samples_count; buf->length = ctx->samples_framecount;
buf->speed = ctx->samples_speed; buf->speed = ctx->samples_speed;
buf->width = ctx->samples_width; buf->format = ctx->samples_format;
buf->numchannels = ctx->samples_channels; buf->numchannels = ctx->samples_channels;
buf->soundoffset = ctx->samples_start; buf->soundoffset = ctx->samples_framestart;
buf->data = ctx->samples_buffer; buf->data = ctx->samples_buffer;
//if we couldn't return any new data, then we're at an eof, return NULL to signal that. //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->length = 0;
buf->numchannels = ctx->samples_channels; buf->numchannels = ctx->samples_channels;
buf->speed = ctx->samples_speed; buf->speed = ctx->samples_speed;
buf->width = ctx->samples_width; buf->format = ctx->samples_format;
} }
return ctx->pFormatCtx->duration / (float)AV_TIME_BASE; 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 { //pos3, quat4, scale3
float posquatscale[10]; //raw values, used to calibrate ranges float posquatscale[10]; //raw values, used to calibrate ranges
} *posedata = NULL, *pd; //per bone*joint } *posedata = NULL, *pd; //per bone*joint
avec4_t *ivert; vecV_t *ivert;
vec2_t *ist; vec2_t *ist;
vec3_t *overt; vec3_t *overt;
vec3_t *onorm = NULL; vec3_t *onorm = NULL;

View File

@ -16,7 +16,10 @@ vector mousefar;
float releasedmouse; float releasedmouse;
//#define WALLBROWSERS
#ifdef WALLBROWSERS
string pointedshadername; string pointedshadername;
#endif
vector pointedsurfacenormal; vector pointedsurfacenormal;
entity pointedent; entity pointedent;
float pointedsurface; float pointedsurface;
@ -73,7 +76,7 @@ void(vector mouse) editor_options_overlay =
else else
txt = strcat(txt, " (OFF)"); 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; pos_y += 8;
} }
}; };
@ -382,6 +385,7 @@ float (float event, float parama, float paramb) wrap_InputEvent =
return TRUE; return TRUE;
} }
} }
#ifdef WALLBROWSERS
else if (pointedshadername != "") else if (pointedshadername != "")
{ {
/* if (parama == K_MOUSE2) /* if (parama == K_MOUSE2)
@ -399,6 +403,7 @@ float (float event, float parama, float paramb) wrap_InputEvent =
return TRUE; return TRUE;
} }
} }
#endif
if (orig_input_event) if (orig_input_event)
return orig_input_event(event, parama, paramb); 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 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; input_buttons = 0;
#ifdef WALLBROWSERS
if ((input_buttons & 1) && pointedshadername == "") if ((input_buttons & 1) && pointedshadername == "")
{ {
t = mousefar; t = mousefar;
@ -584,6 +590,7 @@ void() CSQC_Input_Frame =
// pointparticles(particleeffectnum("te_spike"), xyz1 + dir1*f1 + dir2*f2, trace_plane_normal, 1); // pointparticles(particleeffectnum("te_spike"), xyz1 + dir1*f1 + dir2*f2, trace_plane_normal, 1);
} }
} }
#endif
}; };
/* /*