1
0
Fork 0
forked from fte/fteqw

Fix various compiler warnings.

Added sv_guidkey cvar, allowing cross-server guid key generation (although it lacks auth).
Support .ico, because we can.
preliminary support for sdl 2.0.6's vulkan stuff. will wait till its actually released before its properly used.
Fix capturedemo.
videomap should typically use premultiplied alpha, apparently.
Updated sound drivers. No more old drivers. Better cvar registration. More drivers optionally support float output.
Added certificate log for dtls connections.
Rewrote font char cache, now supports full unicode char range, not just ucs-2. Attempt to support FreeType 2.5+ rgba fonts.
XMPP now supports carbons, and shows avatars in conversations. Updated xmpp's scram auth to be more strict, including the plus variation (hopefully), to block evil tls proxies.
ffmpeg plugin now uses the decoupled api for decoding too.
Cef plugin updated to support fte-scheme post data properly, as well as request/response headers (like cross-origin).

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5148 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2017-09-20 11:27:13 +00:00
parent ce1f4f1b28
commit 0c8ad17f7c
99 changed files with 6612 additions and 3860 deletions

View file

@ -15,7 +15,10 @@ else
BASE_DIR:=$(realpath .) BASE_DIR:=$(realpath .)
endif endif
ifeq ($(SVNREVISION),)
SVNREVISION:=-DSVNREVISION=$(shell test -d $(BASE_DIR)/../.svn && svnversion $(BASE_DIR) || echo -) SVNREVISION:=-DSVNREVISION=$(shell test -d $(BASE_DIR)/../.svn && svnversion $(BASE_DIR) || echo -)
endif
MAKE:=$(MAKE) SVNREVISION=$(SVNREVISION)
WHOAMI:=$(shell whoami) WHOAMI:=$(shell whoami)
@ -875,11 +878,13 @@ GLCL_EXE_NAME=../$(EXE_NAME)cl-gl$(FTE_FULLTARGET)
#SDLCONFIG:=libs/sdl2_mingw/$(CC_MACHINE)/bin/sdl2-config --prefix=libs/sdl2_mingw/$(CC_MACHINE) #SDLCONFIG:=libs/sdl2_mingw/$(CC_MACHINE)/bin/sdl2-config --prefix=libs/sdl2_mingw/$(CC_MACHINE)
ifdef windir ifdef windir
GL_LDFLAGS=$(GLLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs` GL_LDFLAGS=$(GLLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`
VK_LDFLAGS=$(VKLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`
M_LDFLAGS=$(MLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs` M_LDFLAGS=$(MLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`
SV_LDFLAGS=`$(SDLCONFIG) --static-libs` SV_LDFLAGS=`$(SDLCONFIG) --static-libs`
else else
GL_LDFLAGS=$(GLLDFLAGS) $(IMAGELDFLAGS) $(OGGVORBISLDFLAGS) `$(SDLCONFIG) --static-libs` GL_LDFLAGS=$(GLLDFLAGS) $(IMAGELDFLAGS) $(OGGVORBISLDFLAGS) `$(SDLCONFIG) --static-libs`
M_LDFLAGS=$(MLDFLAGS) `$(SDLCONFIG) --static-libs` $(IMAGELDFLAGS) $(OGGVORBISLDFLAGS) VK_LDFLAGS=$(VKLDFLAGS) $(IMAGELDFLAGS) $(OGGVORBISLDFLAGS) `$(SDLCONFIG) --static-libs`
M_LDFLAGS=$(MLDFLAGS) $(IMAGELDFLAGS) $(OGGVORBISLDFLAGS) `$(SDLCONFIG) --static-libs`
SV_LDFLAGS=`$(SDLCONFIG) --static-libs` SV_LDFLAGS=`$(SDLCONFIG) --static-libs`
endif endif
GL_CFLAGS=-DFTE_SDL $(GLCFLAGS) `$(SDLCONFIG) --cflags` GL_CFLAGS=-DFTE_SDL $(GLCFLAGS) `$(SDLCONFIG) --cflags`
@ -887,6 +892,9 @@ GLB_DIR=gl_$(FTE_FULLTARGET)
GLCL_DIR=glcl_$(FTE_FULLTARGET) GLCL_DIR=glcl_$(FTE_FULLTARGET)
SV_DIR?=sv_$(FTE_FULLTARGET) SV_DIR?=sv_$(FTE_FULLTARGET)
VKCL_OBJS=$(VKQUAKE_OBJS) $(BOTLIB_OBJS) $(SPEEX_OBJS) snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(D3DGL_OBJS) $(LTO_END)
VK_CFLAGS=-DFTE_SDL $(VKCFLAGS) `$(SDLCONFIG) --cflags`
SV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) $(SERVERONLY_OBJS) $(BOTLIB_OBJS) SV_OBJS=$(COMMON_OBJS) $(SERVER_OBJS) $(PROGS_OBJS) $(SERVERONLY_OBJS) $(BOTLIB_OBJS)
SV_EXE_NAME=../$(EXE_NAME)-sv$(FTE_FULLTARGET) SV_EXE_NAME=../$(EXE_NAME)-sv$(FTE_FULLTARGET)
SV_CFLAGS=-DFTE_SDL `$(SDLCONFIG) --cflags` $(SERVER_ONLY_CFLAGS) SV_CFLAGS=-DFTE_SDL `$(SDLCONFIG) --cflags` $(SERVER_ONLY_CFLAGS)
@ -904,6 +912,7 @@ QCC_DIR=qcc$(BITS)
ifeq (,$(findstring NO_ZLIB,$(CFLAGS))) ifeq (,$(findstring NO_ZLIB,$(CFLAGS)))
SV_LDFLAGS+=-lz SV_LDFLAGS+=-lz
GL_LDFLAGS+=-lz GL_LDFLAGS+=-lz
VK_LDFLAGS+=-lz
M_LDFLAGS+=-lz M_LDFLAGS+=-lz
QCC_LDFLAGS+=-L$(ARCHLIBS) -lz QCC_LDFLAGS+=-L$(ARCHLIBS) -lz
endif endif
@ -998,20 +1007,19 @@ ifeq (win_SDL,$(findstring win,$(FTE_TARGET))$(findstring _SDL,$(FTE_TARGET)))
GLCL_EXE_NAME=../$(EXE_NAME)-sdl-glcl$(BITS)$(EXEPOSTFIX) GLCL_EXE_NAME=../$(EXE_NAME)-sdl-glcl$(BITS)$(EXEPOSTFIX)
ifdef windir ifdef windir
GL_LDFLAGS=$(GLLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs` GL_LDFLAGS=$(GLLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`
VK_LDFLAGS=$(GLLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`
M_LDFLAGS=$(MLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs` M_LDFLAGS=$(MLDFLAGS) -lmingw32 -lws2_32 `$(SDLCONFIG) --static-libs`
SV_LDFLAGS=$(MINGW_LIBS_DIR)/libz.a -lm -lmingw32 -lws2_32 -lwinmm `$(SDLCONFIG) --static-libs` SV_LDFLAGS=$(MINGW_LIBS_DIR)/libz.a -lm -lmingw32 -lws2_32 -lwinmm `$(SDLCONFIG) --static-libs`
QCC_LDFLAGS=$(MINGW_LIBS_DIR)/libz.a QCC_LDFLAGS=$(MINGW_LIBS_DIR)/libz.a
else else
GL_LDFLAGS=$(IMAGELDFLAGS) $(OGGVORBISLDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32 $(GLLDFLAGS) `$(SDLCONFIG) --libs` GL_LDFLAGS=$(IMAGELDFLAGS) $(OGGVORBISLDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32 $(GLLDFLAGS) `$(SDLCONFIG) --libs`
M_LDFLAGS=$(IMAGELDFLAGS) $(OGGVORBISLDFLAGS) -lws2_32 -lmingw32 -mwindows -ldxguid -lwinmm -lole32 $(MLDFLAGS) `$(SDL_CONFIG) --libs` $(IMAGELDFLAGS) VK_LDFLAGS=$(IMAGELDFLAGS) $(OGGVORBISLDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32 $(GLLDFLAGS) `$(SDLCONFIG) --libs`
M_LDFLAGS=$(IMAGELDFLAGS) $(OGGVORBISLDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32 $(MLDFLAGS) `$(SDLCONFIG) --libs`
SV_LDFLAGS=$(MINGW_LIBS_DIR)/libz.a -lm -lmingw32 -lws2_32 -lwinmm `$(SDLCONFIG) --libs` SV_LDFLAGS=$(MINGW_LIBS_DIR)/libz.a -lm -lmingw32 -lws2_32 -lwinmm `$(SDLCONFIG) --libs`
QCC_LDFLAGS=$(MINGW_LIBS_DIR)/libz.a QCC_LDFLAGS=$(MINGW_LIBS_DIR)/libz.a
endif endif
GL_CFLAGS=-DFTE_SDL -I$(MINGW_LIBS_DIR)/ -I$(MINGW_LIBS_DIR) -I$(LIBS_DIR) $(GLCFLAGS) $(LIBVORBISFILE_STATIC) $(DX7SDK) $(SPEEXCFLAGS) GL_CFLAGS=-DFTE_SDL -I$(MINGW_LIBS_DIR)/ -I$(MINGW_LIBS_DIR) -I$(LIBS_DIR) $(GLCFLAGS) $(LIBVORBISFILE_STATIC) $(DX7SDK) $(SPEEXCFLAGS)
ifeq ($(shell echo $(FTE_TARGET)|grep -E -i -v "win32.*sdl"),)
GL_CFLAGS+= -D_MINGW_VFPRINTF
endif
GLB_DIR=gl_mgw_sdl$(BITS) GLB_DIR=gl_mgw_sdl$(BITS)
GLCL_DIR=glcl_mgw_sdl$(BITS) GLCL_DIR=glcl_mgw_sdl$(BITS)
@ -1025,39 +1033,35 @@ ifeq (win_SDL,$(findstring win,$(FTE_TARGET))$(findstring _SDL,$(FTE_TARGET)))
MB_DIR=m_mgw_sdl$(BITS) MB_DIR=m_mgw_sdl$(BITS)
M_EXE_NAME=../$(EXE_NAME)-sdl$(BITS)$(EXEPOSTFIX) M_EXE_NAME=../$(EXE_NAME)-sdl$(BITS)$(EXEPOSTFIX)
MCL_OBJS=$(D3DGL_OBJS) $(GLQUAKE_OBJS) $(SOFTWARE_OBJS) $(D3DQUAKE_OBJS) $(BOTLIB_OBJS) $(SPEEX_OBJS) gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(LTO_END) resources.o $(LTO_START) #with d3d...
M_CFLAGS=$(D3DCFLAGS) -DFTE_SDL -I$(LIBS_DIR) -I$(MINGW_LIBS_DIR)/ -I$(MINGW_LIBS_DIR) $(GLCFLAGS) $(LIBVORBISFILE_STATIC) -D_MERGED_SDL $(DX7SDK) $(SPEEXCFLAGS) #MCL_OBJS=$(D3DGL_OBJS) $(GLQUAKE_OBJS) $(SOFTWARE_OBJS) $(D3DQUAKE_OBJS) $(BOTLIB_OBJS) $(SPEEX_OBJS) gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(LTO_END) resources.o $(LTO_START)
#M_CFLAGS=$(D3DCFLAGS) $(VKCFLAGS) $(GLCFLAGS) -DFTE_SDL -I$(LIBS_DIR) -I$(LIBS_DIR) -I$(MINGW_LIBS_DIR)/ -I$(MINGW_LIBS_DIR) $(LIBVORBISFILE_STATIC) $(DX7SDK) $(SPEEXCFLAGS)
ifeq ($(shell echo $(FTE_TARGET)|grep -E -i -v "win32.*sdl"),) #without d3d...
M_CFLAGS+= -D_MINGW_VFPRINTF MCL_OBJS=$(D3DGL_OBJS) $(GLQUAKE_OBJS) $(SOFTWARE_OBJS) $(BOTLIB_OBJS) $(SPEEX_OBJS) gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(LTO_END) resources.o $(LTO_START)
endif M_CFLAGS=$(VKCFLAGS) $(GLCFLAGS) -DFTE_SDL -I$(LIBS_DIR) -I$(LIBS_DIR) -I$(MINGW_LIBS_DIR)/ -I$(MINGW_LIBS_DIR) $(LIBVORBISFILE_STATIC) $(DX7SDK) $(SPEEXCFLAGS)
M_LDFLAGS=$(GLLDFLAGS) $(IMAGELDFLAGS) $(OGGVORBISLDFLAGS)
D3DCL_OBJS=$(D3DQUAKE_OBJS) $(BOTLIB_OBJS) $(SPEEX_OBJS) snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(D3DGL_OBJS) $(LTO_END) resources.o $(LTO_START) D3DCL_OBJS=$(D3DQUAKE_OBJS) $(BOTLIB_OBJS) $(SPEEX_OBJS) snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(D3DGL_OBJS) $(LTO_END) resources.o $(LTO_START)
D3D_EXE_NAME=../$(EXE_NAME)-sdl-d3d$(BITS)$(EXEPOSTFIX) D3D_EXE_NAME=../$(EXE_NAME)-sdl-d3d$(BITS)$(EXEPOSTFIX)
D3DCL_EXE_NAME=../$(EXE_NAME)-sdl-d3dcl$(BITS)$(EXEPOSTFIX) D3DCL_EXE_NAME=../$(EXE_NAME)-sdl-d3dcl$(BITS)$(EXEPOSTFIX)
D3D_LDFLAGS=$(IMAGELDFLAGS) $(OGGVORBISLDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32 D3D_LDFLAGS=$(IMAGELDFLAGS) $(OGGVORBISLDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32
D3D_CFLAGS=$(D3DCFLAGS) -DFTE_SDL -DNO_XFLIP -I$(LIBS_DIR) -I$(MINGW_LIBS_DIR)/ -I$(MINGW_LIBS_DIR) $(LIBVORBISFILE_STATIC) $(DX7SDK) $(SPEEXCFLAGS) D3D_CFLAGS=$(D3DCFLAGS) -DFTE_SDL -DNO_XFLIP -I$(LIBS_DIR) -I$(MINGW_LIBS_DIR)/ -I$(MINGW_LIBS_DIR) $(LIBVORBISFILE_STATIC) $(DX7SDK) $(SPEEXCFLAGS)
ifeq ($(shell echo $(FTE_TARGET)|grep -E -i -v "win32.*sdl"),)
D3D_CFLAGS+= -D_MINGW_VFPRINTF
endif
D3DB_DIR=sdl_d3d_mgw$(BITS) D3DB_DIR=sdl_d3d_mgw$(BITS)
D3DCL_DIR=sdl_d3dcl_mgw$(BITS) D3DCL_DIR=sdl_d3dcl_mgw$(BITS)
VKCL_OBJS=$(D3DQUAKE_OBJS) $(BOTLIB_OBJS) $(SPEEX_OBJS) snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(D3DGL_OBJS) $(LTO_END) resources.o $(LTO_START) VKCL_OBJS=$(VKQUAKE_OBJS) $(BOTLIB_OBJS) $(SPEEX_OBJS) gl_bloom.o gl_vidsdl.o snd_sdl.o cd_sdl.o sys_sdl.o in_sdl.o snd_directx.o $(D3DGL_OBJS) $(LTO_END) resources.o $(LTO_START)
VK_EXE_NAME=../$(EXE_NAME)-sdl-vk$(BITS)$(EXEPOSTFIX) VK_EXE_NAME=../$(EXE_NAME)-sdl-vk$(BITS)$(EXEPOSTFIX)
VKCL_EXE_NAME=../$(EXE_NAME)-sdl-vkcl$(BITS)$(EXEPOSTFIX) VKCL_EXE_NAME=../$(EXE_NAME)-sdl-vkcl$(BITS)$(EXEPOSTFIX)
VK_LDFLAGS=$(IMAGELDFLAGS) $(OGGVORBISLDFLAGS) -lws2_32 -lmingw32 $(SDL_LDFLAGS) -mwindows -ldxguid -lwinmm -lole32
VK_CFLAGS=$(VKCFLAGS) -DFTE_SDL -DNO_XFLIP -I$(LIBS_DIR) -I$(MINGW_LIBS_DIR)/ -I$(MINGW_LIBS_DIR) $(LIBVORBISFILE_STATIC) $(DX7SDK) $(SPEEXCFLAGS) VK_CFLAGS=$(VKCFLAGS) -DFTE_SDL -DNO_XFLIP -I$(LIBS_DIR) -I$(MINGW_LIBS_DIR)/ -I$(MINGW_LIBS_DIR) $(LIBVORBISFILE_STATIC) $(DX7SDK) $(SPEEXCFLAGS)
ifeq ($(shell echo $(FTE_TARGET)|grep -E -i -v "win32.*sdl"),)
VK_CFLAGS+= -D_MINGW_VFPRINTF
endif
VKB_DIR=sdl_vk_mgw$(BITS) VKB_DIR=sdl_vk_mgw$(BITS)
VKCL_DIR=sdl_vkcl_mgw$(BITS) VKCL_DIR=sdl_vkcl_mgw$(BITS)
ifeq ($(shell echo $(FTE_TARGET)|grep -E -i -v "win32.*sdl"),)
GL_CFLAGS+= -D_MINGW_VFPRINTF
VK_CFLAGS+= -D_MINGW_VFPRINTF
D3D_CFLAGS+= -D_MINGW_VFPRINTF
M_CFLAGS+= -D_MINGW_VFPRINTF
endif
endif endif
#FTE_TARGET=vc (Visual C) #FTE_TARGET=vc (Visual C)

View file

@ -867,7 +867,9 @@ void CLFTE_ParseEntities(void)
qboolean isvalid = false; qboolean isvalid = false;
qboolean removeflag; qboolean removeflag;
int inputframe = cls.netchan.incoming_sequence; int inputframe = cls.netchan.incoming_sequence;
#if defined(QUAKESTATS) || defined(NQPROT)
int i; int i;
#endif
// int i; // int i;
// for (i = cl.validsequence+1; i < cls.netchan.incoming_sequence; i++) // for (i = cl.validsequence+1; i < cls.netchan.incoming_sequence; i++)

View file

@ -1902,7 +1902,7 @@ void CL_SendCmd (double frametime, qboolean mainloop)
#ifdef HLCLIENT #ifdef HLCLIENT
if (!CLHL_BuildUserInput(msecstouse, &independantphysics[0])) if (!CLHL_BuildUserInput(msecstouse, &independantphysics[0]))
#endif #endif
for (plnum = 0; plnum < cl.splitclients; plnum++) for (plnum = 0; plnum < (cl.splitclients?cl.splitclients:1); plnum++)
{ {
vec3_t mousemovements; vec3_t mousemovements;
CL_AdjustAngles (plnum, frametime); CL_AdjustAngles (plnum, frametime);
@ -1934,6 +1934,7 @@ void CL_SendCmd (double frametime, qboolean mainloop)
if (fullsend && (cl_pendingcmd[plnum].msec > 12.9 && cl_pendingcmd[plnum].msec < 13) && cls.maxfps == 77) if (fullsend && (cl_pendingcmd[plnum].msec > 12.9 && cl_pendingcmd[plnum].msec < 13) && cls.maxfps == 77)
cl_pendingcmd[plnum].msec = 13; cl_pendingcmd[plnum].msec = 13;
} }
msecstouse = cl_pendingcmd[0].msec;
//the main loop isn't allowed to send //the main loop isn't allowed to send
if (runningindepphys && mainloop) if (runningindepphys && mainloop)
@ -1945,9 +1946,6 @@ void CL_SendCmd (double frametime, qboolean mainloop)
if (!fullsend) if (!fullsend)
return; // when we're actually playing we try to match netfps exactly to avoid gameplay problems return; // when we're actually playing we try to match netfps exactly to avoid gameplay problems
if (msecstouse == 12)
msecstouse = 13;
// if (msecstouse > 127) // if (msecstouse > 127)
// Con_Printf("%i\n", msecstouse, msecs); // Con_Printf("%i\n", msecstouse, msecs);

View file

@ -157,7 +157,7 @@ cvar_t cl_gunangley = CVAR("cl_gunangley", "0");
cvar_t cl_gunanglez = CVAR("cl_gunanglez", "0"); cvar_t cl_gunanglez = CVAR("cl_gunanglez", "0");
cvar_t cl_proxyaddr = CVAR("cl_proxyaddr", ""); cvar_t cl_proxyaddr = CVAR("cl_proxyaddr", "");
cvar_t cl_sendguid = CVARD("cl_sendguid", "0", "Send a randomly generated 'globally unique' id to servers, which can be used by servers for score rankings and stuff. Different servers will see different guids. Delete the 'qkey' file in order to appear as a different user.\nIf set to 2, all servers will see the same guid. Be warned that this can show other people the guid that you're using."); cvar_t cl_sendguid = CVARD("cl_sendguid", "", "Send a randomly generated 'globally unique' id to servers, which can be used by servers for score rankings and stuff. Different servers will see different guids. Delete the 'qkey' file in order to appear as a different user.\nIf set to 2, all servers will see the same guid. Be warned that this can show other people the guid that you're using.");
cvar_t cl_downloads = CVARFD("cl_downloads", "1", CVAR_NOTFROMSERVER, "Allows you to block all automatic downloads."); cvar_t cl_downloads = CVARFD("cl_downloads", "1", CVAR_NOTFROMSERVER, "Allows you to block all automatic downloads.");
cvar_t cl_download_csprogs = CVARFD("cl_download_csprogs", "1", CVAR_NOTFROMSERVER, "Download updated client gamecode if available. Warning: If you clear this to avoid downloading vm code, you should also clear cl_download_packages."); cvar_t cl_download_csprogs = CVARFD("cl_download_csprogs", "1", CVAR_NOTFROMSERVER, "Download updated client gamecode if available. Warning: If you clear this to avoid downloading vm code, you should also clear cl_download_packages.");
cvar_t cl_download_redirection = CVARFD("cl_download_redirection", "2", CVAR_NOTFROMSERVER, "Follow download redirection to download packages instead of individual files. Also allows the server to send nearly arbitary download commands.\n2: allows redirection only to named packages files (and demos/*.mvd), which is a bit safer."); cvar_t cl_download_redirection = CVARFD("cl_download_redirection", "2", CVAR_NOTFROMSERVER, "Follow download redirection to download packages instead of individual files. Also allows the server to send nearly arbitary download commands.\n2: allows redirection only to named packages files (and demos/*.mvd), which is a bit safer.");
@ -452,7 +452,7 @@ void CL_SupportedFTEExtensions(int *pext1, int *pext2)
} }
#endif #endif
char *CL_GUIDString(netadr_t *adr) char *CL_GUIDString(netadr_t *adr, const char *guidstring)
{ {
static qbyte buf[2048]; static qbyte buf[2048];
static int buflen; static int buflen;
@ -461,7 +461,16 @@ char *CL_GUIDString(netadr_t *adr)
void *blocks[2]; void *blocks[2];
int lens[2]; int lens[2];
if (!cl_sendguid.ival) if (!*cl_sendguid.string && guidstring && *guidstring)
{
serveraddr[0] = '#'; //leading hash is to stop servers from being able to scrape from other servers.
Q_strncpyz(serveraddr+1, guidstring, sizeof(serveraddr)-1);
}
else if (cl_sendguid.ival == 2)
*serveraddr = 0;
else if (cl_sendguid.ival)
NET_AdrToString(serveraddr, sizeof(serveraddr), adr);
else
return NULL; return NULL;
if (*connectinfo.guid && connectinfo.istransfer) if (*connectinfo.guid && connectinfo.istransfer)
@ -498,11 +507,6 @@ char *CL_GUIDString(netadr_t *adr)
} }
} }
if (cl_sendguid.ival == 2)
*serveraddr = 0;
else
NET_AdrToString(serveraddr, sizeof(serveraddr), adr);
blocks[0] = buf;lens[0] = buflen; blocks[0] = buf;lens[0] = buflen;
blocks[1] = serveraddr;lens[1] = strlen(serveraddr); blocks[1] = serveraddr;lens[1] = strlen(serveraddr);
Com_BlocksChecksum(2, blocks, lens, (void*)digest); Com_BlocksChecksum(2, blocks, lens, (void*)digest);
@ -522,8 +526,9 @@ void CL_SendConnectPacket (netadr_t *to, int mtu,
#ifdef PROTOCOL_VERSION_FTE #ifdef PROTOCOL_VERSION_FTE
int ftepext, int ftepext2, int ftepext, int ftepext2,
#endif #endif
int compressioncrc int compressioncrc,
/*, ...*/) //dmw new parms const char *guidhash
/*, ...*/)
{ {
extern cvar_t qport; extern cvar_t qport;
netadr_t addr; netadr_t addr;
@ -683,7 +688,7 @@ void CL_SendConnectPacket (netadr_t *to, int mtu,
#endif #endif
connectinfo.compresscrc = 0; connectinfo.compresscrc = 0;
info = CL_GUIDString(to); info = CL_GUIDString(to, guidhash);
if (info) if (info)
Q_strncatz(data, va("0x%x \"%s\"\n", PROTOCOL_INFO_GUID, info), sizeof(data)); Q_strncatz(data, va("0x%x \"%s\"\n", PROTOCOL_INFO_GUID, info), sizeof(data));
@ -714,17 +719,18 @@ void CL_CheckForResend (void)
qboolean keeptrying = true; qboolean keeptrying = true;
char *host; char *host;
extern int r_blockvidrestart; extern int r_blockvidrestart;
const char *lbp;
#ifndef CLIENTONLY #ifndef CLIENTONLY
if (!cls.state && (!connectinfo.trying || sv.state != ss_clustermode) && sv.state) if (!cls.state && (!connectinfo.trying || sv.state != ss_clustermode) && sv.state)
{ {
const char *lbp;
#ifdef NQPROT #ifdef NQPROT
qboolean proquakeangles = false; qboolean proquakeangles = false;
#endif #endif
#ifdef NETPREPARSE #ifdef NETPREPARSE
extern cvar_t dpcompat_nopreparse; extern cvar_t dpcompat_nopreparse;
#endif #endif
extern cvar_t sv_guidhash;
memset(&connectinfo, 0, sizeof(connectinfo)); memset(&connectinfo, 0, sizeof(connectinfo));
Q_strncpyz (cls.servername, "internalserver", sizeof(cls.servername)); Q_strncpyz (cls.servername, "internalserver", sizeof(cls.servername));
Cvar_ForceSet(&cl_servername, cls.servername); Cvar_ForceSet(&cl_servername, cls.servername);
@ -945,7 +951,7 @@ void CL_CheckForResend (void)
{ {
if (!connectinfo.challenge) if (!connectinfo.challenge)
connectinfo.challenge = rand(); connectinfo.challenge = rand();
CL_SendConnectPacket (NULL, 8192-16, connectinfo.fteext1, connectinfo.fteext2, false); CL_SendConnectPacket (NULL, 8192-16, connectinfo.fteext1, connectinfo.fteext2, 0, sv_guidhash.string);
} }
return; return;
@ -976,6 +982,10 @@ void CL_CheckForResend (void)
if (connectinfo.time && realtime - connectinfo.time < 5.0) if (connectinfo.time && realtime - connectinfo.time < 5.0)
return; return;
#ifdef HAVE_DTLS
if (connectinfo.dtlsupgrade != DTLS_ACTIVE)
#endif
//FIXME: this is switching ports far too much
NET_InitClient(false); NET_InitClient(false);
t1 = Sys_DoubleTime (); t1 = Sys_DoubleTime ();
@ -2833,6 +2843,7 @@ void CL_ConnectionlessPacket (void)
#ifdef HAVE_DTLS #ifdef HAVE_DTLS
qboolean candtls = false; qboolean candtls = false;
#endif #endif
char guidhash[256];
s = MSG_ReadString (); s = MSG_ReadString ();
COM_Parse(s); COM_Parse(s);
@ -2867,7 +2878,7 @@ void CL_ConnectionlessPacket (void)
connectinfo.protocol = CP_QUAKE3; connectinfo.protocol = CP_QUAKE3;
connectinfo.challenge = atoi(s+17); connectinfo.challenge = atoi(s+17);
CL_SendConnectPacket (&net_from, 0, 0, 0, 0/*, ...*/); CL_SendConnectPacket (&net_from, 0, 0, 0, 0/*, ...*/, NULL);
} }
else else
{ {
@ -2993,6 +3004,17 @@ void CL_ConnectionlessPacket (void)
c = MSG_ReadLong();/*ident*/ c = MSG_ReadLong();/*ident*/
switch(c) switch(c)
{ {
case PROTOCOL_INFO_GUID:
if (len > sizeof(guidhash)-1)
{
MSG_ReadData(guidhash, sizeof(guidhash));
MSG_ReadSkip(len-sizeof(guidhash));
len = sizeof(guidhash)-1;
}
else
MSG_ReadData(guidhash, len);
guidhash[len] = 0;
break;
default: default:
MSG_ReadSkip(len); /*payload*/ MSG_ReadSkip(len); /*payload*/
break; break;
@ -3007,11 +3029,12 @@ void CL_ConnectionlessPacket (void)
case PROTOCOL_VERSION_FTE2: pext2 = l; break; case PROTOCOL_VERSION_FTE2: pext2 = l; break;
case PROTOCOL_VERSION_FRAGMENT: mtu = l; break; case PROTOCOL_VERSION_FRAGMENT: mtu = l; break;
#ifdef HAVE_DTLS #ifdef HAVE_DTLS
case PROTOCOL_VERSION_DTLSUPGRADE: candtls = l; break; //0:not enabled. 1:use if you want. 2:require it. case PROTOCOL_VERSION_DTLSUPGRADE: candtls = l; break; //0:not enabled. 1:explicit use allowed. 2:favour it. 3: require it
#endif #endif
#ifdef HUFFNETWORK #ifdef HUFFNETWORK
case PROTOCOL_VERSION_HUFFMAN: huffcrc = l; break; case PROTOCOL_VERSION_HUFFMAN: huffcrc = l; break;
#endif #endif
case PROTOCOL_INFO_GUID: Q_snprintfz(guidhash, sizeof(guidhash), "0x%x", l); break;
default: default:
break; break;
} }
@ -3041,7 +3064,7 @@ void CL_ConnectionlessPacket (void)
} }
#endif #endif
CL_SendConnectPacket (&net_from, mtu, pext, pext2, huffcrc/*, ...*/); CL_SendConnectPacket (&net_from, mtu, pext, pext2, huffcrc/*, ...*/, guidhash);
return; return;
} }
#ifdef Q2CLIENT #ifdef Q2CLIENT

View file

@ -35,10 +35,12 @@ static char *CLNQ_ParseProQuakeMessage (char *s);
static void DLC_Poll(qdownload_t *dl); static void DLC_Poll(qdownload_t *dl);
static void CL_ProcessUserInfo (int slot, player_info_t *player); static void CL_ProcessUserInfo (int slot, player_info_t *player);
#ifdef NQPROT
static char cl_dp_csqc_progsname[128]; static char cl_dp_csqc_progsname[128];
static int cl_dp_csqc_progssize; static int cl_dp_csqc_progssize;
static int cl_dp_csqc_progscrc; static int cl_dp_csqc_progscrc;
static int cl_dp_serverextension_download; static int cl_dp_serverextension_download;
#endif
#ifdef AVAIL_ZLIB #ifdef AVAIL_ZLIB
#ifndef ZEXPORT #ifndef ZEXPORT
@ -178,6 +180,7 @@ static char *svc_qwstrings[] =
"???", "???",
}; };
#ifdef NQPROT
static char *svc_nqstrings[] = static char *svc_nqstrings[] =
{ {
"nqsvc_bad", "nqsvc_bad",
@ -278,6 +281,7 @@ static char *svc_nqstrings[] =
"NEW PROTOCOL(87)", //87 "NEW PROTOCOL(87)", //87
"NEW PROTOCOL(88)" //88 "NEW PROTOCOL(88)" //88
}; };
#endif
extern cvar_t requiredownloads, cl_standardchat, msg_filter, msg_filter_frags, msg_filter_pickups, cl_countpendingpl, cl_download_mapsrc; extern cvar_t requiredownloads, cl_standardchat, msg_filter, msg_filter_frags, msg_filter_pickups, cl_countpendingpl, cl_download_mapsrc;
int oldparsecountmod; int oldparsecountmod;
@ -2650,6 +2654,7 @@ qboolean CL_ParseOOBDownload(void)
return true; return true;
} }
#ifdef NQPROT
static void CLDP_ParseDownloadData(void) static void CLDP_ParseDownloadData(void)
{ {
qdownload_t *dl = cls.download; qdownload_t *dl = cls.download;
@ -2781,6 +2786,7 @@ static void CLDP_ParseDownloadFinished(char *s)
CL_RequestNextDownload (); CL_RequestNextDownload ();
} }
#endif
static vfsfile_t *upload_file; static vfsfile_t *upload_file;
static qbyte *upload_data; static qbyte *upload_data;
@ -2870,6 +2876,7 @@ void CL_StopUpload(void)
upload_pos = upload_size = 0; upload_pos = upload_size = 0;
} }
#if 0 //in case we ever want to add any uploads other than snaps
static qboolean CL_StartUploadFile(char *filename) static qboolean CL_StartUploadFile(char *filename)
{ {
if (!COM_CheckParm("-fileul")) if (!COM_CheckParm("-fileul"))
@ -2897,6 +2904,7 @@ static qboolean CL_StartUploadFile(char *filename)
} }
return false; return false;
} }
#endif
/* /*
===================================================================== =====================================================================
@ -2905,9 +2913,6 @@ static qboolean CL_StartUploadFile(char *filename)
===================================================================== =====================================================================
*/ */
#ifdef CLIENTONLY
static float nextdemotime;
#endif
void CL_ClearParseState(void) void CL_ClearParseState(void)
{ {
@ -4340,6 +4345,7 @@ static void CL_ParseBaselineDelta (void)
memcpy(cl_baselines + es.number, &es, sizeof(es)); memcpy(cl_baselines + es.number, &es, sizeof(es));
} }
#ifdef Q2CLIENT
static void CLQ2_Precache_f (void) static void CLQ2_Precache_f (void)
{ {
Model_CheckDownloads(); Model_CheckDownloads();
@ -4353,6 +4359,7 @@ static void CLQ2_Precache_f (void)
CG_Start(); CG_Start();
#endif #endif
} }
#endif

View file

@ -147,12 +147,12 @@ static qintptr_t VARGS Plug_Draw_LoadImageData(void *offset, quintptr_t mask, co
{ {
// name = va("%s", name); // name = va("%s", name);
t = Image_FindTexture(name, NULL, IF_NOMIPMAP|IF_UIPIC|IF_CLAMP); t = Image_FindTexture(name, NULL, IF_PREMULTIPLYALPHA|IF_NOMIPMAP|IF_UIPIC|IF_CLAMP);
if (!TEXVALID(t)) if (!TEXVALID(t))
t = Image_CreateTexture(name, NULL, IF_NOMIPMAP|IF_UIPIC|IF_CLAMP); t = Image_CreateTexture(name, NULL, IF_PREMULTIPLYALPHA|IF_NOMIPMAP|IF_UIPIC|IF_CLAMP);
if (TEXVALID(t)) if (TEXVALID(t))
{ {
Image_Upload(t, TF_RGBA32, rgbdata, NULL, width, height, IF_NOMIPMAP|IF_UIPIC|IF_CLAMP); Image_Upload(t, TF_RGBA32, rgbdata, NULL, width, height, IF_PREMULTIPLYALPHA|IF_NOMIPMAP|IF_UIPIC|IF_CLAMP);
ret = Plug_Draw_LoadImage(name, 3, NULL); ret = Plug_Draw_LoadImage(name, 3, NULL);
} }
@ -1118,6 +1118,10 @@ static qintptr_t VARGS Plug_Con_GetConsoleString(void *offset, quintptr_t mask,
{ {
Q_strncpyz(value, con->title, size); Q_strncpyz(value, con->title, size);
} }
else if (!strcmp(attrib, "icon"))
{
Q_strncpyz(value, con->icon, size);
}
else if (!strcmp(attrib, "prompt")) else if (!strcmp(attrib, "prompt"))
{ {
Q_strncpyz(value, con->prompt, size); Q_strncpyz(value, con->prompt, size);
@ -1152,6 +1156,8 @@ static qintptr_t VARGS Plug_Con_SetConsoleString(void *offset, quintptr_t mask,
Con_Footerf(con, false, "%s", value); Con_Footerf(con, false, "%s", value);
else if (!strcmp(attrib, "title")) else if (!strcmp(attrib, "title"))
Q_strncpyz(con->title, value, sizeof(con->title)); Q_strncpyz(con->title, value, sizeof(con->title));
else if (!strcmp(attrib, "icon"))
Q_strncpyz(con->icon, value, sizeof(con->icon));
else if (!strcmp(attrib, "prompt")) else if (!strcmp(attrib, "prompt"))
Q_strncpyz(con->prompt, value, sizeof(con->prompt)); Q_strncpyz(con->prompt, value, sizeof(con->prompt));
else if (!strcmp(attrib, "backimage")) else if (!strcmp(attrib, "backimage"))

View file

@ -2275,7 +2275,7 @@ int Image_WritePNG (char *filename, enum fs_relative fsroot, int compression, vo
#endif #endif
qboolean WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, int instride, int width, int height, uploadfmt_t fmt); qboolean WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, int instride, int width, int height, uploadfmt_t fmt);
qboolean WriteTGA(char *filename, enum fs_relative fsroot, qbyte *fte_restrict rgb_buffer, int bytestride, int width, int height, enum uploadfmt fmt) qboolean WriteTGA(char *filename, enum fs_relative fsroot, const qbyte *fte_restrict rgb_buffer, int bytestride, int width, int height, enum uploadfmt fmt)
{ {
size_t c, i; size_t c, i;
vfsfile_t *vfs; vfsfile_t *vfs;
@ -2321,7 +2321,7 @@ qboolean WriteTGA(char *filename, enum fs_relative fsroot, qbyte *fte_restrict r
{ //if we're upside down, lets just use an upside down tga. { //if we're upside down, lets just use an upside down tga.
rgb_buffer += bytestride*(height-1); rgb_buffer += bytestride*(height-1);
bytestride = -bytestride; bytestride = -bytestride;
//now we can just do everything else in-place //now we can just do everything without worrying about rows
} }
else //our data is top-down, set up the header to also be top-down. else //our data is top-down, set up the header to also be top-down.
header[17] = 0x20; header[17] = 0x20;
@ -2330,45 +2330,44 @@ qboolean WriteTGA(char *filename, enum fs_relative fsroot, qbyte *fte_restrict r
{ //can just directly write it { //can just directly write it
//bgr24, bgra24 //bgr24, bgra24
c = width*height*opx; c = width*height*opx;
VFS_WRITE(vfs, header, sizeof(header));
VFS_WRITE(vfs, rgb_buffer, c);
} }
else else
{ {
qbyte *fte_restrict rgb_out = malloc(width*opx*height);
//no need to swap alpha, and if we're just swapping alpha will be fine in-place. //no need to swap alpha, and if we're just swapping alpha will be fine in-place.
if (rgb) if (rgb)
{ //rgb24, rgbx32, rgba32 { //rgb24, rgbx32, rgba32
qbyte tmp[3]; // compact, and swap
// compact in place, and swap
c = width*height; c = width*height;
for (i=0 ; i<c ; i++) for (i=0 ; i<c ; i++)
{ {
tmp[2] = rgb_buffer[i*ipx+0]; rgb_out[i*opx+2] = rgb_buffer[i*ipx+0];
tmp[1] = rgb_buffer[i*ipx+1]; rgb_out[i*opx+1] = rgb_buffer[i*ipx+1];
tmp[0] = rgb_buffer[i*ipx+2]; rgb_out[i*opx+0] = rgb_buffer[i*ipx+2];
rgb_buffer[i*opx+0] = tmp[0];
rgb_buffer[i*opx+1] = tmp[1];
rgb_buffer[i*opx+2] = tmp[2];
} }
} }
else else
{ //(bgr24), bgrx32, (bgra32) { //(bgr24), bgrx32, (bgra32)
qbyte tmp[3]; // compact
// compact in place
c = width*height; c = width*height;
for (i=0 ; i<c ; i++) for (i=0 ; i<c ; i++)
{ {
tmp[0] = rgb_buffer[i*ipx+0]; rgb_out[i*opx+0] = rgb_buffer[i*ipx+0];
tmp[1] = rgb_buffer[i*ipx+1]; rgb_out[i*opx+1] = rgb_buffer[i*ipx+1];
tmp[2] = rgb_buffer[i*ipx+2]; rgb_out[i*opx+2] = rgb_buffer[i*ipx+2];
rgb_buffer[i*opx+0] = tmp[0];
rgb_buffer[i*opx+1] = tmp[1];
rgb_buffer[i*opx+2] = tmp[2];
} }
} }
c *= opx; c *= opx;
}
VFS_WRITE(vfs, header, sizeof(header)); VFS_WRITE(vfs, header, sizeof(header));
VFS_WRITE(vfs, rgb_buffer, c); VFS_WRITE(vfs, rgb_out, c);
free(rgb_out);
}
VFS_CLOSE(vfs); VFS_CLOSE(vfs);
} }
return true; return true;

View file

@ -1409,6 +1409,7 @@ void CSQC_CvarChanged(cvar_t *var);
#define CSQC_UnconnectedOkay(inprinciple) false #define CSQC_UnconnectedOkay(inprinciple) false
#define CSQC_UnconnectedInit() false #define CSQC_UnconnectedInit() false
#define CSQC_UseGamecodeLoadingScreen() false #define CSQC_UseGamecodeLoadingScreen() false
#define CSQC_Parse_SetAngles(seat,newangles,wasdelta) false
#endif #endif
// //
@ -1659,6 +1660,19 @@ void Stats_Clear(void);
void Stats_Init(void); void Stats_Init(void);
enum uploadfmt; enum uploadfmt;
/*struct mediacallbacks_s
{ //functions provided by the engine/renderer, for faster/off-thread updates
qboolean pbocanoffthread;
qboolean (VARGS *PBOLock)(struct mediacallbacks_s *ctx, size_t width, size_t height, uploadfmt_t fmt, qboolean *lost);
void (VARGS *PBOUpdate)(struct mediacallbacks_s *ctx, void *data, size_t width, size_t height, int stride);
void (VARGS *PBOUnlock)(struct mediacallbacks_s *ctx);
void (VARGS *AudioStream) (void *auddata, int rate, int frames, int channels, int width);
void (VARGS *WorkQueue) (void *wctx, void (VARGS *callback)(void *data), void *data);
void (VARGS *WorkSync) (void *wctx, int *address, int oldvalue); //blocks until the address changes. make sure you queued something that will change it from that value. oldvalue is present to avoid races. if you're reading the address then you should probably volatile it to avoid compiler opts reading it twice (fixme: needs a proper barrier).
};
*/
typedef struct typedef struct
{ {
size_t structsize; size_t structsize;
@ -1676,6 +1690,8 @@ typedef struct
void (VARGS *changestream) (void *ctx, const char *streamname); void (VARGS *changestream) (void *ctx, const char *streamname);
qboolean (VARGS *getproperty) (void *ctx, const char *field, char *out, size_t *outsize); //if out is null, returns required buffer size. returns 0 on failure / buffer too small qboolean (VARGS *getproperty) (void *ctx, const char *field, char *out, size_t *outsize); //if out is null, returns required buffer size. returns 0 on failure / buffer too small
// void *(VARGS *createdecoderCB)(const char *name, struct mediacallbacks_s *callbacks);
} media_decoder_funcs_t; } media_decoder_funcs_t;
typedef struct typedef struct
{ {

View file

@ -2028,6 +2028,14 @@ static int Con_DrawConsoleLines(console_t *con, conline_t *l, int sx, int ex, in
picw = ex-sx; picw = ex-sx;
} }
} }
//a fall back image (mostly for delay-loading or whatever.
if (R_GetShaderSizes(pic, NULL, NULL, false) <= 0)
{
imgname = Info_ValueForKey(linkinfo, "fbimg");
if (*imgname)
pic = R_RegisterPic(imgname, NULL);
}
} }
break; break;
} }
@ -2348,6 +2356,7 @@ void Con_DrawConsole (int lines, qboolean noback)
if (Key_Dest_Has(kdm_cwindows)) if (Key_Dest_Has(kdm_cwindows))
{ {
int top = 8; //padding at the top
if (con_curwindow==w) if (con_curwindow==w)
R2D_ImageColours(0.0, 0.05, 0.1, 0.8); R2D_ImageColours(0.0, 0.05, 0.1, 0.8);
else else
@ -2355,6 +2364,10 @@ void Con_DrawConsole (int lines, qboolean noback)
R2D_FillBlock(w->wnd_x, w->wnd_y, w->wnd_w, w->wnd_h); R2D_FillBlock(w->wnd_x, w->wnd_y, w->wnd_w, w->wnd_h);
R2D_ImageColours(1, 1, 1, 1); R2D_ImageColours(1, 1, 1, 1);
//fixme: scale up this font...
Draw_FunStringWidth(w->wnd_x, w->wnd_y, w->title, w->wnd_w-top, 2, (con_curwindow==w)?true:false);
Draw_FunStringWidth(w->wnd_x+w->wnd_w-top, w->wnd_y, "X", top, 2, ((w->buttonsdown == CB_CLOSE && w->mousecursor[0] > w->wnd_w-(8+top) && w->mousecursor[1] < top) || (con_curwindow==w && w->mousecursor[0] >= w->wnd_w-(8+top) && w->mousecursor[0] < w->wnd_w-8 && w->mousecursor[1] >= 0 && w->mousecursor[1] < 8))?true:false);
if (w->backshader || *w->backimage) if (w->backshader || *w->backimage)
{ {
shader_t *shader = w->backshader; shader_t *shader = w->backshader;
@ -2362,7 +2375,10 @@ void Con_DrawConsole (int lines, qboolean noback)
shader = w->backshader = R_RegisterPic(w->backimage, NULL);// R_RegisterCustom(w->backimage, SUF_NONE, Shader_DefaultCinematic, w->backimage); shader = w->backshader = R_RegisterPic(w->backimage, NULL);// R_RegisterCustom(w->backimage, SUF_NONE, Shader_DefaultCinematic, w->backimage);
if (shader) if (shader)
{ {
int top = 8; float backx = w->wnd_x+8;
float backy = w->wnd_y+top;
float backw = w->wnd_w-16;
float backh = w->wnd_h-8-top;
#ifdef HAVE_MEDIA_DECODER #ifdef HAVE_MEDIA_DECODER
cin_t *cin = R_ShaderGetCinematic(shader); cin_t *cin = R_ShaderGetCinematic(shader);
if (cin) if (cin)
@ -2372,8 +2388,8 @@ void Con_DrawConsole (int lines, qboolean noback)
{ {
float x = 0; float x = 0;
// float r = x+w->wnd_w-16; // float r = x+w->wnd_w-16;
const char *buttons[] = {"bck", "fwd", "rld", "home", ((w->linebuffered == Con_Navigate)?(char*)key_lines[edit_line]:url)}; const char *buttons[] = {"bck", "fwd", "rld", "home", "", ((w->linebuffered == Con_Navigate)?(char*)key_lines[edit_line]:url)};
const char *buttoncmds[] = {"cmd:back", "cmd:forward", "cmd:refresh", ENGINEWEBSITE, NULL}; const char *buttoncmds[] = {"cmd:back", "cmd:forward", "cmd:refresh", ENGINEWEBSITE, NULL, NULL};
float tw; float tw;
int i, fl; int i, fl;
@ -2382,6 +2398,14 @@ void Con_DrawConsole (int lines, qboolean noback)
if (i == countof(buttons)-1) if (i == countof(buttons)-1)
tw = ~0u; tw = ~0u;
else if (i == countof(buttons)-2) else if (i == countof(buttons)-2)
{
tw = 8+8;
if (*w->icon)
R2D_Image(w->wnd_x+8+x, w->wnd_y+top, tw, tw, 0, 0, 1, 1, R_RegisterPic(w->icon, NULL));
else
tw = 0;
}
else if (i == countof(buttons)-3)
tw = 40; tw = 40;
else else
tw = 32; tw = 32;
@ -2407,22 +2431,38 @@ void Con_DrawConsole (int lines, qboolean noback)
x += tw; x += tw;
} }
top += 8; top += 8;
backy += 8;
backh -= 8;
} }
Media_Send_Resize(cin, ((w->wnd_w-16.0)*(int)vid.rotpixelwidth) / (float)vid.width, ((w->wnd_h-8-top)*(int)vid.rotpixelheight) / (float)vid.height); //convert these to pixels.
Media_Send_MouseMove(cin, (w->mousecursor[0]) / (w->wnd_w-16.0), (w->mousecursor[1]-top) / (w->wnd_h-8.0-top)); backx = (backx*(int)vid.rotpixelwidth) / (float)vid.width;
backy = (backy*(int)vid.rotpixelheight) / (float)vid.height;
backw = (backw*(int)vid.rotpixelwidth) / (float)vid.width;
backh = (backh*(int)vid.rotpixelheight) / (float)vid.height;
//snap to pixels. this avoids issues with linear filtering
backx = (int)backx;
backy = (int)backy;
backw = (int)backw;
backh = (int)backh;
Media_Send_Resize(cin, backw, backh);
//convert back to screen coords now.
backx = (backx*(int)vid.width) / (float)vid.rotpixelwidth;
backy = (backy*(int)vid.height) / (float)vid.rotpixelheight;
backw = (backw*(int)vid.width) / (float)vid.rotpixelwidth;
backh = (backh*(int)vid.height) / (float)vid.rotpixelheight;
Media_Send_MouseMove(cin, (w->mousecursor[0]) / backw, (w->mousecursor[1]-top) / backh);
if (con_curwindow==w) if (con_curwindow==w)
Media_Send_Command(cin, "cmd:focus"); Media_Send_Command(cin, "cmd:focus");
else else
Media_Send_Command(cin, "cmd:unfocus"); Media_Send_Command(cin, "cmd:unfocus");
} }
#endif #endif
R2D_Image(w->wnd_x+8, w->wnd_y+top, w->wnd_w-16, w->wnd_h-8-top, 0, 0, 1, 1, shader); R2D_Image(backx, backy, backw, backh, 0, 0, 1, 1, shader);
} }
} }
Draw_FunStringWidth(w->wnd_x, w->wnd_y, w->title, w->wnd_w-8, 2, (con_curwindow==w)?true:false);
Draw_FunStringWidth(w->wnd_x+w->wnd_w-8, w->wnd_y, "X", 8, 2, ((w->buttonsdown == CB_CLOSE && w->mousecursor[0] > w->wnd_w-16 && w->mousecursor[1] < 8) || (con_curwindow==w && w->mousecursor[0] >= w->wnd_w-16 && w->mousecursor[0] < w->wnd_w-8 && w->mousecursor[1] >= 0 && w->mousecursor[1] < 8))?true:false);
w->unseentext = false; w->unseentext = false;
} }
else else

View file

@ -35,7 +35,7 @@ char *r_defaultimageextensions =
; ;
static void QDECL R_ImageExtensions_Callback(struct cvar_s *var, char *oldvalue); static void QDECL R_ImageExtensions_Callback(struct cvar_s *var, char *oldvalue);
cvar_t r_imageexensions = CVARCD("r_imageexensions", NULL, R_ImageExtensions_Callback, "The list of image file extensions which fte should attempt to load."); cvar_t r_imageexensions = CVARCD("r_imageexensions", NULL, R_ImageExtensions_Callback, "The list of image file extensions which fte should attempt to load.");
cvar_t r_image_downloadsizelimit = CVARFD("r_image_downloadsizelimit", "64000", CVAR_NOTFROMSERVER, "The maximum allowed file size of images loaded from a web-based url. 0 disables completely, while empty imposes no limit."); cvar_t r_image_downloadsizelimit = CVARFD("r_image_downloadsizelimit", "131072", CVAR_NOTFROMSERVER, "The maximum allowed file size of images loaded from a web-based url. 0 disables completely, while empty imposes no limit.");
extern cvar_t gl_lerpimages; extern cvar_t gl_lerpimages;
extern cvar_t gl_picmip2d; extern cvar_t gl_picmip2d;
extern cvar_t gl_picmip; extern cvar_t gl_picmip;
@ -717,14 +717,18 @@ void (PNGAPI *qpng_read_end) PNGARG((png_structp png_ptr, png_infop info_ptr)) P
void (PNGAPI *qpng_read_image) PNGARG((png_structp png_ptr, png_bytepp image)) PSTATIC(png_read_image); void (PNGAPI *qpng_read_image) PNGARG((png_structp png_ptr, png_bytepp image)) PSTATIC(png_read_image);
png_byte (PNGAPI *qpng_get_bit_depth) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_bit_depth); png_byte (PNGAPI *qpng_get_bit_depth) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_bit_depth);
png_byte (PNGAPI *qpng_get_channels) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_channels); png_byte (PNGAPI *qpng_get_channels) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_channels);
#if PNG_LIBPNG_VER < 10400
png_uint_32 (PNGAPI *qpng_get_rowbytes) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_rowbytes);
#else
png_size_t (PNGAPI *qpng_get_rowbytes) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_rowbytes); png_size_t (PNGAPI *qpng_get_rowbytes) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_rowbytes);
#endif
void (PNGAPI *qpng_read_update_info) PNGARG((png_structp png_ptr, png_infop info_ptr)) PSTATIC(png_read_update_info); void (PNGAPI *qpng_read_update_info) PNGARG((png_structp png_ptr, png_infop info_ptr)) PSTATIC(png_read_update_info);
void (PNGAPI *qpng_set_strip_16) PNGARG((png_structp png_ptr)) PSTATIC(png_set_strip_16); void (PNGAPI *qpng_set_strip_16) PNGARG((png_structp png_ptr)) PSTATIC(png_set_strip_16);
void (PNGAPI *qpng_set_expand) PNGARG((png_structp png_ptr)) PSTATIC(png_set_expand); void (PNGAPI *qpng_set_expand) PNGARG((png_structp png_ptr)) PSTATIC(png_set_expand);
void (PNGAPI *qpng_set_gray_to_rgb) PNGARG((png_structp png_ptr)) PSTATIC(png_set_gray_to_rgb); void (PNGAPI *qpng_set_gray_to_rgb) PNGARG((png_structp png_ptr)) PSTATIC(png_set_gray_to_rgb);
void (PNGAPI *qpng_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)) PSTATIC(png_set_tRNS_to_alpha); void (PNGAPI *qpng_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)) PSTATIC(png_set_tRNS_to_alpha);
png_uint_32 (PNGAPI *qpng_get_valid) PNGARG((png_const_structp png_ptr, png_const_infop info_ptr, png_uint_32 flag)) PSTATIC(png_get_valid); png_uint_32 (PNGAPI *qpng_get_valid) PNGARG((png_const_structp png_ptr, png_const_infop info_ptr, png_uint_32 flag)) PSTATIC(png_get_valid);
#if PNG_LIBPNG_VER > 10400 #if PNG_LIBPNG_VER >= 10400
void (PNGAPI *qpng_set_expand_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)) PSTATIC(png_set_expand_gray_1_2_4_to_8); void (PNGAPI *qpng_set_expand_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)) PSTATIC(png_set_expand_gray_1_2_4_to_8);
#else #else
void (PNGAPI *qpng_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)) PSTATIC(png_set_gray_1_2_4_to_8); void (PNGAPI *qpng_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)) PSTATIC(png_set_gray_1_2_4_to_8);
@ -2050,12 +2054,6 @@ qbyte *ReadPCXPalette(qbyte *buf, int len, qbyte *out)
typedef struct bmpheader_s typedef struct bmpheader_s
{ {
/* unsigned short Type;*/
unsigned int Size;
unsigned short Reserved1;
unsigned short Reserved2;
unsigned int OffsetofBMPBits;
unsigned int SizeofBITMAPINFOHEADER; unsigned int SizeofBITMAPINFOHEADER;
signed int Width; signed int Width;
signed int Height; signed int Height;
@ -2079,20 +2077,13 @@ typedef struct bmpheaderv4_s
unsigned int Gamma[3]; unsigned int Gamma[3];
} bmpheaderv4_t; } bmpheaderv4_t;
qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height) static qbyte *ReadRawBMPFile(qbyte *buf, int length, int *width, int *height, size_t OffsetofBMPBits)
{ {
unsigned int i; unsigned int i;
bmpheader_t h; bmpheader_t h;
qbyte *data; qbyte *data;
if (buf[0] != 'B' || buf[1] != 'M') memcpy(&h, (bmpheader_t *)buf, sizeof(h));
return NULL;
memcpy(&h, (bmpheader_t *)(buf+2), sizeof(h));
h.Size = LittleLong(h.Size);
h.Reserved1 = LittleShort(h.Reserved1);
h.Reserved2 = LittleShort(h.Reserved2);
h.OffsetofBMPBits = LittleLong(h.OffsetofBMPBits);
h.SizeofBITMAPINFOHEADER = LittleLong(h.SizeofBITMAPINFOHEADER); h.SizeofBITMAPINFOHEADER = LittleLong(h.SizeofBITMAPINFOHEADER);
h.Width = LittleLong(h.Width); h.Width = LittleLong(h.Width);
h.Height = LittleLong(h.Height); h.Height = LittleLong(h.Height);
@ -2107,8 +2098,9 @@ qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height)
if (h.Compression) //RLE? BITFIELDS (gah)? if (h.Compression) //RLE? BITFIELDS (gah)?
return NULL; return NULL;
if (length < h.Size)
return NULL; //truncated... if (!OffsetofBMPBits)
h.Height /= 2; //icons are weird.
*width = h.Width; *width = h.Width;
*height = h.Height; *height = h.Height;
@ -2125,15 +2117,18 @@ qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height)
if (h.Width&1) if (h.Width&1)
return NULL; return NULL;
data = buf+2; data = buf;
data += sizeof(h); data += sizeof(h);
for (i = 0; i < h.NumofColorIndices; i++) for (i = 0; i < h.NumofColorIndices; i++)
{ {
pal[i] = data[i*4+0] + (data[i*4+1]<<8) + (data[i*4+2]<<16) + (255/*data[i*4+3]*/<<16); pal[i] = data[i*4+2] + (data[i*4+1]<<8) + (data[i*4+0]<<16) + (255/*data[i*4+3]*/<<24);
} }
buf += h.OffsetofBMPBits; if (OffsetofBMPBits)
buf += OffsetofBMPBits;
else
buf = data+h.NumofColorIndices*4;
data32 = BZ_Malloc(h.Width * h.Height*4); data32 = BZ_Malloc(h.Width * h.Height*4);
for (y = 0; y < h.Height; y++) for (y = 0; y < h.Height; y++)
{ {
@ -2143,7 +2138,22 @@ qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height)
data32[i++] = pal[buf[x]>>4]; data32[i++] = pal[buf[x]>>4];
data32[i++] = pal[buf[x]&15]; data32[i++] = pal[buf[x]&15];
} }
buf += h.Width>>1; buf += (h.Width+1)>>1;
}
if (!OffsetofBMPBits)
{
for (y = 0; y < h.Height; y++)
{
i = (h.Height-1-y) * (h.Width);
for (x = 0; x < h.Width; x++)
{
if (buf[x>>3]&(1<<(7-(x&7))))
data32[i] &= 0x00ffffff;
i++;
}
buf += (h.Width+7)>>3;
}
} }
return (qbyte *)data32; return (qbyte *)data32;
@ -2158,7 +2168,7 @@ qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height)
if (h.NumofColorIndices>256) if (h.NumofColorIndices>256)
return NULL; return NULL;
data = buf+2; data = buf;
data += sizeof(h); data += sizeof(h);
for (i = 0; i < h.NumofColorIndices; i++) for (i = 0; i < h.NumofColorIndices; i++)
@ -2166,7 +2176,10 @@ qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height)
pal[i] = data[i*4+2] + (data[i*4+1]<<8) + (data[i*4+0]<<16) + (255/*data[i*4+3]*/<<24); pal[i] = data[i*4+2] + (data[i*4+1]<<8) + (data[i*4+0]<<16) + (255/*data[i*4+3]*/<<24);
} }
buf += h.OffsetofBMPBits; if (OffsetofBMPBits)
buf += OffsetofBMPBits;
else
buf += h.SizeofBITMAPINFOHEADER + h.NumofColorIndices*4;
data32 = BZ_Malloc(h.Width * h.Height*4); data32 = BZ_Malloc(h.Width * h.Height*4);
for (y = 0; y < h.Height; y++) for (y = 0; y < h.Height; y++)
{ {
@ -2179,12 +2192,30 @@ qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height)
buf += h.Width; buf += h.Width;
} }
if (!OffsetofBMPBits)
{
for (y = 0; y < h.Height; y++)
{
i = (h.Height-1-y) * (h.Width);
for (x = 0; x < h.Width; x++)
{
if (buf[x>>3]&(1<<(7-(x&7))))
data32[i] &= 0x00ffffff;
i++;
}
buf += (h.Width+7)>>3;
}
}
return (qbyte *)data32; return (qbyte *)data32;
} }
else if (h.BitCount == 24) //24 bit... no 16? else if (h.BitCount == 24) //24 bit... no 16?
{ {
int x, y; int x, y;
buf += h.OffsetofBMPBits; if (OffsetofBMPBits)
buf += OffsetofBMPBits;
else
buf += h.SizeofBITMAPINFOHEADER;
data = BZ_Malloc(h.Width * h.Height*4); data = BZ_Malloc(h.Width * h.Height*4);
for (y = 0; y < h.Height; y++) for (y = 0; y < h.Height; y++)
{ {
@ -2202,12 +2233,50 @@ qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height)
return data; return data;
} }
else if (h.BitCount == 32)
{
int x, y;
if (OffsetofBMPBits)
buf += OffsetofBMPBits;
else
buf += h.SizeofBITMAPINFOHEADER;
data = BZ_Malloc(h.Width * h.Height*4);
for (y = 0; y < h.Height; y++)
{
i = (h.Height-1-y) * (h.Width);
for (x = 0; x < h.Width; x++)
{
data[i*4+0] = buf[x*4+2];
data[i*4+1] = buf[x*4+1];
data[i*4+2] = buf[x*4+0];
data[i*4+3] = buf[x*4+3];
i++;
}
buf += h.Width*4;
}
return data;
}
else else
return NULL; return NULL;
return NULL; return NULL;
} }
qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height)
{
unsigned short Type = buf[0] | (buf[1]<<8);
unsigned short Size = buf[2] | (buf[3]<<8) | (buf[4]<<16) | (buf[5]<<24);
// unsigned short Reserved1 = buf[6] | (buf[7]<<8);
// unsigned short Reserved2 = buf[8] | (buf[9]<<8);
unsigned short OffsetofBMPBits = buf[10] | (buf[11]<<8) | (buf[12]<<16) | (buf[13]<<24);
if (Type != ('B'|('M'<<8)))
return NULL;
if (Size > length)
return NULL; //it got truncated at some point
return ReadRawBMPFile(buf + 14, length-14, width, height, OffsetofBMPBits - 14);
}
qboolean WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, int instride, int width, int height, uploadfmt_t fmt) qboolean WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, int instride, int width, int height, uploadfmt_t fmt)
{ {
int y; int y;
@ -2218,6 +2287,7 @@ qboolean WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, int in
int outstride; int outstride;
int bits = 32; int bits = 32;
int extraheadersize = sizeof(h4); int extraheadersize = sizeof(h4);
size_t fsize;
memset(&h4, 0, sizeof(h4)); memset(&h4, 0, sizeof(h4));
h4.ColourSpace[0] = 'W'; h4.ColourSpace[0] = 'W';
@ -2274,10 +2344,10 @@ qboolean WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, int in
outstride = width * (bits/8); outstride = width * (bits/8);
outstride = (outstride+3)&~3; //bmp pads rows to a multiple of 4 bytes. outstride = (outstride+3)&~3; //bmp pads rows to a multiple of 4 bytes.
h.Size = 2+sizeof(h)+extraheadersize + outstride*height; // h.Size = 14+sizeof(h)+extraheadersize + outstride*height;
h.Reserved1 = 0; // h.Reserved1 = 0;
h.Reserved2 = 0; // h.Reserved2 = 0;
h.OffsetofBMPBits = 2+sizeof(h)+extraheadersize; //yes, this is misaligned. // h.OffsetofBMPBits = 2+sizeof(h)+extraheadersize; //yes, this is misaligned.
h.SizeofBITMAPINFOHEADER = (sizeof(h)-12)+extraheadersize; h.SizeofBITMAPINFOHEADER = (sizeof(h)-12)+extraheadersize;
h.Width = width; h.Width = width;
h.Height = height; h.Height = height;
@ -2294,14 +2364,38 @@ qboolean WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, int in
in += instride*(height-1); in += instride*(height-1);
instride *= -1; instride *= -1;
out = data = BZ_Malloc(h.Size); fsize = 14+sizeof(h)+extraheadersize + outstride*height; //size
out = data = BZ_Malloc(fsize);
//Type
*out++ = 'B'; *out++ = 'B';
*out++ = 'M'; *out++ = 'M';
//Size
*out++ = fsize&0xff;
*out++ = (fsize>>8)&0xff;
*out++ = (fsize>>16)&0xff;
*out++ = (fsize>>24)&0xff;
//Reserved1
y = 0;
*out++ = y&0xff;
*out++ = (y>>8)&0xff;
//Reserved1
y = 0;
*out++ = y&0xff;
*out++ = (y>>8)&0xff;
//OffsetofBMPBits
y = 2+sizeof(h)+extraheadersize; //yes, this is misaligned.
*out++ = y&0xff;
*out++ = (y>>8)&0xff;
*out++ = (y>>16)&0xff;
*out++ = (y>>24)&0xff;
//bmpheader
memcpy(out, &h, sizeof(h)); memcpy(out, &h, sizeof(h));
out += sizeof(h); out += sizeof(h);
//v4 header
memcpy(out, &h4, extraheadersize); memcpy(out, &h4, extraheadersize);
out += extraheadersize; out += extraheadersize;
//data
for (y = 0; y < height; y++) for (y = 0; y < height; y++)
{ {
memcpy(out, in, width * (bits/8)); memcpy(out, in, width * (bits/8));
@ -2310,12 +2404,69 @@ qboolean WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, int in
in += instride; in += instride;
} }
COM_WriteFile(filename, fsroot, data, h.Size); COM_WriteFile(filename, fsroot, data, fsize);
BZ_Free(data); BZ_Free(data);
return true; return true;
} }
static qbyte *ReadICOFile(qbyte *buf, int length, int *width, int *height, const char *fname)
{
qbyte *ret;
size_t imgcount = buf[4] | (buf[5]<<8);
struct
{
qbyte bWidth;
qbyte bHeight;
qbyte bColorCount;
qbyte bReserved;
unsigned short wPlanes;
unsigned short wBitCount;
unsigned short dwSize_low;
unsigned short dwSize_high;
unsigned short dwOffset_low;
unsigned short dwOffset_high;
} *img = (void*)(buf+6), *bestimg = NULL;
size_t bestpixels = 0;
size_t bestdepth = 0;
//always favour the png first
for (imgcount = buf[4] | (buf[5]<<8), img = (void*)(buf+6); imgcount-->0; img++)
{
size_t cc = img->wBitCount;
size_t px = (img->bWidth?img->bWidth:256) * (img->bHeight?img->bHeight:256);
if (!cc) //if that was omitted, try and guess it based on raw image size. this is an over estimate.
cc = 8 * (bestimg->dwSize_low | (bestimg->dwSize_high<<16)) / px;
if (!bestimg || cc > bestdepth || (cc == bestdepth && px > bestpixels))
{
bestimg = img;
bestdepth = cc;
bestpixels = px;
}
}
if (bestimg)
{
qbyte *indata = buf + (bestimg->dwOffset_low | (bestimg->dwOffset_high<<16));
size_t insize = (bestimg->dwSize_low | (bestimg->dwSize_high<<16));
#ifdef AVAIL_PNGLIB
if (insize > 4 && (indata[0] == 137 && indata[1] == 'P' && indata[2] == 'N' && indata[3] == 'G') && (ret = ReadPNGFile(indata, insize, width, height, fname)))
{
TRACE(("dbg: Read32BitImageFile: icon png\n"));
return ret;
}
else
#endif
if ((ret = ReadRawBMPFile(indata, insize, width, height, 0)))
{
TRACE(("dbg: Read32BitImageFile: icon png\n"));
return ret;
}
}
return NULL;
}
#ifndef NPFTE #ifndef NPFTE
@ -2784,7 +2935,13 @@ qbyte *Read32BitImageFile(qbyte *buf, int len, int *width, int *height, qboolean
if (len > 2 && (buf[0] == 'B' && buf[1] == 'M') && (data = ReadBMPFile(buf, len, width, height))) if (len > 2 && (buf[0] == 'B' && buf[1] == 'M') && (data = ReadBMPFile(buf, len, width, height)))
{ {
TRACE(("dbg: Read32BitImageFile: bitmap\n")); TRACE(("dbg: Read32BitImageFile: bmp\n"));
return data;
}
if (len > 6 && buf[0]==0&&buf[1]==0 && buf[2]==1&&buf[3]==0 && (data = ReadICOFile(buf, len, width, height, fname)))
{
TRACE(("dbg: Read32BitImageFile: ico\n"));
return data; return data;
} }
@ -4220,12 +4377,29 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
mips->mip[0].data = rgbadata; mips->mip[0].data = rgbadata;
else else
{ {
switch(mips->encoding)
{
case PTI_RGBA8:
case PTI_RGBX8:
case PTI_BGRA8:
case PTI_BGRX8:
case PTI_RGBA8_SRGB:
case PTI_RGBX8_SRGB:
case PTI_BGRA8_SRGB:
case PTI_BGRX8_SRGB:
mips->mip[0].data = BZ_Malloc(((mips->mip[0].width+3)&~3)*mips->mip[0].height*4); mips->mip[0].data = BZ_Malloc(((mips->mip[0].width+3)&~3)*mips->mip[0].height*4);
//FIXME: should be sRGB-aware, but probably not a common path on hardware that can actually do srgb. //FIXME: should be sRGB-aware, but probably not a common path on hardware that can actually do srgb.
Image_ResampleTexture(rgbadata, imgwidth, imgheight, mips->mip[0].data, mips->mip[0].width, mips->mip[0].height); Image_ResampleTexture(rgbadata, imgwidth, imgheight, mips->mip[0].data, mips->mip[0].width, mips->mip[0].height);
if (freedata) if (freedata)
BZ_Free(rgbadata); BZ_Free(rgbadata);
freedata = true; freedata = true;
break;
default: //scaling not supported...
mips->mip[0].data = rgbadata;
mips->mip[0].width = imgwidth;
mips->mip[0].height = imgheight;
break;
}
} }
} }
else else

View file

@ -2,6 +2,11 @@
#include <SDL.h> #include <SDL.h>
#if SDL_VERSION_ATLEAST(2,0,6) && defined(VKQUAKE)
#include <SDL_vulkan.h>
#include "../vk/vkrenderer.h"
#endif
#if SDL_MAJOR_VERSION >=2 #if SDL_MAJOR_VERSION >=2
SDL_Window *sdlwindow; SDL_Window *sdlwindow;
#else #else
@ -776,6 +781,17 @@ void Sys_SendKeyEvents(void)
default: default:
break; break;
case SDL_WINDOWEVENT_SIZE_CHANGED: case SDL_WINDOWEVENT_SIZE_CHANGED:
#if SDL_VERSION_ATLEAST(2,0,6) && defined(VKQUAKE)
if (qrenderer == QR_VULKAN)
{
unsigned window_width, window_height;
SDL_Vulkan_GetDrawableSize(sdlwindow, &window_width, &window_height); //get the proper physical size.
if (vid.pixelwidth != window_width || vid.pixelheight != window_height)
vk.neednewswapchain = true;
}
else
#endif
{
#if SDL_PATCHLEVEL >= 1 #if SDL_PATCHLEVEL >= 1
SDL_GL_GetDrawableSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight); //get the proper physical size. SDL_GL_GetDrawableSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight); //get the proper physical size.
#else #else
@ -786,6 +802,7 @@ void Sys_SendKeyEvents(void)
Cvar_ForceCallback(&vid_conautoscale); Cvar_ForceCallback(&vid_conautoscale);
Cvar_ForceCallback(&vid_conwidth); Cvar_ForceCallback(&vid_conwidth);
} }
}
break; break;
case SDL_WINDOWEVENT_FOCUS_GAINED: case SDL_WINDOWEVENT_FOCUS_GAINED:
vid.activeapp = true; vid.activeapp = true;

View file

@ -187,7 +187,9 @@ static char *manifestpackages; //metapackage named by the manicfest.
static char *declinedpackages; //metapackage named by the manicfest. static char *declinedpackages; //metapackage named by the manicfest.
static int domanifestinstall; //SECURITY_MANIFEST_* static int domanifestinstall; //SECURITY_MANIFEST_*
#ifdef PLUGINS
static qboolean pluginpromptshown; //so we only show prompts for new externally-installed plugins once, instead of every time the file is reloaded. static qboolean pluginpromptshown; //so we only show prompts for new externally-installed plugins once, instead of every time the file is reloaded.
#endif
static qboolean doautoupdate; //updates will be marked (but not applied without the user's actions) static qboolean doautoupdate; //updates will be marked (but not applied without the user's actions)
static qboolean pkg_updating; //when flagged, further changes are blocked until completion. static qboolean pkg_updating; //when flagged, further changes are blocked until completion.
@ -2550,6 +2552,7 @@ void PM_ManifestPackage(const char *metaname, int security)
void PM_Command_f(void) void PM_Command_f(void)
{ {
size_t i;
package_t *p; package_t *p;
const char *act = Cmd_Argv(1); const char *act = Cmd_Argv(1);
const char *key; const char *key;
@ -2706,8 +2709,13 @@ void PM_Command_f(void)
{ {
PM_RevertChanges(); PM_RevertChanges();
} }
else if (!strcmp(act, "update"))
{ //flush package cache, make a new request.
for (i = 0; i < numdownloadablelists; i++)
downloadablelist[i].received = 0;
}
else if (!strcmp(act, "upgrade")) else if (!strcmp(act, "upgrade"))
{ { //auto-mark any updated packages.
unsigned int changes = PM_MarkUpdates(); unsigned int changes = PM_MarkUpdates();
if (changes) if (changes)
{ {
@ -2718,7 +2726,7 @@ void PM_Command_f(void)
Con_Printf("Already using latest versions of all packages\n"); Con_Printf("Already using latest versions of all packages\n");
} }
else if (!strcmp(act, "add") || !strcmp(act, "get") || !strcmp(act, "install") || !strcmp(act, "enable")) else if (!strcmp(act, "add") || !strcmp(act, "get") || !strcmp(act, "install") || !strcmp(act, "enable"))
{ { //FIXME: make sure this updates.
int arg = 2; int arg = 2;
for (arg = 2; arg < Cmd_Argc(); arg++) for (arg = 2; arg < Cmd_Argc(); arg++)
{ {
@ -2732,7 +2740,7 @@ void PM_Command_f(void)
PM_PrintChanges(); PM_PrintChanges();
} }
else if (!strcmp(act, "reinstall")) else if (!strcmp(act, "reinstall"))
{ { //fixme: favour the current verson.
int arg = 2; int arg = 2;
for (arg = 2; arg < Cmd_Argc(); arg++) for (arg = 2; arg < Cmd_Argc(); arg++)
{ {
@ -2784,7 +2792,7 @@ void PM_Command_f(void)
PM_PrintChanges(); PM_PrintChanges();
} }
else else
Con_Printf("%s: Unknown action %s\nShould be one of list, show, search, revert, add, rem, del, changes, apply\n", Cmd_Argv(0), act); Con_Printf("%s: Unknown action %s\nShould be one of list, show, search, upgrade, revert, add, rem, del, changes, apply\n", Cmd_Argv(0), act);
} }
qboolean PM_FindUpdatedEngine(char *syspath, size_t syspathsize) qboolean PM_FindUpdatedEngine(char *syspath, size_t syspathsize)

View file

@ -2434,9 +2434,7 @@ cin_t *Media_StartCin(char *name)
if (!cin) if (!cin)
cin = Media_Plugin_TryLoad(name); cin = Media_Plugin_TryLoad(name);
#endif #endif
if (!cin) if (cin)
Con_Printf("Unable to decode \"%s\"\n", name);
else
cin->filmstarttime = realtime; cin->filmstarttime = realtime;
return cin; return cin;
} }
@ -2465,7 +2463,6 @@ qboolean Media_BeginNextFilm(void)
} }
videoshader = R_RegisterCustom(sname, SUF_NONE, Shader_DefaultCinematic, p->name); videoshader = R_RegisterCustom(sname, SUF_NONE, Shader_DefaultCinematic, p->name);
Z_Free(p);
cin = R_ShaderGetCinematic(videoshader); cin = R_ShaderGetCinematic(videoshader);
if (cin) if (cin)
@ -2474,16 +2471,15 @@ qboolean Media_BeginNextFilm(void)
Media_Send_Reset(cin); Media_Send_Reset(cin);
if (cin->changestream) if (cin->changestream)
cin->changestream(cin, "cmd:focus"); cin->changestream(cin, "cmd:focus");
return true;
} }
else else
{ {
Con_Printf("Unable to play cinematic %s\n", p->name);
R_UnloadShader(videoshader); R_UnloadShader(videoshader);
videoshader = NULL; videoshader = NULL;
return false;
} }
Z_Free(p);
return !!videoshader;
} }
qboolean Media_StopFilm(qboolean all) qboolean Media_StopFilm(qboolean all)
{ {
@ -2515,6 +2511,8 @@ qboolean Media_StopFilm(qboolean all)
} }
//for q2 cinematic-maps. //for q2 cinematic-maps.
//q2 sends 'nextserver' when the client's cinematics end so that the game can progress to the next map.
//qc might want to make use of it too, but its probably best to just send a playfilm command at the start of the map and then just wait it out. maybe treat nextserver as an unpause request.
if (!videoshader && cls.state == ca_active) if (!videoshader && cls.state == ca_active)
{ {
CL_SendClientCommand(true, "nextserver %i", cl.servercount); CL_SendClientCommand(true, "nextserver %i", cl.servercount);
@ -2819,8 +2817,19 @@ void Media_PlayFilm_f (void)
void Media_PlayVideoWindowed_f (void) void Media_PlayVideoWindowed_f (void)
{ {
char *videomap = Cmd_Argv(1); char *videomap = Cmd_Argv(1);
shader_t *s;
console_t *con;
if (!qrenderer)
return;
s = R_RegisterCustom(va("consolevid_%s", videomap), SUF_NONE, Shader_DefaultCinematic, videomap);
if (!R_ShaderGetCinematic(s))
{
R_UnloadShader(s);
Con_Printf("Unable to load video %s\n", videomap);
return;
}
console_t *con = Con_Create(videomap, 0); con = Con_Create(videomap, 0);
if (!con) if (!con)
return; return;
con->parseflags = PFS_FORCEUTF8; con->parseflags = PFS_FORCEUTF8;
@ -2834,7 +2843,7 @@ void Media_PlayVideoWindowed_f (void)
Q_strncpyz(con->backimage, "", sizeof(con->backimage)); Q_strncpyz(con->backimage, "", sizeof(con->backimage));
if (con->backshader) if (con->backshader)
R_UnloadShader(con->backshader); R_UnloadShader(con->backshader);
con->backshader = R_RegisterCustom(va("consolevid_%s", videomap), SUF_NONE, Shader_DefaultCinematic, videomap); con->backshader = s;
Con_SetActive(con); Con_SetActive(con);
} }
@ -3526,9 +3535,8 @@ void Media_RecordFrame (void)
buffer = qglMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB); buffer = qglMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);
if (buffer) if (buffer)
{ {
qbyte *firstrow = (offscreen_queue[frame].stride<0)?buffer - offscreen_queue[frame].stride*(offscreen_queue[frame].height-1):buffer;
//FIXME: thread these (with audio too, to avoid races) //FIXME: thread these (with audio too, to avoid races)
currentcapture_funcs->capture_video(currentcapture_ctx, offscreen_captureframe, firstrow, offscreen_queue[frame].stride, offscreen_queue[frame].width, offscreen_queue[frame].height, offscreen_queue[frame].format); currentcapture_funcs->capture_video(currentcapture_ctx, offscreen_captureframe, buffer, offscreen_queue[frame].stride, offscreen_queue[frame].width, offscreen_queue[frame].height, offscreen_queue[frame].format);
qglUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); qglUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);
} }
offscreen_captureframe++; offscreen_captureframe++;
@ -3536,12 +3544,14 @@ void Media_RecordFrame (void)
frame = captureframe%countof(offscreen_queue); frame = captureframe%countof(offscreen_queue);
//if we have no pbo yet, create one. //if we have no pbo yet, create one.
if (!offscreen_queue[frame].pbo_handle) if (!offscreen_queue[frame].pbo_handle || offscreen_queue[frame].width != vid.fbpwidth || offscreen_queue[frame].height != vid.fbpheight)
{ {
int imagesize = 0; int imagesize = 0;
if (offscreen_queue[frame].pbo_handle)
qglDeleteBuffersARB(1, &offscreen_queue[frame].pbo_handle);
offscreen_queue[frame].format = offscreen_format; offscreen_queue[frame].format = offscreen_format;
offscreen_queue[frame].width = vid.pixelwidth; offscreen_queue[frame].width = vid.fbpwidth;
offscreen_queue[frame].height = vid.pixelheight; offscreen_queue[frame].height = vid.fbpheight;
switch(offscreen_queue[frame].format) switch(offscreen_queue[frame].format)
{ {
case TF_BGR24: case TF_BGR24:
@ -3556,13 +3566,13 @@ void Media_RecordFrame (void)
break; break;
} }
offscreen_queue[frame].stride = vid.pixelwidth*-imagesize;//gl is upside down offscreen_queue[frame].stride = vid.fbpwidth*-imagesize;//gl is upside down
imagesize *= offscreen_queue[frame].width * offscreen_queue[frame].height; imagesize *= offscreen_queue[frame].width * offscreen_queue[frame].height;
qglGenBuffersARB(1, &offscreen_queue[frame].pbo_handle); qglGenBuffersARB(1, &offscreen_queue[frame].pbo_handle);
qglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, offscreen_queue[frame].pbo_handle); qglBindBufferARB(GL_PIXEL_PACK_BUFFER_ARB, offscreen_queue[frame].pbo_handle);
qglBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, imagesize, NULL, GL_STATIC_READ_ARB); qglBufferDataARB(GL_PIXEL_PACK_BUFFER_ARB, imagesize, NULL, GL_STREAM_READ_ARB);
} }
//get the gpu to copy the texture into the pbo. the driver should pipeline this read until we actually map the pbo, hopefully avoiding stalls //get the gpu to copy the texture into the pbo. the driver should pipeline this read until we actually map the pbo, hopefully avoiding stalls
@ -3671,7 +3681,7 @@ static unsigned int MSD_GetDMAPos(soundcardinfo_t *sc)
s = captureframe*(sc->sn.speed*captureframeinterval); s = captureframe*(sc->sn.speed*captureframeinterval);
// s >>= (sc->sn.samplebits/8) - 1; // s >>= sc->sn.samplebytes - 1;
s *= sc->sn.numchannels; s *= sc->sn.numchannels;
return s; return s;
} }
@ -3701,7 +3711,7 @@ static void MSD_Submit(soundcardinfo_t *sc, int start, int end)
if (framestosubmit > maxframes) if (framestosubmit > maxframes)
framestosubmit = maxframes; framestosubmit = maxframes;
bytesperframe = sc->sn.numchannels*sc->sn.samplebits/8; bytesperframe = sc->sn.numchannels*sc->sn.samplebytes;
offset = (lastpos % (sc->sn.samples/sc->sn.numchannels)); offset = (lastpos % (sc->sn.samples/sc->sn.numchannels));
@ -3759,7 +3769,22 @@ void Media_InitFakeSoundDevice (int speed, int channels, int samplebits)
sc->sn.samples = speed*0.5; sc->sn.samples = speed*0.5;
sc->sn.speed = speed; sc->sn.speed = speed;
sc->sn.samplebits = samplebits; switch(samplebits)
{
case 32:
sc->sn.samplebytes = 4;
sc->sn.sampleformat = QSF_F32;
break;
default:
case 16:
sc->sn.samplebytes = 2;
sc->sn.sampleformat = QSF_S16;
break;
case 8:
sc->sn.samplebytes = 1;
sc->sn.sampleformat = QSF_U8;
break;
}
sc->sn.samplepos = 0; sc->sn.samplepos = 0;
sc->sn.numchannels = channels; sc->sn.numchannels = channels;
sc->inactive_sound = true; sc->inactive_sound = true;
@ -3767,7 +3792,7 @@ void Media_InitFakeSoundDevice (int speed, int channels, int samplebits)
sc->sn.samples -= sc->sn.samples%1152; //truncate slightly to keep vfw happy. sc->sn.samples -= sc->sn.samples%1152; //truncate slightly to keep vfw happy.
sc->samplequeue = -1; sc->samplequeue = -1;
sc->sn.buffer = (unsigned char *) BZ_Malloc(sc->sn.samples*sc->sn.numchannels*(sc->sn.samplebits/8)); sc->sn.buffer = (unsigned char *) BZ_Malloc(sc->sn.samples*sc->sn.numchannels*sc->sn.samplebytes);
Z_ReallocElements((void**)&sc->channel, &sc->max_chans, MAX_DYNAMIC_CHANNELS+NUM_AMBIENTS+NUM_MUSICS, sizeof(*sc->channel)); Z_ReallocElements((void**)&sc->channel, &sc->max_chans, MAX_DYNAMIC_CHANNELS+NUM_AMBIENTS+NUM_MUSICS, sizeof(*sc->channel));
@ -3801,8 +3826,7 @@ void Media_StopRecordFilm_f (void)
buffer = qglMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB); buffer = qglMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);
if (buffer) if (buffer)
{ {
qbyte *firstrow = (offscreen_queue[frame].stride<0)?buffer - offscreen_queue[frame].stride*(offscreen_queue[frame].height-1):buffer; currentcapture_funcs->capture_video(currentcapture_ctx, offscreen_captureframe, buffer, offscreen_queue[frame].stride, offscreen_queue[frame].width, offscreen_queue[frame].height, offscreen_queue[frame].format);
currentcapture_funcs->capture_video(currentcapture_ctx, offscreen_captureframe, firstrow, offscreen_queue[frame].stride, offscreen_queue[frame].width, offscreen_queue[frame].height, offscreen_queue[frame].format);
qglUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB); qglUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);
} }
offscreen_captureframe++; offscreen_captureframe++;
@ -3931,8 +3955,8 @@ static void Media_RecordFilm (char *recordingname, qboolean demo)
capturingfbo = true; capturingfbo = true;
capturetexture = R2D_RT_Configure("$democapture", capturewidth.ival, captureheight.ival, TF_BGRA32, RT_IMAGEFLAGS); capturetexture = R2D_RT_Configure("$democapture", capturewidth.ival, captureheight.ival, TF_BGRA32, RT_IMAGEFLAGS);
captureoldfbo = GLBE_FBO_Update(&capturefbo, FBO_RB_DEPTH|(Sh_StencilShadowsActive()?FBO_RB_STENCIL:0), &capturetexture, 1, r_nulltex, capturewidth.ival, captureheight.ival, 0); captureoldfbo = GLBE_FBO_Update(&capturefbo, FBO_RB_DEPTH|(Sh_StencilShadowsActive()?FBO_RB_STENCIL:0), &capturetexture, 1, r_nulltex, capturewidth.ival, captureheight.ival, 0);
vid.fbpwidth = capturewidth.ival; vid.fbpwidth = capturetexture->width;
vid.fbpheight = captureheight.ival; vid.fbpheight = capturetexture->height;
vid.framebuffer = capturetexture; vid.framebuffer = capturetexture;
} }
#endif #endif

View file

@ -1376,7 +1376,16 @@ void P_ParticleEffect_f(void)
} }
else if (!strcmp(var, "alpha")) else if (!strcmp(var, "alpha"))
{
ptype->alpha = atof(value); ptype->alpha = atof(value);
if (Cmd_Argc()>2)
ptype->alpharand = atof(Cmd_Argv(2)) - ptype->alpha;
if (Cmd_Argc()>3)
{
ptype->alphachange = atof(Cmd_Argv(3));
setalphadelta = true;
}
}
else if (!strcmp(var, "alpharand")) else if (!strcmp(var, "alpharand"))
ptype->alpharand = atof(value); ptype->alpharand = atof(value);
#ifndef NOLEGACY #ifndef NOLEGACY

View file

@ -496,7 +496,7 @@ void QCBUILTIN PF_cs_media_create (pubprogfuncs_t *prinst, struct globalvars_s *
"videomap %s\n" "videomap %s\n"
"rgbgen vertex\n" "rgbgen vertex\n"
"alphagen vertex\n" "alphagen vertex\n"
"blendfunc blend\n" "blendfunc gl_one gl_one_minus_src_alpha\n"
"nodepth\n" "nodepth\n"
"}\n" "}\n"
"}\n", "}\n",
@ -537,17 +537,20 @@ void QCBUILTIN PF_cs_media_command (pubprogfuncs_t *prinst, struct globalvars_s
return; return;
Media_Send_Command(cin, command); Media_Send_Command(cin, command);
} }
// #490 float(string name, float key, float eventtype) gecko_keyevent // #490 float(string name, float key, float eventtype, optional float charcode) gecko_keyevent
void QCBUILTIN PF_cs_media_keyevent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) void QCBUILTIN PF_cs_media_keyevent (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{ {
const char *shader = PR_GetStringOfs(prinst, OFS_PARM0); const char *shader = PR_GetStringOfs(prinst, OFS_PARM0);
int key = G_FLOAT(OFS_PARM1); int key = G_FLOAT(OFS_PARM1);
int eventtype = G_FLOAT(OFS_PARM2); int eventtype = G_FLOAT(OFS_PARM2);
int charcode = (prinst->callargc>3)?G_FLOAT(OFS_PARM3):((key>127)?0:key);
cin_t *cin; cin_t *cin;
cin = R_ShaderFindCinematic(shader); cin = R_ShaderFindCinematic(shader);
G_FLOAT(OFS_RETURN) = 0;
if (!cin) if (!cin)
return; return;
Media_Send_KeyEvent(cin, MP_TranslateQCtoFTECodes(key), (key>127)?0:key, eventtype); Media_Send_KeyEvent(cin, MP_TranslateQCtoFTECodes(key), charcode, eventtype);
G_FLOAT(OFS_RETURN) = 1;
} }
// #491 void(string name, float x, float y) gecko_mousemove // #491 void(string name, float x, float y) gecko_mousemove
void QCBUILTIN PF_cs_media_mousemove (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) void QCBUILTIN PF_cs_media_mousemove (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)

View file

@ -348,9 +348,9 @@ static void CSQC_FindGlobals(qboolean nofuncs)
{ {
etype_t etype = ev_void; etype_t etype = ev_void;
if (!csqcg.trace_surfaceflagsi) if (!csqcg.trace_surfaceflagsi)
csqcg.trace_surfaceflagsi = PR_FindGlobal(csqcprogs, "trace_surfaceflags", 0, &etype); csqcg.trace_surfaceflagsi = (int*)PR_FindGlobal(csqcprogs, "trace_surfaceflags", 0, &etype);
if (!csqcg.trace_endcontentsi) if (!csqcg.trace_endcontentsi)
csqcg.trace_endcontentsi = PR_FindGlobal(csqcprogs, "trace_endcontents", 0, &etype); csqcg.trace_endcontentsi = (int*)PR_FindGlobal(csqcprogs, "trace_endcontents", 0, &etype);
} }
#else #else
if (!csqcg.trace_surfaceflagsf && !csqcg.trace_surfaceflagsi) if (!csqcg.trace_surfaceflagsf && !csqcg.trace_surfaceflagsi)
@ -1885,14 +1885,24 @@ void QCBUILTIN PF_R_GetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_
case VF_SIZE_X: case VF_SIZE_X:
*r = r_refdef.grect.width; *r = r_refdef.grect.width;
if (csqc_isdarkplaces)
*r *= (float)vid.pixelwidth / vid.width;
break; break;
case VF_SIZE_Y: case VF_SIZE_Y:
*r = r_refdef.grect.height; *r = r_refdef.grect.height;
if (csqc_isdarkplaces)
*r *= (float)vid.pixelheight / vid.height;
break; break;
case VF_SIZE: case VF_SIZE:
r[0] = r_refdef.grect.width; r[0] = r_refdef.grect.width;
r[1] = r_refdef.grect.height; r[1] = r_refdef.grect.height;
r[2] = 0; r[2] = 0;
if (csqc_isdarkplaces)
{
r[0] *= (float)vid.pixelwidth / vid.width;
r[1] *= (float)vid.pixelheight / vid.height;
}
break; break;
case VF_MIN_X: case VF_MIN_X:
@ -2076,6 +2086,12 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_
r_refdef.grect.width = p[0]; r_refdef.grect.width = p[0];
r_refdef.grect.height = p[1]; r_refdef.grect.height = p[1];
r_refdef.dirty |= RDFD_FOV; r_refdef.dirty |= RDFD_FOV;
if (csqc_isdarkplaces)
{
r_refdef.grect.width *= (float)vid.width / vid.pixelwidth;
r_refdef.grect.height *= (float)vid.height / vid.pixelheight;
}
break; break;
case VF_MIN_X: case VF_MIN_X:
@ -7671,8 +7687,16 @@ qboolean CSQC_DrawView(void)
{ {
void *pr_globals = PR_globals(csqcprogs, PR_CURRENT); void *pr_globals = PR_globals(csqcprogs, PR_CURRENT);
if (csqc_isdarkplaces)
{ //fucked for compatibility.
G_FLOAT(OFS_PARM0) = vid.pixelwidth;
G_FLOAT(OFS_PARM1) = vid.pixelheight;
}
else
{
G_FLOAT(OFS_PARM0) = vid.width; G_FLOAT(OFS_PARM0) = vid.width;
G_FLOAT(OFS_PARM1) = vid.height; G_FLOAT(OFS_PARM1) = vid.height;
}
G_FLOAT(OFS_PARM2) = !Key_Dest_Has(kdm_emenu) && !r_refdef.eyeoffset[0] && !r_refdef.eyeoffset[1]; G_FLOAT(OFS_PARM2) = !Key_Dest_Has(kdm_emenu) && !r_refdef.eyeoffset[0] && !r_refdef.eyeoffset[1];
if (csqcg.f_updateviewloading && cls.state && cls.state < ca_active) if (csqcg.f_updateviewloading && cls.state && cls.state < ca_active)

View file

@ -1124,8 +1124,10 @@ extern rendererinfo_t swrendererinfo;
#ifdef VKQUAKE #ifdef VKQUAKE
extern rendererinfo_t vkrendererinfo; extern rendererinfo_t vkrendererinfo;
//rendererinfo_t headlessvkrendererinfo; //rendererinfo_t headlessvkrendererinfo;
#if defined(_WIN32) && defined(GLQUAKE) && !defined(FTE_SDL)
extern rendererinfo_t nvvkrendererinfo; extern rendererinfo_t nvvkrendererinfo;
#endif #endif
#endif
#ifdef HEADLESSQUAKE #ifdef HEADLESSQUAKE
extern rendererinfo_t headlessrenderer; extern rendererinfo_t headlessrenderer;
#endif #endif
@ -1154,7 +1156,7 @@ rendererinfo_t *rendererinfo[] =
#endif #endif
#ifdef VKQUAKE #ifdef VKQUAKE
&vkrendererinfo, &vkrendererinfo,
#if defined(_WIN32) && defined(GLQUAKE) #if defined(_WIN32) && defined(GLQUAKE) && !defined(FTE_SDL)
&nvvkrendererinfo, &nvvkrendererinfo,
#endif #endif
#endif #endif

View file

@ -309,6 +309,7 @@ int i;
if((fp = FS_OpenVFS(fname, "rb", FS_GAME)) == NULL) if((fp = FS_OpenVFS(fname, "rb", FS_GAME)) == NULL)
{ {
if((fp = FS_OpenVFS(va("video/%s.roq", fname), "rb", FS_GAME)) == NULL) //for q3 compat
return NULL; return NULL;
} }

View file

@ -484,7 +484,7 @@ qboolean OpenAL_LoadCache(unsigned int *bufptr, sfxcache_t *sc, float volume)
return true; return true;
} }
void OpenAL_CvarInit(void) static void QDECL OpenAL_CvarInit(void)
{ {
Cvar_Register(&s_al_debug, SOUNDVARS); Cvar_Register(&s_al_debug, SOUNDVARS);
Cvar_Register(&s_al_use_reverb, SOUNDVARS); Cvar_Register(&s_al_use_reverb, SOUNDVARS);
@ -1347,6 +1347,7 @@ static qboolean QDECL OpenAL_InitCard(soundcardinfo_t *sc, const char *devname)
sc->inactive_sound = true; sc->inactive_sound = true;
sc->selfpainting = true; sc->selfpainting = true;
sc->sn.sampleformat = QSF_EXTERNALMIXER;
OnChangeALSettings(NULL, NULL); OnChangeALSettings(NULL, NULL);
@ -1397,7 +1398,8 @@ sounddriver_t OPENAL_Output =
{ {
SDRVNAME, SDRVNAME,
OpenAL_InitCard, OpenAL_InitCard,
OpenAL_Enumerate OpenAL_Enumerate,
OpenAL_CvarInit
}; };

View file

@ -137,7 +137,7 @@ static void ALSA_RW_Submit (soundcardinfo_t *sc, int start, int end)
unsigned int frames, offset, ringsize; unsigned int frames, offset, ringsize;
unsigned chunk; unsigned chunk;
int result; int result;
int stride = sc->sn.numchannels * (sc->sn.samplebits/8); int stride = sc->sn.numchannels * sc->sn.samplebytes;
while(1) while(1)
{ {
@ -323,7 +323,20 @@ static qboolean QDECL ALSA_InitCard (soundcardinfo_t *sc, const char *pcmname)
Con_Printf ("ALSA: Using PCM %s.\n", pcmname); Con_Printf ("ALSA: Using PCM %s.\n", pcmname);
#if 1 #if 1
err = psnd_pcm_set_params(pcm, ((sc->sn.samplebits==8)?SND_PCM_FORMAT_U8:SND_PCM_FORMAT_S16), (mmap?SND_PCM_ACCESS_MMAP_INTERLEAVED:SND_PCM_ACCESS_RW_INTERLEAVED), sc->sn.numchannels, sc->sn.speed, true, 0.04*1000000); if (!sc->sn.sampleformat)
sc->sn.sampleformat = (sc->sn.samplebytes==1)?QSF_U8:QSF_S16;
switch(sc->sn.sampleformat)
{
case QSF_U8: err = SND_PCM_FORMAT_U8; break;
case QSF_S8: err = SND_PCM_FORMAT_S8; break;
case QSF_S16: err = SND_PCM_FORMAT_S16; break;
case QSF_F32: err = SND_PCM_FORMAT_FLOAT; break;
default:
Con_Printf (CON_ERROR "ALSA: unsupported sample format %i\n", sc->sn.sampleformat);
goto error;
}
err = psnd_pcm_set_params(pcm, err, (mmap?SND_PCM_ACCESS_MMAP_INTERLEAVED:SND_PCM_ACCESS_RW_INTERLEAVED), sc->sn.numchannels, sc->sn.speed, true, 0.04*1000000);
if (0 > err) if (0 > err)
{ {
Con_Printf (CON_ERROR "ALSA: error setting params. %s\n", psnd_strerror (err)); Con_Printf (CON_ERROR "ALSA: error setting params. %s\n", psnd_strerror (err));
@ -332,7 +345,7 @@ static qboolean QDECL ALSA_InitCard (soundcardinfo_t *sc, const char *pcmname)
// sc->sn.numchannels = stereo; // sc->sn.numchannels = stereo;
// sc->sn.samplepos = 0; // sc->sn.samplepos = 0;
// sc->sn.samplebits = bps; // sc->sn.samplebytes = bps/8;
sc->samplequeue = buffer_size = 2048; sc->samplequeue = buffer_size = 2048;
#else #else
@ -351,7 +364,7 @@ static qboolean QDECL ALSA_InitCard (soundcardinfo_t *sc, const char *pcmname)
} }
// get sample bit size // get sample bit size
bps = sc->sn.samplebits; bps = sc->sn.samplebytes*8;
{ {
snd_pcm_format_t spft; snd_pcm_format_t spft;
if (bps == 16) if (bps == 16)
@ -456,7 +469,7 @@ static qboolean QDECL ALSA_InitCard (soundcardinfo_t *sc, const char *pcmname)
sc->sn.numchannels = stereo; sc->sn.numchannels = stereo;
sc->sn.samplepos = 0; sc->sn.samplepos = 0;
sc->sn.samplebits = bps; sc->sn.samplebytes = bps/8;
buffer_size = sc->sn.samples / stereo; buffer_size = sc->sn.samples / stereo;
if (buffer_size) if (buffer_size)
@ -509,7 +522,7 @@ static qboolean QDECL ALSA_InitCard (soundcardinfo_t *sc, const char *pcmname)
sc->Submit = ALSA_RW_Submit; sc->Submit = ALSA_RW_Submit;
sc->samplequeue = sc->sn.samples; sc->samplequeue = sc->sn.samples;
sc->sn.buffer = malloc(sc->sn.samples * (sc->sn.samplebits/8)); sc->sn.buffer = malloc(sc->sn.samples * sc->sn.samplebytes);
err = psnd_pcm_prepare(pcm); err = psnd_pcm_prepare(pcm);
if (0 > err) if (0 > err)

View file

@ -531,11 +531,8 @@ static unsigned int DSOUND_GetDMAPos(soundcardinfo_t *sc)
IDirectSoundBuffer_GetCurrentPosition(dh->pDSBuf, &mmtime, &dwWrite); IDirectSoundBuffer_GetCurrentPosition(dh->pDSBuf, &mmtime, &dwWrite);
s = mmtime - dh->mmstarttime; s = mmtime - dh->mmstarttime;
s /= sc->sn.samplebytes;
s /= (sc->sn.samplebits/8);
s %= (sc->sn.samples); s %= (sc->sn.samples);
return s; return s;
} }
@ -657,15 +654,25 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname)
sc->sn.numchannels = 1; sc->sn.numchannels = 1;
} }
if (sc->sn.samplebits == 32) switch(sc->sn.samplebytes)
{ //FTE does not support 32bit int audio, rather we interpret samplebits 32 as floats. {
case 4:
//FTE does not support 32bit int audio, rather we interpret samplebits 32 as floats.
format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
format.Format.cbSize = 22; format.Format.cbSize = 22;
memcpy(&format.SubFormat, &QKSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)); memcpy(&format.SubFormat, &QKSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID));
sc->sn.sampleformat = QSF_F32;
break;
case 2:
sc->sn.sampleformat = QSF_S16;
break;
case 1:
sc->sn.sampleformat = QSF_U8;
break;
} }
format.Format.nChannels = sc->sn.numchannels; format.Format.nChannels = sc->sn.numchannels;
format.Format.wBitsPerSample = sc->sn.samplebits; format.Format.wBitsPerSample = sc->sn.samplebytes*8;
format.Format.nSamplesPerSec = sc->sn.speed; format.Format.nSamplesPerSec = sc->sn.speed;
format.Format.nBlockAlign = format.Format.nChannels * format.Format.wBitsPerSample / 8; format.Format.nBlockAlign = format.Format.nChannels * format.Format.wBitsPerSample / 8;
format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec * format.Format.nBlockAlign; format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec * format.Format.nBlockAlign;
@ -842,7 +849,7 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname)
} }
sc->sn.numchannels = format.Format.nChannels; sc->sn.numchannels = format.Format.nChannels;
sc->sn.samplebits = format.Format.wBitsPerSample; sc->sn.samplebytes = format.Format.wBitsPerSample/8;
sc->sn.speed = format.Format.nSamplesPerSec; sc->sn.speed = format.Format.nSamplesPerSec;
if (DS_OK != IDirectSoundBuffer_GetCaps (dh->pDSBuf, &dsbcaps)) if (DS_OK != IDirectSoundBuffer_GetCaps (dh->pDSBuf, &dsbcaps))
@ -902,7 +909,7 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname)
Con_DPrintf(" %d channel(s)\n" Con_DPrintf(" %d channel(s)\n"
" %d bits/sample\n" " %d bits/sample\n"
" %d bytes/sec\n", " %d bytes/sec\n",
sc->sn.numchannels, sc->sn.samplebits, sc->sn.speed); sc->sn.numchannels, sc->sn.samplebytes*8, sc->sn.speed);
// initialize the buffer // initialize the buffer
@ -938,7 +945,7 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname)
IDirectSoundBuffer_GetCurrentPosition(dh->pDSBuf, &dh->mmstarttime, &dwWrite); IDirectSoundBuffer_GetCurrentPosition(dh->pDSBuf, &dh->mmstarttime, &dwWrite);
IDirectSoundBuffer_Play(dh->pDSBuf, 0, 0, DSBPLAY_LOOPING); IDirectSoundBuffer_Play(dh->pDSBuf, 0, 0, DSBPLAY_LOOPING);
sc->sn.samples = dh->gSndBufSize/(sc->sn.samplebits/8); sc->sn.samples = dh->gSndBufSize/sc->sn.samplebytes;
sc->sn.samplepos = 0; sc->sn.samplepos = 0;
sc->sn.buffer = NULL; sc->sn.buffer = NULL;

View file

@ -220,7 +220,7 @@ void S_SoundInfo_f(void)
for (sc = sndcardinfo; sc; sc = sc->next) for (sc = sndcardinfo; sc; sc = sc->next)
{ {
Con_Printf("Audio Device: %s\n", sc->name); Con_Printf("Audio Device: %s\n", sc->name);
Con_Printf(" %d channels, %gkhz, %d bit audio%s\n", sc->sn.numchannels, sc->sn.speed/1000.0, sc->sn.samplebits, sc->selfpainting?", threaded":""); Con_Printf(" %d channels, %gkhz, %d bit audio%s\n", sc->sn.numchannels, sc->sn.speed/1000.0, sc->sn.samplebytes*8, sc->selfpainting?", threaded":"");
Con_Printf(" %d samples in buffer\n", sc->sn.samples); Con_Printf(" %d samples in buffer\n", sc->sn.samples);
for (i = 0, active = 0, known = 0; i < sc->total_chans; i++) for (i = 0, active = 0, known = 0; i < sc->total_chans; i++)
{ {
@ -1697,18 +1697,24 @@ extern sounddriver_t OPENAL_Output;
#ifdef __DJGPP__ #ifdef __DJGPP__
extern sounddriver_t SBLASTER_Output; extern sounddriver_t SBLASTER_Output;
#endif #endif
#if defined(_WIN32) && !defined(WINRT) && !defined(FTE_SDL)
sounddriver pSNDIO_InitCard; extern sounddriver_t WaveOut_Output;
sounddriver pOSS_InitCard;
sounddriver pMacOS_InitCard;
sounddriver pSDL_InitCard;
sounddriver pWAV_InitCard;
sounddriver pDroid_InitCard;
sounddriver pAHI_InitCard;
#ifdef NACL
extern sounddriver pPPAPI_InitCard;
#endif #endif
#ifdef MACOSX
sounddriver_t MacOS_AudioOutput; //prefered on mac
#endif
#ifdef ANDROID
sounddriver_t Droid_AudioOutput; //prefered on android (java thread)
#endif
#if defined(__MORPHOS__)
sounddriver_t AHI_AudioOutput; //prefered on morphos
#endif
#ifdef NACL
extern sounddriver_t PPAPI_AudioOutput; //nacl
#endif
sounddriver_t SNDIO_AudioOutput; //bsd
//in order of preference //in order of preference
static sounddriver_t *outputdrivers[] = static sounddriver_t *outputdrivers[] =
{ {
@ -1731,37 +1737,35 @@ static sounddriver_t *outputdrivers[] =
#ifdef __linux__ #ifdef __linux__
&ALSA_Output, //pure shite &ALSA_Output, //pure shite
#endif #endif
&OSS_Output, //good, but not likely to work any more &OSS_Output, //good, but not likely to work any more on linux (unlike every other unix system with a decent opengl driver)
#ifdef __DJGPP__ #ifdef __DJGPP__
&SBLASTER_Output, //zomgwtfdos? &SBLASTER_Output, //zomgwtfdos?
#endif #endif
#if defined(_WIN32) && !defined(WINRT) && !defined(FTE_SDL)
&WaveOut_Output, //doesn't work properly in vista, etc.
#endif
#ifdef MACOSX
&MacOS_AudioOutput, //prefered on mac
#endif
#ifdef ANDROID
&Droid_AudioOutput, //prefered on android (java thread)
#endif
#if defined(__MORPHOS__)
&AHI_AudioOutput, //prefered on morphos
#endif
#ifdef NACL
&PPAPI_AudioOutput, //google's native client
#endif
&SNDIO_AudioOutput, //prefered on OpenBSD
#endif #endif
NULL NULL
}; };
typedef struct {
char *name;
sounddriver *ptr;
} sdriver_t;
static sdriver_t olddrivers[] = {
#ifdef HAVE_MIXER
//in order of preference
{"MacOS", &pMacOS_InitCard}, //prefered on mac
{"Droid", &pDroid_InitCard}, //prefered on android (java thread)
{"AHI", &pAHI_InitCard}, //prefered on morphos
#ifdef NACL
{"PPAPI", &pPPAPI_InitCard}, //google's native client
#endif
{"SNDIO", &pSNDIO_InitCard}, //prefered on OpenBSD
{"WaveOut", &pWAV_InitCard}, //doesn't work properly in vista, etc.
#endif
{NULL, NULL}
};
static soundcardinfo_t *SNDDMA_Init(char *driver, char *device, int seat) static soundcardinfo_t *SNDDMA_Init(char *driver, char *device, int seat)
{ {
soundcardinfo_t *sc = Z_Malloc(sizeof(soundcardinfo_t)); soundcardinfo_t *sc = Z_Malloc(sizeof(soundcardinfo_t));
sdriver_t *od;
sounddriver_t *sd; sounddriver_t *sd;
int i; int i;
int st; int st;
@ -1803,10 +1807,12 @@ static soundcardinfo_t *SNDDMA_Init(char *driver, char *device, int seat)
sc->sn.numchannels = 1; sc->sn.numchannels = 1;
// set requested sample bits // set requested sample bits
if (snd_samplebits.ival >= 16) if (snd_samplebits.ival >= 32)
sc->sn.samplebits = 16; sc->sn.samplebytes = 4;
else if (snd_samplebits.ival >= 16)
sc->sn.samplebytes = 2;
else else
sc->sn.samplebits = 8; sc->sn.samplebytes = 1;
// set requested buffer size // set requested buffer size
if (snd_buffersize.ival > 0) if (snd_buffersize.ival > 0)
@ -1826,7 +1832,12 @@ static soundcardinfo_t *SNDDMA_Init(char *driver, char *device, int seat)
st = (**sd->InitCard)(sc, device); st = (**sd->InitCard)(sc, device);
if (st) if (st)
{ {
cardinited: if (!sc->sn.sampleformat)
{
Con_TPrintf("S_Startup: Ignoring soundcard %s due to unspecified sample format.\n", sc->name);
S_ShutdownCard(sc);
continue;
}
S_DefaultSpeakerConfiguration(sc); S_DefaultSpeakerConfiguration(sc);
if (snd_speed) if (snd_speed)
{ //if the sample speeds of multiple soundcards do not match, it'll fail. { //if the sample speeds of multiple soundcards do not match, it'll fail.
@ -1834,7 +1845,7 @@ cardinited:
{ {
Con_TPrintf("S_Startup: Ignoring soundcard %s due to mismatched sample speeds.\n", sc->name); Con_TPrintf("S_Startup: Ignoring soundcard %s due to mismatched sample speeds.\n", sc->name);
S_ShutdownCard(sc); S_ShutdownCard(sc);
continue; return NULL;
} }
} }
else else
@ -1849,21 +1860,6 @@ cardinited:
} }
} }
for (i = 0; olddrivers[i].name; i++)
{
od = &olddrivers[i];
if (!driver || !Q_strcasecmp(od->name, driver))
{
//skip drivers which are not present.
if (!*od->ptr)
continue;
st = (**od->ptr)(sc, device?atoi(device):0);
if (st == 1)
goto cardinited;
}
}
S_ShutdownCard(sc); S_ShutdownCard(sc);
if (!driver) if (!driver)
@ -2152,7 +2148,7 @@ S_Init
*/ */
void S_Init (void) void S_Init (void)
{ {
int p; int p, i;
Con_DPrintf("\nSound Initialization\n"); Con_DPrintf("\nSound Initialization\n");
@ -2208,9 +2204,12 @@ void S_Init (void)
mixermutex = Sys_CreateMutex(); mixermutex = Sys_CreateMutex();
#endif #endif
#ifdef AVAIL_OPENAL for (i = 0; outputdrivers[i]; i++)
OpenAL_CvarInit(); {
#endif sounddriver_t *sd = outputdrivers[i];
if (sd && sd->name && sd->RegisterCvars)
sd->RegisterCvars();
}
if (COM_CheckParm("-nosound")) if (COM_CheckParm("-nosound"))
{ {
@ -3072,7 +3071,7 @@ static void S_ClearBuffer (soundcardinfo_t *sc)
if (!sound_started || !sc->sn.buffer) if (!sound_started || !sc->sn.buffer)
return; return;
if (sc->sn.samplebits == 8) if (sc->sn.sampleformat == QSF_U8)
clear = 0x80; clear = 0x80;
else else
clear = 0; clear = 0;
@ -3081,7 +3080,7 @@ static void S_ClearBuffer (soundcardinfo_t *sc)
buffer = sc->Lock(sc, &dummy); buffer = sc->Lock(sc, &dummy);
if (buffer) if (buffer)
{ {
Q_memset(buffer, clear, sc->sn.samples * sc->sn.samplebits/8); Q_memset(buffer, clear, sc->sn.samples * sc->sn.samplebytes);
sc->Unlock(sc, buffer); sc->Unlock(sc, buffer);
} }
} }

View file

@ -21,7 +21,7 @@ JNIEXPORT jint JNICALL Java_com_fteqw_FTEDroidEngine_audioinfo(JNIEnv *env, jcla
case 1: case 1:
return sc->sn.numchannels; return sc->sn.numchannels;
case 2: case 2:
return sc->sn.samplebits; return sc->sn.samplebytes*8;
default: default:
return sc->sn.speed; return sc->sn.speed;
} }
@ -37,10 +37,10 @@ JNIEXPORT jint JNICALL Java_com_fteqw_FTEDroidEngine_paintaudio(JNIEnv *env, jcl
if (sc) if (sc)
{ {
int buffersize = sc->sn.samples*sc->sn.samplebits/8; int buffersize = sc->sn.samples*sc->sn.samplebytes;
int curtime = S_GetMixerTime(sc); int curtime = S_GetMixerTime(sc);
framesz = sc->sn.numchannels * sc->sn.samplebits/8; framesz = sc->sn.numchannels * sc->sn.samplebytes;
S_PaintChannels (sc, curtime + (len / framesz)); S_PaintChannels (sc, curtime + (len / framesz));
@ -80,7 +80,7 @@ static void Droid_Shutdown(soundcardinfo_t *sc)
//return the number of samples that have already been submitted to the device. //return the number of samples that have already been submitted to the device.
static unsigned int Droid_GetDMAPos(soundcardinfo_t *sc) static unsigned int Droid_GetDMAPos(soundcardinfo_t *sc)
{ {
sc->sn.samplepos = sc->snd_sent / (sc->sn.samplebits/8); sc->sn.samplepos = sc->snd_sent / sc->sn.samplebytes;
return sc->sn.samplepos; return sc->sn.samplepos;
} }
@ -107,16 +107,26 @@ static void Droid_Submit(soundcardinfo_t *sc, int start, int end)
//8bit is not guarenteed. //8bit is not guarenteed.
//there's no reference to sample rates. I assume 44.1khz will always work, though we want to avoid that cpu+mem load if we can //there's no reference to sample rates. I assume 44.1khz will always work, though we want to avoid that cpu+mem load if we can
//nor any guarentee about channels supported. I assume mono will always work. //nor any guarentee about channels supported. I assume mono will always work.
static int Droid_InitCard (soundcardinfo_t *sc, int cardnum) static qboolean Droid_InitCard (soundcardinfo_t *sc, const char *cardname)
{ {
if (sys_sc) if (sys_sc)
return 2; return false; //can only cope with one device.
if (cardname && *cardname)
return false; //only the default device
sc->selfpainting = true; sc->selfpainting = true;
// sc->sn.speed = 11025; // sc->sn.speed = 11025;
// sc->sn.samplebits = 16; // sc->sn.samplebytes = 2;
// sc->sn.numchannels = 1; // sc->sn.numchannels = 1;
if (sc->sn.samplebytes == 1)
sc->sn.sampleformat = QSF_U8;
else /*if (sc->sn.samplebytes == 2)*/
{
sc->sn.samplebytes = 2;
sc->sn.sampleformat = QSF_S16;
}
/*internal buffer should have 1 sec audio*/ /*internal buffer should have 1 sec audio*/
sc->sn.samples = sc->sn.speed*sc->sn.numchannels; sc->sn.samples = sc->sn.speed*sc->sn.numchannels;
@ -127,7 +137,7 @@ static int Droid_InitCard (soundcardinfo_t *sc, int cardnum)
sc->Shutdown = Droid_Shutdown; sc->Shutdown = Droid_Shutdown;
sc->GetDMAPos = Droid_GetDMAPos; sc->GetDMAPos = Droid_GetDMAPos;
sc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebits/8); sc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebytes);
sys_sc = sc; sys_sc = sc;
@ -135,5 +145,10 @@ static int Droid_InitCard (soundcardinfo_t *sc, int cardnum)
return 1; return 1;
} }
int (*pDroid_InitCard) (soundcardinfo_t *sc, int cardnum) = &Droid_InitCard;
sounddriver_t Droid_AudioOutput =
{
"Android",
Droid_InitCard,
NULL
};

View file

@ -14,6 +14,11 @@
#include <sys/stat.h> #include <sys/stat.h>
#endif #endif
#ifndef AFMT_FLOAT
#define AFMT_FLOAT 0x00004000 //OSS4 supports it, but linux is too shit to define it.
#endif
static int tryrates[] = { 11025, 22051, 44100, 8000, 48000 }; static int tryrates[] = { 11025, 22051, 44100, 8000, 48000 };
static unsigned int OSS_MMap_GetDMAPos(soundcardinfo_t *sc) static unsigned int OSS_MMap_GetDMAPos(soundcardinfo_t *sc)
@ -30,9 +35,9 @@ static unsigned int OSS_MMap_GetDMAPos(soundcardinfo_t *sc)
sc->audio_fd = -1; sc->audio_fd = -1;
return 0; return 0;
} }
// shm->samplepos = (count.bytes / (shm->samplebits / 8)) & (shm->samples-1); // shm->samplepos = (count.bytes / shm->samplebytes) & (shm->samples-1);
// fprintf(stderr, "%d \r", count.ptr); // fprintf(stderr, "%d \r", count.ptr);
sc->sn.samplepos = count.ptr / (sc->sn.samplebits / 8); sc->sn.samplepos = count.ptr / sc->sn.samplebytes;
} }
return sc->sn.samplepos; return sc->sn.samplepos;
@ -48,7 +53,7 @@ static unsigned int OSS_Alsa_GetDMAPos(soundcardinfo_t *sc)
if (ioctl (sc->audio_fd, SNDCTL_DSP_GETOSPACE, &info) != -1) if (ioctl (sc->audio_fd, SNDCTL_DSP_GETOSPACE, &info) != -1)
{ {
bytes = sc->snd_sent + info.bytes; bytes = sc->snd_sent + info.bytes;
sc->sn.samplepos = bytes / (sc->sn.samplebits / 8); sc->sn.samplepos = bytes / sc->sn.samplebytes;
} }
return sc->sn.samplepos; return sc->sn.samplepos;
} }
@ -61,12 +66,12 @@ static void OSS_Alsa_Submit(soundcardinfo_t *sc, int start, int end)
int result; int result;
/*we can't change the data that was already written*/ /*we can't change the data that was already written*/
bytes = end * sc->sn.numchannels * (sc->sn.samplebits/8); bytes = end * sc->sn.numchannels * sc->sn.samplebytes;
bytes -= sc->snd_sent; bytes -= sc->snd_sent;
if (!bytes) if (!bytes)
return; return;
ringsize = sc->sn.samples * (sc->sn.samplebits/8); ringsize = sc->sn.samples * sc->sn.samplebytes;
chunk = bytes; chunk = bytes;
offset = sc->snd_sent % ringsize; offset = sc->snd_sent % ringsize;
@ -99,7 +104,7 @@ static void OSS_Shutdown(soundcardinfo_t *sc)
if (sc->Submit == OSS_Alsa_Submit) if (sc->Submit == OSS_Alsa_Submit)
free(sc->sn.buffer); /*if using alsa-compat, just free the buffer*/ free(sc->sn.buffer); /*if using alsa-compat, just free the buffer*/
else else
munmap(sc->sn.buffer, sc->sn.samples * (sc->sn.samplebits/8)); munmap(sc->sn.buffer, sc->sn.samples * sc->sn.samplebytes);
} }
if (sc->audio_fd != -1) if (sc->audio_fd != -1)
close(sc->audio_fd); close(sc->audio_fd);
@ -214,20 +219,15 @@ static qboolean OSS_InitCard(soundcardinfo_t *sc, const char *snddev)
sc->sn.numchannels = 1; sc->sn.numchannels = 1;
#endif #endif
//choose bits
// ask the device what it supports // ask the device what it supports
ioctl(sc->audio_fd, SNDCTL_DSP_GETFMTS, &fmt); ioctl(sc->audio_fd, SNDCTL_DSP_GETFMTS, &fmt);
if (!(fmt & AFMT_S16_LE) && sc->sn.samplebits > 8)
sc->sn.samplebits = 8; // they asked for 16bit (the default) but their card does not support it //choose a format
if (!(fmt & AFMT_U8) && sc->sn.samplebits == 8) if (sc->sn.samplebytes >= 4 && (fmt & AFMT_FLOAT))
{ //their card doesn't support 8bit which we're trying to use.
Con_Printf(CON_ERROR "OSS: No needed sample formats supported\n");
OSS_Shutdown(sc);
return false;
}
if (sc->sn.samplebits == 16)
{ {
rc = AFMT_S16_LE; sc->sn.samplebytes = 4;
sc->sn.sampleformat = QSF_F32;
rc = AFMT_FLOAT;
rc = ioctl(sc->audio_fd, SNDCTL_DSP_SETFMT, &rc); rc = ioctl(sc->audio_fd, SNDCTL_DSP_SETFMT, &rc);
if (rc < 0) if (rc < 0)
{ {
@ -237,8 +237,24 @@ static qboolean OSS_InitCard(soundcardinfo_t *sc, const char *snddev)
return false; return false;
} }
} }
else if (sc->sn.samplebits == 8) else if (sc->sn.samplebytes >= 2 && (fmt & AFMT_S16_NE))
{ {
sc->sn.samplebytes = 2;
sc->sn.sampleformat = QSF_S16;
rc = AFMT_S16_NE;
rc = ioctl(sc->audio_fd, SNDCTL_DSP_SETFMT, &rc);
if (rc < 0)
{
perror(snddev);
Con_Printf(CON_ERROR "OSS: Could not support 16-bit data. Try 8-bit.\n");
OSS_Shutdown(sc);
return false;
}
}
else if (/*sc->sn.samplebytes == 1 && */(fmt & AFMT_U8))
{
sc->sn.samplebytes = 1;
sc->sn.sampleformat = QSF_U8;
rc = AFMT_U8; rc = AFMT_U8;
rc = ioctl(sc->audio_fd, SNDCTL_DSP_SETFMT, &rc); rc = ioctl(sc->audio_fd, SNDCTL_DSP_SETFMT, &rc);
if (rc < 0) if (rc < 0)
@ -249,10 +265,24 @@ static qboolean OSS_InitCard(soundcardinfo_t *sc, const char *snddev)
return false; return false;
} }
} }
else if (/*sc->sn.samplebytes == 1 && */(fmt & AFMT_S8))
{
sc->sn.samplebytes = 1;
sc->sn.sampleformat = QSF_S8;
rc = AFMT_S8;
rc = ioctl(sc->audio_fd, SNDCTL_DSP_SETFMT, &rc);
if (rc < 0)
{
perror(snddev);
Con_Printf(CON_ERROR "OSS: Could not support 8-bit data.\n");
OSS_Shutdown(sc);
return false;
}
}
else else
{ {
perror(snddev); perror(snddev);
Con_Printf(CON_ERROR "OSS: %d-bit sound not supported.\n", sc->sn.samplebits); Con_Printf(CON_ERROR "OSS: %d-bit sound not supported.\n", sc->sn.samplebytes*8);
OSS_Shutdown(sc); OSS_Shutdown(sc);
return false; return false;
} }
@ -286,7 +316,7 @@ static qboolean OSS_InitCard(soundcardinfo_t *sc, const char *snddev)
return false; return false;
} }
sc->sn.samples = info.fragstotal * info.fragsize; sc->sn.samples = info.fragstotal * info.fragsize;
sc->sn.samples /= (sc->sn.samplebits/8); sc->sn.samples /= sc->sn.samplebytes;
/*samples is the number of samples*channels */ /*samples is the number of samples*channels */
// memory map the dma buffer // memory map the dma buffer
@ -297,7 +327,7 @@ static qboolean OSS_InitCard(soundcardinfo_t *sc, const char *snddev)
} }
else if ((caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP)) else if ((caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP))
{ {
sc->sn.buffer = (unsigned char *) mmap(NULL, sc->sn.samples*(sc->sn.samplebits/8), PROT_WRITE, MAP_FILE|MAP_SHARED, sc->audio_fd, 0); sc->sn.buffer = (unsigned char *) mmap(NULL, sc->sn.samples*sc->sn.samplebytes, PROT_WRITE, MAP_FILE|MAP_SHARED, sc->audio_fd, 0);
if (sc->sn.buffer == MAP_FAILED) if (sc->sn.buffer == MAP_FAILED)
{ {
Con_Printf("%s: device reported mmap capability, but mmap failed.\n", snddev); Con_Printf("%s: device reported mmap capability, but mmap failed.\n", snddev);
@ -315,9 +345,9 @@ static qboolean OSS_InitCard(soundcardinfo_t *sc, const char *snddev)
{ {
sc->sn.buffer = NULL; sc->sn.buffer = NULL;
sc->samplequeue = info.bytes / (sc->sn.samplebits/8); sc->samplequeue = info.bytes / sc->sn.samplebytes;
sc->sn.samples*=2; sc->sn.samples*=2;
sc->sn.buffer = malloc(sc->sn.samples*(sc->sn.samplebits/8)); sc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebytes);
sc->Submit = OSS_Alsa_Submit; sc->Submit = OSS_Alsa_Submit;
sc->GetDMAPos = OSS_Alsa_GetDMAPos; sc->GetDMAPos = OSS_Alsa_GetDMAPos;
} }

View file

@ -45,7 +45,7 @@ static OSStatus AudioRender(void *inRefCon,
struct MacOSSound_Private *pdata = sc->handle; struct MacOSSound_Private *pdata = sc->handle;
int start = pdata->readpos; int start = pdata->readpos;
int buffersize = sc->sn.samples * (sc->sn.samplebits/8); int buffersize = sc->sn.samples * sc->sn.samplebytes;
int bytes = ioData->mBuffers[0].mDataByteSize; int bytes = ioData->mBuffers[0].mDataByteSize;
int remaining; int remaining;
@ -64,7 +64,7 @@ static OSStatus AudioRender(void *inRefCon,
memcpy(ioData->mBuffers[0].mData, sc->sn.buffer + start, bytes); memcpy(ioData->mBuffers[0].mData, sc->sn.buffer + start, bytes);
memcpy((char*)ioData->mBuffers[0].mData+bytes, sc->sn.buffer, remaining); memcpy((char*)ioData->mBuffers[0].mData+bytes, sc->sn.buffer, remaining);
pdata->readpos += inNumberFrames*sc->sn.numchannels * (sc->sn.samplebits/8); pdata->readpos += inNumberFrames*sc->sn.numchannels * sc->sn.samplebytes;
return noErr; return noErr;
} }
@ -93,7 +93,7 @@ static void MacOS_Shutdown(soundcardinfo_t *sc)
static unsigned int MacOS_GetDMAPos(soundcardinfo_t *sc) static unsigned int MacOS_GetDMAPos(soundcardinfo_t *sc)
{ {
struct MacOSSound_Private *pdata = sc->handle; struct MacOSSound_Private *pdata = sc->handle;
sc->sn.samplepos = pdata->readpos/(sc->sn.samplebits/8); sc->sn.samplepos = pdata->readpos/sc->sn.samplebytes;
return sc->sn.samplepos; return sc->sn.samplepos;
} }
@ -110,12 +110,12 @@ static void MacOS_Unlock(soundcardinfo_t *sc, void *buffer)
{ {
} }
static int MacOS_InitCard(soundcardinfo_t *sc, int cardnum) static qboolean MacOS_InitCard(soundcardinfo_t *sc, const char *cardname)
{ {
ComponentResult err = noErr; ComponentResult err = noErr;
if (cardnum) if (cardname && *cardname)
return 2; /* no more */ return false; //only the default device will be used for now.
struct MacOSSound_Private *pdata = Z_Malloc(sizeof(*pdata)); struct MacOSSound_Private *pdata = Z_Malloc(sizeof(*pdata));
if (!pdata) if (!pdata)
@ -198,13 +198,14 @@ static int MacOS_InitCard(soundcardinfo_t *sc, int cardnum)
// set the shm structure // set the shm structure
sc->sn.speed = streamFormat.mSampleRate; sc->sn.speed = streamFormat.mSampleRate;
sc->sn.samplebits = streamFormat.mBitsPerChannel; sc->sn.samplebytes = streamFormat.mBitsPerChannel/8;
sc->sn.sampleformat = QCF_S16;
sc->sn.numchannels = streamFormat.mChannelsPerFrame; sc->sn.numchannels = streamFormat.mChannelsPerFrame;
sc->sn.samples = 256 * 1024; sc->sn.samples = 256 * 1024;
sc->sn.buffer = Z_Malloc(sc->sn.samples*sc->sn.samplebits/8); sc->sn.buffer = Z_Malloc(sc->sn.samples*sc->sn.samplebytes);
int i; int i;
for (i = 0; i < sc->sn.samples*sc->sn.samplebits/8; i++) for (i = 0; i < sc->sn.samples*sc->sn.samplebytes; i++)
sc->sn.buffer[i] = rand(); sc->sn.buffer[i] = rand();
if (sc->sn.buffer == 0) if (sc->sn.buffer == 0)
@ -249,5 +250,9 @@ static int MacOS_InitCard(soundcardinfo_t *sc, int cardnum)
return TRUE; return TRUE;
} }
sounddriver pMacOS_InitCard = &MacOS_InitCard; sounddriver_t MacOS_AudioOutput =
{
"CoreAudio",
MacOS_InitCard,
NULL
};

View file

@ -52,26 +52,12 @@ void S_TransferPaintBuffer(soundcardinfo_t *sc, int endtime)
if (!pbuf) if (!pbuf)
return; return;
if (sc->sn.samplebits == 16) switch(sc->sn.sampleformat)
{ {
short *out = (short *) pbuf; case QSF_INVALID: //erk...
while (count) case QSF_EXTERNALMIXER: //shouldn't reach this.
{ break;
for (i = 0; i < numc; i++) case QSF_U8:
{
val = *p++;// * snd_vol) >> 8;
if (val > 0x7fff)
val = 0x7fff;
else if (val < (short)0x8000)
val = (short)0x8000;
out[out_idx] = val;
out_idx = (out_idx + 1) % outlimit;
}
p += MAXSOUNDCHANNELS - numc;
count -= numc;
}
}
else if (sc->sn.samplebits == 8)
{ {
unsigned char *out = (unsigned char *) pbuf; unsigned char *out = (unsigned char *) pbuf;
while (count) while (count)
@ -90,7 +76,48 @@ void S_TransferPaintBuffer(soundcardinfo_t *sc, int endtime)
count -= numc; count -= numc;
} }
} }
else if (sc->sn.samplebits == 32) break;
case QSF_S8:
{
char *out = (char *) pbuf;
while (count)
{
for (i = 0; i < numc; i++)
{
val = *p++;// * snd_vol) >> 8;
if (val > 0x7fff)
val = 0x7fff;
else if (val < (short)0x8000)
val = (short)0x8000;
out[out_idx] = (val>>8);
out_idx = (out_idx + 1) % outlimit;
}
p += MAXSOUNDCHANNELS - numc;
count -= numc;
}
}
break;
case QSF_S16:
{
short *out = (short *) pbuf;
while (count)
{
for (i = 0; i < numc; i++)
{
val = *p++;// * snd_vol) >> 8;
if (val > 0x7fff)
val = 0x7fff;
else if (val < (short)0x8000)
val = (short)0x8000;
out[out_idx] = val;
out_idx = (out_idx + 1) % outlimit;
}
p += MAXSOUNDCHANNELS - numc;
count -= numc;
}
}
break;
case QSF_F32:
{ {
float *out = (float *) pbuf; float *out = (float *) pbuf;
while (count) while (count)
@ -104,6 +131,8 @@ void S_TransferPaintBuffer(soundcardinfo_t *sc, int endtime)
count -= numc; count -= numc;
} }
} }
break;
}
sc->Unlock(sc, pbuf); sc->Unlock(sc, pbuf);
} }

View file

@ -115,7 +115,7 @@ static void AHI_Submit(soundcardinfo_t *sc)
{ {
} }
static int AHI_InitCard(soundcardinfo_t *sc, int cardnum) static qboolean AHI_InitCard(soundcardinfo_t *sc, const char *cardname)
{ {
struct AHIdata *ad; struct AHIdata *ad;
@ -129,8 +129,8 @@ static int AHI_InitCard(soundcardinfo_t *sc, int cardnum)
struct AHISampleInfo sample; struct AHISampleInfo sample;
if (cardnum) if (cardname && *cardname)
return 2; /* Which means "no more cards" */ return false; /* only allow the default audio device */
ad = AllocVec(sizeof(*ad), MEMF_ANY); ad = AllocVec(sizeof(*ad), MEMF_ANY);
if (ad) if (ad)
@ -171,7 +171,7 @@ static int AHI_InitCard(soundcardinfo_t *sc, int cardnum)
channels = 2; channels = 2;
sc->sn.speed = speed; sc->sn.speed = speed;
sc->sn.samplebits = bits; sc->sn.samplebytes = bits/8;
sc->sn.numchannels = channels; sc->sn.numchannels = channels;
sc->sn.samples = speed*channels; sc->sn.samples = speed*channels;
@ -194,6 +194,7 @@ static int AHI_InitCard(soundcardinfo_t *sc, int cardnum)
else else
sample.ahisi_Type = AHIST_S16S; sample.ahisi_Type = AHIST_S16S;
} }
sc->sn.sampleformat = (bits==8)?QSF_S8:QSF_S16;
sample.ahisi_Address = ad->samplebuffer; sample.ahisi_Address = ad->samplebuffer;
sample.ahisi_Length = (speed*(bits/8)*channels)/AHI_SampleFrameSize(sample.ahisi_Type); sample.ahisi_Length = (speed*(bits/8)*channels)/AHI_SampleFrameSize(sample.ahisi_Type);
@ -236,7 +237,7 @@ static int AHI_InitCard(soundcardinfo_t *sc, int cardnum)
Con_Printf("Using AHI mode \"%s\" for audio output\n", sc->name); Con_Printf("Using AHI mode \"%s\" for audio output\n", sc->name);
Con_Printf("Channels: %d bits: %d frequency: %d\n", channels, bits, speed); Con_Printf("Channels: %d bits: %d frequency: %d\n", channels, bits, speed);
return 1; return true;
} }
} }
} }
@ -256,7 +257,12 @@ static int AHI_InitCard(soundcardinfo_t *sc, int cardnum)
FreeVec(ad); FreeVec(ad);
} }
return 0; return false;
} }
sounddriver pAHI_InitCard = &AHI_InitCard; sounddriver_t AHI_AudioOutput =
{
"AHI",
AHI_InitCard,
NULL
};

View file

@ -359,7 +359,7 @@ static unsigned int SBLASTER_GetDMAPos(soundcardinfo_t *sc)
outportb(0xc, 0); outportb(0xc, 0);
count = inportb(dma*2+1); count = inportb(dma*2+1);
count += inportb(dma*2+1) << 8; count += inportb(dma*2+1) << 8;
if (sc->sn.samplebits == 16) if (sc->sn.samplebytes == 2)
count /= 2; count /= 2;
count = sc->sn.samples - (count+1); count = sc->sn.samples - (count+1);
} }
@ -368,7 +368,7 @@ static unsigned int SBLASTER_GetDMAPos(soundcardinfo_t *sc)
outportb(0xd8, 0); outportb(0xd8, 0);
count = inportb(0xc0+(dma-4)*4+2); count = inportb(0xc0+(dma-4)*4+2);
count += inportb(0xc0+(dma-4)*4+2) << 8; count += inportb(0xc0+(dma-4)*4+2) << 8;
if (sc->sn.samplebits == 8) if (sc->sn.samplebytes == 1)
count *= 2; count *= 2;
count = sc->sn.samples - (count+1); count = sc->sn.samples - (count+1);
} }
@ -523,23 +523,28 @@ static qboolean SBLASTER_InitCard(soundcardinfo_t *sc, const char *pcmname)
{ {
if (sc->sn.numchannels != 1) if (sc->sn.numchannels != 1)
sc->sn.numchannels = 2; sc->sn.numchannels = 2;
if (sc->sn.samplebits != 8) if (sc->sn.samplebytes != 1)
sc->sn.samplebits = 16; sc->sn.samplebytes = 2;
} }
// version 3 cards (sb pro) do 8 bit stereo // version 3 cards (sb pro) do 8 bit stereo
else if (dsp_version == 3) else if (dsp_version == 3)
{ {
if (sc->sn.numchannels != 1) if (sc->sn.numchannels != 1)
sc->sn.numchannels = 2; sc->sn.numchannels = 2;
sc->sn.samplebits = 8; sc->sn.samplebytes = 1;
} }
// v2 cards do 8 bit mono // v2 cards do 8 bit mono
else else
{ {
sc->sn.numchannels = 1; sc->sn.numchannels = 1;
sc->sn.samplebits = 8; sc->sn.samplebytes = 1;
} }
if (sc->sn.samplebytes == 2)
sc->sn.sampleformat = QSF_S16;
else
sc->sn.sampleformat = QSF_U8;
sc->Lock = SBLASTER_LockBuffer; sc->Lock = SBLASTER_LockBuffer;
sc->Unlock = SBLASTER_UnlockBuffer; sc->Unlock = SBLASTER_UnlockBuffer;
sc->Shutdown = SBLASTER_Shutdown; sc->Shutdown = SBLASTER_Shutdown;
@ -560,10 +565,10 @@ static qboolean SBLASTER_InitCard(soundcardinfo_t *sc, const char *pcmname)
dma_size = size; dma_size = size;
memset(dma_buffer, 0, dma_size); memset(dma_buffer, 0, dma_size);
sc->sn.samples = size/(sc->sn.samplebits/8); sc->sn.samples = size/sc->sn.samplebytes;
sc->sn.samplepos = 0; sc->sn.samplepos = 0;
sc->sn.buffer = (unsigned char *) dma_buffer; sc->sn.buffer = (unsigned char *) dma_buffer;
sc->sn.samples = size/(sc->sn.samplebits/8); sc->sn.samples = size/sc->sn.samplebytes;
StartDMA(); StartDMA();
StartSB(sc); StartSB(sc);

View file

@ -18,12 +18,18 @@
#define SDL_AUDIO_ALLOW_FREQUENCY_CHANGE 0x00000001 #define SDL_AUDIO_ALLOW_FREQUENCY_CHANGE 0x00000001
#define SDL_AUDIO_ALLOW_FORMAT_CHANGE 0x00000002 #define SDL_AUDIO_ALLOW_FORMAT_CHANGE 0x00000002
#define SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004 #define SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004
#define AUDIO_U8 0x0008
#define AUDIO_S8 0x8008
#define AUDIO_S16LSB 0x8010 #define AUDIO_S16LSB 0x8010
#define AUDIO_S16MSB 0x9010 #define AUDIO_S16MSB 0x9010
#define AUDIO_F32LSB 0x8120
#define AUDIO_F32MSB 0x9120
#if __BYTE_ORDER == __BIG_ENDIAN #if __BYTE_ORDER == __BIG_ENDIAN
#define AUDIO_S16SYS AUDIO_S16MSB #define AUDIO_S16SYS AUDIO_S16MSB
#define AUDIO_F32SYS AUDIO_F32MSB
#else #else
#define AUDIO_S16SYS AUDIO_S16LSB #define AUDIO_S16SYS AUDIO_S16LSB
#define AUDIO_F32SYS AUDIO_F32LSB
#endif #endif
#define SDLCALL QDECL #define SDLCALL QDECL
@ -129,8 +135,11 @@ static void SSDL_Shutdown(soundcardinfo_t *sc)
Con_DPrintf("Shutdown SDL sound\n"); Con_DPrintf("Shutdown SDL sound\n");
#if SDL_MAJOR_VERSION >= 2 #if SDL_MAJOR_VERSION >= 2
if (sc->audio_fd)
{
SDL_PauseAudioDevice(sc->audio_fd, 1); SDL_PauseAudioDevice(sc->audio_fd, 1);
SDL_CloseAudioDevice(sc->audio_fd); SDL_CloseAudioDevice(sc->audio_fd);
}
#else #else
SDL_CloseAudio(); SDL_CloseAudio();
#endif #endif
@ -142,7 +151,7 @@ static void SSDL_Shutdown(soundcardinfo_t *sc)
} }
static unsigned int SSDL_GetDMAPos(soundcardinfo_t *sc) static unsigned int SSDL_GetDMAPos(soundcardinfo_t *sc)
{ {
sc->sn.samplepos = sc->snd_sent / (sc->sn.samplebits/8); sc->sn.samplepos = sc->snd_sent / sc->sn.samplebytes;
return sc->sn.samplepos; return sc->sn.samplepos;
} }
@ -154,12 +163,12 @@ static void VARGS SSDL_Paint(void *userdata, qbyte *stream, int len)
#ifdef SELFPAINT #ifdef SELFPAINT
sc->sn.buffer = stream; sc->sn.buffer = stream;
sc->sn.samples = len / (sc->sn.samplebits/8); sc->sn.samples = len / sc->sn.samplebytes;
sc->samplequeue = sc->sn.samples; sc->samplequeue = sc->sn.samples;
S_MixerThread(sc); S_MixerThread(sc);
sc->snd_sent += len; sc->snd_sent += len;
#else #else
int buffersize = sc->sn.samples*(sc->sn.samplebits/8); int buffersize = sc->sn.samples*sc->sn.samplebytes;
if (len > buffersize) if (len > buffersize)
{ {
@ -220,23 +229,29 @@ static qboolean QDECL SDL_InitCard(soundcardinfo_t *sc, const char *devicename)
desired.freq = sc->sn.speed; desired.freq = sc->sn.speed;
desired.channels = sc->sn.numchannels; //fixme! desired.channels = sc->sn.numchannels; //fixme!
desired.samples = 0x0200; //'Good values seem to range between 512 and 8192 inclusive, depending on the application and CPU speed.' desired.samples = 0x0200; //'Good values seem to range between 512 and 8192 inclusive, depending on the application and CPU speed.'
desired.format = AUDIO_S16SYS;
desired.callback = (void*)SSDL_Paint; desired.callback = (void*)SSDL_Paint;
desired.userdata = sc; desired.userdata = sc;
memcpy(&obtained, &desired, sizeof(obtained)); memcpy(&obtained, &desired, sizeof(obtained));
#if SDL_MAJOR_VERSION >= 2 #if SDL_MAJOR_VERSION >= 2
sc->audio_fd = SDL_OpenAudioDevice(devicename, false, &desired, &obtained, (sndcardinfo?0:SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) | SDL_AUDIO_ALLOW_CHANNELS_CHANGE); desired.format = AUDIO_F32SYS; //most modern audio APIs favour float audio nowadays.
sc->audio_fd = SDL_OpenAudioDevice(devicename, false, &desired, &obtained, (sndcardinfo?0:SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) | SDL_AUDIO_ALLOW_CHANNELS_CHANGE | SDL_AUDIO_ALLOW_FORMAT_CHANGE);
if (!sc->audio_fd) if (!sc->audio_fd)
{ {
Con_Printf("SDL_OpenAudioDevice(%s) failed: couldn't open sound device (%s).\n", devicename?devicename:"default", SDL_GetError()); Con_Printf("SDL_OpenAudioDevice(%s) failed: couldn't open sound device (%s).\n", devicename?devicename:"default", SDL_GetError());
return false; return false;
} }
if (obtained.format != AUDIO_U8 && obtained.format != AUDIO_S8 && obtained.format != AUDIO_S16SYS && obtained.format != AUDIO_F32SYS)
{ //can't cope with that... try again but force the format (so SDL converts)
SDL_CloseAudioDevice(sc->audio_fd);
sc->audio_fd = SDL_OpenAudioDevice(devicename, false, &desired, &obtained, (sndcardinfo?0:SDL_AUDIO_ALLOW_FREQUENCY_CHANGE) | SDL_AUDIO_ALLOW_CHANNELS_CHANGE);
}
if (devicename) if (devicename)
Con_Printf("Initing SDL audio device '%s'.\n", devicename); Con_Printf("Initing SDL audio device '%s'.\n", devicename);
else else
Con_Printf("Initing default SDL audio device.\n"); Con_Printf("Initing default SDL audio device.\n");
#else #else
desired.format = AUDIO_S16SYS;
if (sndcardinfo) if (sndcardinfo)
return false; //SDL1 only supports opening one audio device at a time. the existing one might not be sdl, but I don't care. return false; //SDL1 only supports opening one audio device at a time. the existing one might not be sdl, but I don't care.
if (SDL_OpenAudio(&desired, &obtained) < 0) if (SDL_OpenAudio(&desired, &obtained) < 0)
@ -248,21 +263,49 @@ static qboolean QDECL SDL_InitCard(soundcardinfo_t *sc, const char *devicename)
#endif #endif
sc->sn.numchannels = obtained.channels; sc->sn.numchannels = obtained.channels;
sc->sn.speed = obtained.freq; sc->sn.speed = obtained.freq;
sc->sn.samplebits = obtained.format&0xff;
sc->sn.samples = 32768;//*sc->sn.numchannels; //doesn't really matter, so long as it's higher than obtained.samples sc->sn.samples = 32768;//*sc->sn.numchannels; //doesn't really matter, so long as it's higher than obtained.samples
switch(obtained.format)
{
case AUDIO_U8:
sc->sn.samplebytes = 1;
sc->sn.sampleformat = QSF_U8;
break;
case AUDIO_S8:
sc->sn.samplebytes = 1;
sc->sn.sampleformat = QSF_S8;
break;
case AUDIO_S16SYS:
sc->sn.samplebytes = 2;
sc->sn.sampleformat = QSF_S16;
break;
case AUDIO_F32SYS:
sc->sn.samplebytes = 4;
sc->sn.sampleformat = QSF_F32;
break;
default:
//unsupported. shouldn't have obtained that.
#if SDL_MAJOR_VERSION >= 2
SDL_CloseAudioDevice(sc->audio_fd);
sc->audio_fd = 0;
#else
SDL_CloseAudio();
#endif
break;
}
#ifdef SELFPAINT #ifdef SELFPAINT
sc->selfpainting = true; sc->selfpainting = true;
#endif #endif
Con_DPrintf("channels: %i\n", sc->sn.numchannels); Con_DPrintf("channels: %i\n", sc->sn.numchannels);
Con_DPrintf("Speed: %i\n", sc->sn.speed); Con_DPrintf("Speed: %i\n", sc->sn.speed);
Con_DPrintf("Samplebits: %i\n", sc->sn.samplebits); Con_DPrintf("Samplebits: %i\n", sc->sn.samplebytes*8);
Con_DPrintf("SDLSamples: %i (low for latency)\n", obtained.samples); Con_DPrintf("SDLSamples: %i (low for latency)\n", obtained.samples);
Con_DPrintf("FakeSamples: %i\n", sc->sn.samples); Con_DPrintf("FakeSamples: %i\n", sc->sn.samples);
#ifndef SELFPAINT #ifndef SELFPAINT
sc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebits/8); sc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebytes);
#endif #endif
Con_DPrintf("Got sound %i-%i\n", obtained.freq, obtained.format); Con_DPrintf("Got sound %i-%i\n", obtained.freq, obtained.format);

View file

@ -37,7 +37,7 @@ struct sndio_private
size_t dma_buffer_size, dma_ptr; size_t dma_buffer_size, dma_ptr;
}; };
static int sndio_init(soundcardinfo_t *, int); static qboolean sndio_init(soundcardinfo_t *, const char *);
static void *sndio_lock(soundcardinfo_t *); static void *sndio_lock(soundcardinfo_t *);
static void sndio_unlock(soundcardinfo_t *, void *); static void sndio_unlock(soundcardinfo_t *, void *);
static void sndio_shutdown(soundcardinfo_t *); static void sndio_shutdown(soundcardinfo_t *);
@ -49,12 +49,7 @@ static void sndio_setunderwater(soundcardinfo_t *sc, qboolean underwater)
{ {
} }
static qboolean sndio_init(soundcardinfo_t *sc, const char *cardname)
#define SND_ERROR 0
#define SND_LOADED 1
#define SND_NOMORE 2 //like error, but doesn't try the next card.
static int
sndio_init(soundcardinfo_t *sc, int cardnum)
{ {
struct sndio_private *sp; struct sndio_private *sp;
struct sio_par par; struct sio_par par;
@ -63,14 +58,14 @@ sndio_init(soundcardinfo_t *sc, int cardnum)
int i; int i;
Con_DPrintf("sndio_init called\n"); Con_DPrintf("sndio_init called\n");
if (cardnum) if (cardname && *cardname)
return SND_NOMORE; return false; //only support the default device for now.
sp = calloc(sizeof(struct sndio_private), 1); sp = calloc(sizeof(struct sndio_private), 1);
if (sp == NULL) if (sp == NULL)
{ {
Con_Printf("Could not get mem"); Con_Printf("Could not get mem");
return SND_ERROR; return false;
} }
Con_DPrintf("trying to open sp->hdl\n"); Con_DPrintf("trying to open sp->hdl\n");
@ -78,7 +73,7 @@ sndio_init(soundcardinfo_t *sc, int cardnum)
if (sp->hdl == NULL) if (sp->hdl == NULL)
{ {
Con_Printf("Could not open sndio device\n"); Con_Printf("Could not open sndio device\n");
return SND_NOMORE; return false;
} }
Con_DPrintf("Opened sndio\n"); Con_DPrintf("Opened sndio\n");
sc->GetDMAPos = sndio_getdmapos; sc->GetDMAPos = sndio_getdmapos;
@ -91,7 +86,7 @@ sndio_init(soundcardinfo_t *sc, int cardnum)
sio_initpar(&par); sio_initpar(&par);
par.rate = sc->sn.speed; par.rate = sc->sn.speed;
par.bits = sc->sn.samplebits; par.bits = (sc->sn.samplebytes==1)?8:16;
par.sig = 1; par.sig = 1;
par.le = SIO_LE_NATIVE; par.le = SIO_LE_NATIVE;
par.pchan = sc->sn.numchannels; par.pchan = sc->sn.numchannels;
@ -101,14 +96,25 @@ sndio_init(soundcardinfo_t *sc, int cardnum)
{ {
Con_Printf("Error setting audio parameters\n"); Con_Printf("Error setting audio parameters\n");
sio_close(sp->hdl); sio_close(sp->hdl);
return SND_ERROR; return false;
} }
if ((par.pchan != 1 && par.pchan != 2) || if ((par.pchan != 1 && par.pchan != 2) ||
(par.bits != 16 || par.sig != 1)) (par.bits != 16 || par.sig != 1))
{ {
Con_Printf("Could not set appropriate audio parameters\n"); Con_Printf("Could not set appropriate audio parameters\n");
sio_close(sp->hdl); sio_close(sp->hdl);
return SND_ERROR; return false;
}
if (par.bits == 16)
{
sc->sn.sampleformat = QSF_S16;
sc->sn.samplebytes = 2;
}
else if (par.bits == 8)
{
sc->sn.sampleformat = QSF_U8;
sc->sn.samplebytes = 1;
} }
/* sc->sn.speed = par.rate; /* sc->sn.speed = par.rate;
sc->sn.numchannels = par.pchan; sc->sn.numchannels = par.pchan;
@ -123,28 +129,28 @@ sndio_init(soundcardinfo_t *sc, int cardnum)
; /* nothing */ ; /* nothing */
sc->sn.samples = i * par.pchan; sc->sn.samples = i * par.pchan;
sp->dma_buffer_size = sc->sn.samples * sc->sn.samplebits / 8; sp->dma_buffer_size = sc->sn.samples * sc->sn.samplebytes;
sc->sn.buffer = calloc(1, sp->dma_buffer_size); sc->sn.buffer = calloc(1, sp->dma_buffer_size);
if (sc->sn.buffer == NULL) if (sc->sn.buffer == NULL)
{ {
Con_Printf("Could not allocate audio ring buffer\n"); Con_Printf("Could not allocate audio ring buffer\n");
return SND_ERROR; return false;
} }
dma_ptr = 0; dma_ptr = 0;
if (!sio_start(sp->hdl)) if (!sio_start(sp->hdl))
{ {
Con_Printf("Could not start audio\n"); Con_Printf("Could not start audio\n");
sio_close(sp->hdl); sio_close(sp->hdl);
return SND_ERROR; return false;
} }
sc->sn.samplepos = 0; sc->sn.samplepos = 0;
Con_DPrintf("sc->sn.speed = %d, par.rate = %d\n", sc->sn.speed, par.rate); Con_DPrintf("sc->sn.speed = %d, par.rate = %d\n", sc->sn.speed, par.rate);
Con_DPrintf("sc->sn.samplebits = %d, par.bits = %d\n", sc->sn.samplebits, par.bits); Con_DPrintf("sc->sn.samplebits = %d, par.bits = %d\n", sc->sn.samplebytes*8, par.bits);
Con_DPrintf("sc->sn.numchannels = %d, par.pchan = %d\n", sc->sn.numchannels, par.pchan); Con_DPrintf("sc->sn.numchannels = %d, par.pchan = %d\n", sc->sn.numchannels, par.pchan);
Con_DPrintf("sc->sn.samples = %d, par.pchan = %d\n", sc->sn.samples, par.pchan); Con_DPrintf("sc->sn.samples = %d, par.pchan = %d\n", sc->sn.samples, par.pchan);
Con_DPrintf("dma_buffer_size = %d\n", sp->dma_buffer_size); Con_DPrintf("dma_buffer_size = %d\n", sp->dma_buffer_size);
return SND_LOADED; return true;
} }
@ -174,7 +180,7 @@ static unsigned int
sndio_getdmapos(soundcardinfo_t *sc) sndio_getdmapos(soundcardinfo_t *sc)
{ {
struct sndio_private *sp = sc->handle; struct sndio_private *sp = sc->handle;
sc->sn.samplepos = dma_ptr / (sc->sn.samplebits / 8); sc->sn.samplepos = dma_ptr / sc->sn.samplebytes;
return sc->sn.samplepos; return sc->sn.samplepos;
} }
@ -207,4 +213,10 @@ sndio_submit(soundcardinfo_t *sc, int startcount, int endcount)
} }
} }
int (*pSNDIO_InitCard) (soundcardinfo_t *sc, int cardnum) = &sndio_init; sounddriver_t SNDIO_AudioOutput =
{
"sndio",
sndio_init,
NULL
};

View file

@ -31,6 +31,18 @@ FORCE_DEFINE_GUID(IID_IAudioRenderClient, 0xF294ACFC, 0x3146, 0x4483, 0xA7, 0x
FORCE_DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); FORCE_DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
FORCE_DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); FORCE_DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
static cvar_t wasapi_forcerate = CVARD("wasapi_forcerate", "0", "Attempts to force snd_khz instead of using the system's default channel count.\nFor this to work, you will need to set wasapi_exclusive 1");
static cvar_t wasapi_forcechannels = CVARD("wasapi_forcechannels", "0", "Attempts to force snd_numchannels instead of using the system's default channel count.\nFor this to work, you will need to set wasapi_exclusive 1");
static cvar_t wasapi_exclusive = CVARD("wasapi_exclusive", "0", "When set, attempts to take exclusive control of the output device, to the detriment of other programs (causing errors or even crashes in them).\nExclusive mode leaves the game free to change the hardware's playback details, instead of being required to use a single system-wide 'mixer' rate.\nIt should also reduce latency a little.");
static cvar_t wasapi_buffersize = CVAR("wasapi_buffersize", "0.01");
static void QDECL WASAPI_RegisterCvars(void)
{
Cvar_Register(&wasapi_forcerate, "WASAPI audio output");
Cvar_Register(&wasapi_forcechannels, "WASAPI audio output");
Cvar_Register(&wasapi_exclusive, "WASAPI audio output");
Cvar_Register(&wasapi_buffersize, "WASAPI audio output");
}
static void *WASAPI_Lock(soundcardinfo_t *sc, unsigned int *startoffset) static void *WASAPI_Lock(soundcardinfo_t *sc, unsigned int *startoffset)
{ {
return sc->sn.buffer; return sc->sn.buffer;
@ -55,19 +67,28 @@ static void WASAPI_Shutdown(soundcardinfo_t *sc)
sc->thread = NULL; sc->thread = NULL;
} }
static qboolean WASAPI_AcceptableFormat(soundcardinfo_t *sc, IAudioClient *dev, WAVEFORMATEX *pwfx) static qboolean WASAPI_AcceptableFormat(soundcardinfo_t *sc, IAudioClient *dev, WAVEFORMATEX *pwfx, qboolean isexclusive)
{ {
if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE && !memcmp(&((WAVEFORMATEXTENSIBLE*)pwfx)->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)) && pwfx->wBitsPerSample == 32) if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE && !memcmp(&((WAVEFORMATEXTENSIBLE*)pwfx)->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID)) && pwfx->wBitsPerSample == 32)
{ { //oo, floating point audio. I guess this means we can have fun with clamping, right?
sc->sn.samplebits = 32;//oo, floating point audio. I guess this means we can have fun with clamping, right? sc->sn.samplebytes = 4;
sc->sn.sampleformat = QSF_F32;
} }
else if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE && memcmp(&((WAVEFORMATEXTENSIBLE*)pwfx)->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID))) else if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE && memcmp(&((WAVEFORMATEXTENSIBLE*)pwfx)->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID)))
{ {
Con_Printf("WASAPI: unsupported sample type\n"); Con_Printf("WASAPI: unsupported sample type\n");
return false; //we only support pcm / floats return false; //we only support pcm / floats
} }
else if (pwfx->wBitsPerSample == 8 || pwfx->wBitsPerSample == 16) else if (pwfx->wBitsPerSample == 8)
sc->sn.samplebits = pwfx->wBitsPerSample; //these sample sizes work {
sc->sn.samplebytes = 1;
sc->sn.sampleformat = QSF_U8;
}
else if (pwfx->wBitsPerSample == 16)
{
sc->sn.samplebytes = 2;
sc->sn.sampleformat = QSF_S16;
}
else else
{ {
Con_Printf("WASAPI: unsupported sample size\n"); Con_Printf("WASAPI: unsupported sample size\n");
@ -83,7 +104,7 @@ static qboolean WASAPI_AcceptableFormat(soundcardinfo_t *sc, IAudioClient *dev,
sc->sn.numchannels = pwfx->nChannels; sc->sn.numchannels = pwfx->nChannels;
sc->sn.speed = pwfx->nSamplesPerSec; sc->sn.speed = pwfx->nSamplesPerSec;
Con_Printf("WASAPI: %i channel %ibit %ukhz\n", sc->sn.numchannels, pwfx->wBitsPerSample, (unsigned int)pwfx->nSamplesPerSec); Con_Printf("WASAPI: %i channel %ibit %ukhz%s\n", sc->sn.numchannels, pwfx->wBitsPerSample, (unsigned int)pwfx->nSamplesPerSec, isexclusive?" exclusive":" non-exclusive");
return true; return true;
} }
static qboolean WASAPI_DetermineFormat(soundcardinfo_t *sc, IAudioClient *dev, qboolean exclusive, WAVEFORMATEX **ret) static qboolean WASAPI_DetermineFormat(soundcardinfo_t *sc, IAudioClient *dev, qboolean exclusive, WAVEFORMATEX **ret)
@ -93,7 +114,7 @@ static qboolean WASAPI_DetermineFormat(soundcardinfo_t *sc, IAudioClient *dev, q
if (!SUCCEEDED(dev->lpVtbl->GetMixFormat(dev, &pwfx))) if (!SUCCEEDED(dev->lpVtbl->GetMixFormat(dev, &pwfx)))
return false; return false;
if (snd_speed || Cvar_Get("wasapi_forcerate", "0", 0, "WASAPI audio output")->ival) if (snd_speed || wasapi_forcerate.ival)
{ //if some other driver has already committed us to a set rate, we need to drive wasapi at that rate too. { //if some other driver has already committed us to a set rate, we need to drive wasapi at that rate too.
//this may cause failures later in this function. //this may cause failures later in this function.
Con_Printf("WASAPI: overriding sampler rate\n"); Con_Printf("WASAPI: overriding sampler rate\n");
@ -101,7 +122,7 @@ static qboolean WASAPI_DetermineFormat(soundcardinfo_t *sc, IAudioClient *dev, q
pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign; pwfx->nAvgBytesPerSec = pwfx->nSamplesPerSec * pwfx->nBlockAlign;
} }
if (Cvar_Get("wasapi_forcechannels", "0", 0, "WASAPI audio output")->ival) if (wasapi_forcechannels.ival)
{ {
Con_Printf("WASAPI: overriding channels\n"); Con_Printf("WASAPI: overriding channels\n");
@ -150,8 +171,7 @@ static qboolean WASAPI_DetermineFormat(soundcardinfo_t *sc, IAudioClient *dev, q
pwfx = pwfx2; pwfx = pwfx2;
} }
} }
if (!WASAPI_AcceptableFormat(sc, dev, pwfx, false))
if (!WASAPI_AcceptableFormat(sc, dev, pwfx))
return false; return false;
} }
else else
@ -170,6 +190,8 @@ static qboolean WASAPI_DetermineFormat(soundcardinfo_t *sc, IAudioClient *dev, q
if (FAILED(dev->lpVtbl->IsFormatSupported(dev, exclusive?AUDCLNT_SHAREMODE_EXCLUSIVE:AUDCLNT_SHAREMODE_SHARED, pwfx, NULL))) if (FAILED(dev->lpVtbl->IsFormatSupported(dev, exclusive?AUDCLNT_SHAREMODE_EXCLUSIVE:AUDCLNT_SHAREMODE_SHARED, pwfx, NULL)))
{ {
//fixme: try float audio...
//try to switch over to 24bit pcm (although with no more 16bit audio) //try to switch over to 24bit pcm (although with no more 16bit audio)
/* pwfx->wBitsPerSample = 24; /* pwfx->wBitsPerSample = 24;
pwfx->nBlockAlign = pwfx->wBitsPerSample/8 * pwfx->nChannels; pwfx->nBlockAlign = pwfx->wBitsPerSample/8 * pwfx->nChannels;
@ -184,8 +206,7 @@ static qboolean WASAPI_DetermineFormat(soundcardinfo_t *sc, IAudioClient *dev, q
} }
} }
} }
if (!WASAPI_AcceptableFormat(sc, dev, pwfx, true))
if (!WASAPI_AcceptableFormat(sc, dev, pwfx))
return false; return false;
} }
@ -193,28 +214,44 @@ static qboolean WASAPI_DetermineFormat(soundcardinfo_t *sc, IAudioClient *dev, q
return true; return true;
} }
static IMMDevice *WASAPI_GetDevice(soundcardinfo_t *sc)
{
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
CoInitialize(NULL); //sigh.
if (SUCCEEDED(CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void**)&pEnumerator)))
{
if (*sc->name)
{
WCHAR wname[256];
pEnumerator->lpVtbl->GetDevice(pEnumerator, widen(wname, sizeof(wname), sc->name), &pDevice);
}
else
pEnumerator->lpVtbl->GetDefaultAudioEndpoint(pEnumerator, eRender, eConsole, &pDevice);
pEnumerator->lpVtbl->Release(pEnumerator);
}
return pDevice;
}
static int WASAPI_Thread(void *arg) static int WASAPI_Thread(void *arg)
{ {
soundcardinfo_t *sc = arg; soundcardinfo_t *sc = arg;
qboolean inited = false; qboolean inited = false;
// REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC; // REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioClient *pAudioClient = NULL; IAudioClient *pAudioClient = NULL;
IAudioRenderClient *pRenderClient = NULL; IAudioRenderClient *pRenderClient = NULL;
UINT32 bufferFrameCount = 0; UINT32 bufferFrameCount = 0;
HANDLE hEvent = NULL; HANDLE hEvent = NULL;
WAVEFORMATEX *pwfx; WAVEFORMATEX *pwfx;
qboolean exclusive = Cvar_Get("wasapi_exclusive", "1", 0, "WASAPI audio output")->ival; qboolean exclusive = wasapi_exclusive.ival;
void *cond = sc->handle; void *cond = sc->handle;
//main thread will wait for us to finish initing, so lets do that... //main thread will wait for us to finish initing, so lets do that...
CoInitialize(NULL); IMMDevice *pDevice = WASAPI_GetDevice(sc);
if (SUCCEEDED(CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void**)&pEnumerator))) if (pDevice)
if (SUCCEEDED(pEnumerator->lpVtbl->GetDefaultAudioEndpoint(pEnumerator, eRender, eConsole, &pDevice))) {
if (SUCCEEDED(pDevice->lpVtbl->Activate(pDevice, &IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient))) if (SUCCEEDED(pDevice->lpVtbl->Activate(pDevice, &IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient)))
{ {
if (!WASAPI_DetermineFormat(sc, pAudioClient, exclusive, &pwfx)) if (!WASAPI_DetermineFormat(sc, pAudioClient, exclusive, &pwfx))
@ -223,12 +260,10 @@ static int WASAPI_Thread(void *arg)
} }
else else
{ {
if (sc->sn.samplebytes && (!snd_speed || sc->sn.speed == snd_speed))
if (sc->sn.samplebits && (!snd_speed || sc->sn.speed == snd_speed))
{ {
HRESULT hr; HRESULT hr;
REFERENCE_TIME buffersize = REFTIMES_PER_SEC * Cvar_Get("wasapi_buffersize", "0.01", 0, "WASAPI audio output")->ival; REFERENCE_TIME buffersize = REFTIMES_PER_SEC * wasapi_buffersize.value;
if (exclusive) if (exclusive)
pAudioClient->lpVtbl->GetDevicePeriod(pAudioClient, NULL, &buffersize); pAudioClient->lpVtbl->GetDevicePeriod(pAudioClient, NULL, &buffersize);
@ -291,6 +326,9 @@ static int WASAPI_Thread(void *arg)
CoTaskMemFree(pwfx); CoTaskMemFree(pwfx);
} }
} }
pDevice->lpVtbl->Release(pDevice);
pDevice = NULL;
}
if (inited) if (inited)
sc->Shutdown = WASAPI_Shutdown; sc->Shutdown = WASAPI_Shutdown;
@ -359,19 +397,12 @@ static int WASAPI_Thread(void *arg)
pRenderClient->lpVtbl->Release(pRenderClient); pRenderClient->lpVtbl->Release(pRenderClient);
if (pAudioClient) if (pAudioClient)
pAudioClient->lpVtbl->Release(pAudioClient); pAudioClient->lpVtbl->Release(pAudioClient);
if (pDevice)
pDevice->lpVtbl->Release(pDevice);
if (pEnumerator)
pEnumerator->lpVtbl->Release(pEnumerator);
return 0; return 0;
} }
static qboolean QDECL WASAPI_InitCard (soundcardinfo_t *sc, const char *cardname) static qboolean QDECL WASAPI_InitCard (soundcardinfo_t *sc, const char *cardname)
{ {
void *cond; void *cond;
if (cardname && *cardname)
return false; //we don't support explicit devices at this time
Q_strncpyz(sc->name, cardname?cardname:"", sizeof(sc->name)); Q_strncpyz(sc->name, cardname?cardname:"", sizeof(sc->name));
sc->selfpainting = true; sc->selfpainting = true;
@ -504,7 +535,8 @@ sounddriver_t WASAPI_Output =
{ {
AUDIODRIVERNAME, AUDIODRIVERNAME,
WASAPI_InitCard, WASAPI_InitCard,
WASAPI_Enumerate WASAPI_Enumerate,
WASAPI_RegisterCvars
}; };
#endif #endif

View file

@ -142,14 +142,8 @@ how many sample are required to fill it up.
static unsigned int WAV_GetDMAPos(soundcardinfo_t *sc) static unsigned int WAV_GetDMAPos(soundcardinfo_t *sc)
{ {
int s; int s;
s = sc->snd_sent * WAV_BUFFER_SIZE; s = sc->snd_sent * WAV_BUFFER_SIZE;
s >>= sc->sn.samplebytes - 1;
s >>= (sc->sn.samplebits/8) - 1;
// s = (s/shm->numchannels % (shm->samples-1))*shm->numchannels;
return s; return s;
} }
@ -194,7 +188,7 @@ static void WAV_Submit(soundcardinfo_t *sc, int start, int end)
else else
chunkstosubmit = 4 + (sc->sn.speed/6000); chunkstosubmit = 4 + (sc->sn.speed/6000);
while (((sc->snd_sent - sc->snd_completed) >> ((sc->sn.samplebits/8) - 1)) < chunkstosubmit) while (((sc->snd_sent - sc->snd_completed) >> (sc->sn.samplebytes - 1)) < chunkstosubmit)
{ {
h = wh->lpWaveHdr + ( sc->snd_sent&WAV_MASK ); h = wh->lpWaveHdr + ( sc->snd_sent&WAV_MASK );
@ -224,15 +218,15 @@ SNDDM_InitWav
Crappy windows multimedia base Crappy windows multimedia base
================== ==================
*/ */
int WAV_InitCard (soundcardinfo_t *sc, int cardnum) qboolean WAV_InitCard (soundcardinfo_t *sc, const char *cardname)
{ {
WAVEFORMATEX format; WAVEFORMATEX format;
int i; int i;
HRESULT hr; HRESULT hr;
wavhandle_t *wh; wavhandle_t *wh;
if (cardnum != 0) if (*cardname)
return 2; //we only support one card, at the moment. return false; //we only support one card, at the moment.
wh = sc->handle = Z_Malloc(sizeof(wavhandle_t)); wh = sc->handle = Z_Malloc(sizeof(wavhandle_t));
@ -242,15 +236,21 @@ int WAV_InitCard (soundcardinfo_t *sc, int cardnum)
if (sc->sn.speed > 48000) // limit waveout to 48000 until that buffer issue gets solved if (sc->sn.speed > 48000) // limit waveout to 48000 until that buffer issue gets solved
sc->sn.speed = 48000; sc->sn.speed = 48000;
if (sc->sn.samplebits > 16) if (sc->sn.samplebytes > 2)
sc->sn.samplebits = 16; {
sc->sn.samplebytes = 2;
sc->sn.sampleformat = QSF_S16;
}
else else
sc->sn.samplebits = 8; {
sc->sn.samplebytes = 1;
sc->sn.sampleformat = QSF_U8;
}
memset (&format, 0, sizeof(format)); memset (&format, 0, sizeof(format));
format.wFormatTag = WAVE_FORMAT_PCM; format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = sc->sn.numchannels; format.nChannels = sc->sn.numchannels;
format.wBitsPerSample = sc->sn.samplebits; format.wBitsPerSample = sc->sn.samplebytes*8;
format.nSamplesPerSec = sc->sn.speed; format.nSamplesPerSec = sc->sn.speed;
format.nBlockAlign = format.nChannels format.nBlockAlign = format.nChannels
*format.wBitsPerSample / 8; *format.wBitsPerSample / 8;
@ -350,7 +350,7 @@ int WAV_InitCard (soundcardinfo_t *sc, int cardnum)
} }
} }
sc->sn.samples = wh->gSndBufSize/(sc->sn.samplebits/8); sc->sn.samples = wh->gSndBufSize/sc->sn.samplebytes;
sc->sn.samplepos = 0; sc->sn.samplepos = 0;
sc->sn.buffer = (unsigned char *) wh->lpData; sc->sn.buffer = (unsigned char *) wh->lpData;
Q_strncpyz(sc->name, "wav out", sizeof(sc->name)); Q_strncpyz(sc->name, "wav out", sizeof(sc->name));
@ -364,5 +364,11 @@ int WAV_InitCard (soundcardinfo_t *sc, int cardnum)
return true; return true;
} }
int (*pWAV_InitCard) (soundcardinfo_t *sc, int cardnum) = &WAV_InitCard; //int (*pWAV_InitCard) (soundcardinfo_t *sc, int cardnum) = &WAV_InitCard;
sounddriver_t WaveOut_Output =
{
"WaveOut",
WAV_InitCard,
NULL
};
#endif #endif

View file

@ -2,13 +2,14 @@
//frankly, xaudio2 gives nothing over directsound, unless we're getting it to do all the mixing instead. which gets really messy and far too involved. //frankly, xaudio2 gives nothing over directsound, unless we're getting it to do all the mixing instead. which gets really messy and far too involved.
//I suppose it has a use with WINRT... although that doesn't apply to any actual users. //I suppose it has a use with WINRT... although that doesn't apply to any actual users.
//(on xp, its actually implemented as a wrapper over directsound, so why even bother. on vista+ its implemented as a wrapper over wasapi)
//we're lazy and don't do any special threading, this makes it inferior to the directsound implementation - potentially, the callback feature could allow for slightly lower latencies. //we're lazy and don't do any special threading, this makes it inferior to the directsound implementation - potentially, the callback feature could allow for slightly lower latencies.
//also no reverb (fixme: XAUDIO2FX_REVERB_PARAMETERS). //also no reverb (fixme: XAUDIO2FX_REVERB_PARAMETERS).
//dxsdk = 2.7 = win7+ //dxsdk = 2.7 = win7+ (redistributable)
//w8sdk = 2.8 = win8+ //w8sdk = 2.8 = win8+ (system component, not available on vista/win7)
//w10sdk = 2.9 = win10+ //w10sdk = 2.9 = win10+ (system component, not available on vista/win7/win8/win8.1)
#if defined(AVAIL_XAUDIO2) && !defined(SERVERONLY) #if defined(AVAIL_XAUDIO2) && !defined(SERVERONLY)
#include "winquake.h" #include "winquake.h"
@ -58,11 +59,11 @@ static void XAUDIO_Submit(soundcardinfo_t *sc, int start, int end)
XAUDIO2_BUFFER buf; XAUDIO2_BUFFER buf;
//determine total buffer size //determine total buffer size
int buffersize = sc->sn.samples*sc->sn.samplebits/8; int buffersize = sc->sn.samples*sc->sn.samplebytes;
//determine time offsets in bytes //determine time offsets in bytes
start *= sc->sn.numchannels*sc->sn.samplebits/8; start *= sc->sn.numchannels*sc->sn.samplebytes;
end *= sc->sn.numchannels*sc->sn.samplebits/8; end *= sc->sn.numchannels*sc->sn.samplebytes;
while (start < end) while (start < end)
{ {
@ -70,14 +71,14 @@ static void XAUDIO_Submit(soundcardinfo_t *sc, int start, int end)
break; //o.O that's not meant to happen break; //o.O that's not meant to happen
memset(&buf, 0, sizeof(buf)); memset(&buf, 0, sizeof(buf));
buf.AudioBytes = end - start; buf.AudioBytes = end - start;
if (buf.AudioBytes > xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebits/8) if (buf.AudioBytes > xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebytes)
{ {
if (buf.AudioBytes < xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebits/8) if (buf.AudioBytes < xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebytes)
{ //dma code should ensure that only multiples of 'samplequeue' are processed. { //dma code should ensure that only multiples of 'samplequeue' are processed.
Con_Printf("XAudio2 underrun\n"); Con_Printf("XAudio2 underrun\n");
break; break;
} }
buf.AudioBytes = xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebits/8; buf.AudioBytes = xa->subbuffersize * sc->sn.numchannels*sc->sn.samplebytes;
} }
buf.pAudioData = xa->bufferstart + (start%buffersize); buf.pAudioData = xa->bufferstart + (start%buffersize);
if ((qbyte*)buf.pAudioData + buf.AudioBytes > xa->bufferstart + buffersize) if ((qbyte*)buf.pAudioData + buf.AudioBytes > xa->bufferstart + buffersize)
@ -141,7 +142,7 @@ static qboolean QDECL XAUDIO_InitCard(soundcardinfo_t *sc, const char *cardname)
wfmt.Format.wFormatTag = WAVE_FORMAT_PCM; wfmt.Format.wFormatTag = WAVE_FORMAT_PCM;
wfmt.Format.nChannels = sc->sn.numchannels; wfmt.Format.nChannels = sc->sn.numchannels;
wfmt.Format.nSamplesPerSec = sc->sn.speed; wfmt.Format.nSamplesPerSec = sc->sn.speed;
wfmt.Format.wBitsPerSample = sc->sn.samplebits; wfmt.Format.wBitsPerSample = sc->sn.samplebytes*8;
wfmt.Format.nBlockAlign = wfmt.Format.nChannels * (wfmt.Format.wBitsPerSample / 8); wfmt.Format.nBlockAlign = wfmt.Format.nChannels * (wfmt.Format.wBitsPerSample / 8);
wfmt.Format.nAvgBytesPerSec = wfmt.Format.nSamplesPerSec * wfmt.Format.nBlockAlign; wfmt.Format.nAvgBytesPerSec = wfmt.Format.nSamplesPerSec * wfmt.Format.nBlockAlign;
wfmt.Format.cbSize = 0; wfmt.Format.cbSize = 0;
@ -159,7 +160,7 @@ static qboolean QDECL XAUDIO_InitCard(soundcardinfo_t *sc, const char *cardname)
sc->sn.samples = xa->buffercount * xa->subbuffersize * sc->sn.numchannels; sc->sn.samples = xa->buffercount * xa->subbuffersize * sc->sn.numchannels;
xa->bufferstart = BZ_Malloc(sc->sn.samples * (sc->sn.samplebits/8)); xa->bufferstart = BZ_Malloc(sc->sn.samples * sc->sn.samplebytes);
if (xa->bufferstart) if (xa->bufferstart)
{ {

View file

@ -80,14 +80,21 @@ typedef struct sfxcache_s
typedef struct typedef struct
{ {
// qboolean gamealive;
// qboolean soundalive;
// qboolean splitbuffer;
int numchannels; // this many samples per frame int numchannels; // this many samples per frame
int samples; // mono samples in buffer (individual, non grouped) int samples; // mono samples in buffer (individual, non grouped)
// int submission_chunk; // don't mix less than this #
int samplepos; // in mono samples int samplepos; // in mono samples
int samplebits; //FIXME: replace with a format enum (with separate framebytes field for lazyness). int samplebytes; // per channel (NOT per frame)
enum
{
QSF_INVALID, //not selected yet...
QSF_EXTERNALMIXER, //this sample format is totally irrelevant as this device uses some sort of external mixer.
QSF_U8, //FIXME: more unsigned formats need changes to S_ClearBuffer
QSF_S8, //signed 8bit format is actually quite rare.
QSF_S16, //normal format
// QSF_X8_S24, //upper 8 bits unused. hopefully we don't need any packed thing
// QSF_S32, //lower 8 bits probably unused. this makes overflow detection messy.
QSF_F32, //modern mixers can use SSE/SIMD stuff, and we can skip clamping so this can be quite nippy.
} sampleformat;
int speed; // this many frames per second int speed; // this many frames per second
unsigned char *buffer; // pointer to mixed pcm buffer (not directly used by mixer) unsigned char *buffer; // pointer to mixed pcm buffer (not directly used by mixer)
} dma_t; } dma_t;
@ -278,11 +285,6 @@ void SNDVC_MicInput(qbyte *buffer, int samples, int freq, int width);
#ifdef AVAIL_OPENAL
void OpenAL_CvarInit(void);
#endif
// ==================================================================== // ====================================================================
// User-setable variables // User-setable variables
// ==================================================================== // ====================================================================
@ -338,8 +340,9 @@ typedef struct
const char *name; //must be a single token, with no : const char *name; //must be a single token, with no :
qboolean (QDECL *InitCard) (soundcardinfo_t *sc, const char *cardname); //NULL for default device. qboolean (QDECL *InitCard) (soundcardinfo_t *sc, const char *cardname); //NULL for default device.
qboolean (QDECL *Enumerate) (void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename)); qboolean (QDECL *Enumerate) (void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename));
void (QDECL *RegisterCvars) (void);
} sounddriver_t; } sounddriver_t;
typedef int (*sounddriver) (soundcardinfo_t *sc, int cardnum); /*typedef int (*sounddriver) (soundcardinfo_t *sc, int cardnum);
extern sounddriver pOPENAL_InitCard; extern sounddriver pOPENAL_InitCard;
extern sounddriver pDSOUND_InitCard; extern sounddriver pDSOUND_InitCard;
extern sounddriver pALSA_InitCard; extern sounddriver pALSA_InitCard;
@ -348,6 +351,7 @@ extern sounddriver pOSS_InitCard;
extern sounddriver pSDL_InitCard; extern sounddriver pSDL_InitCard;
extern sounddriver pWAV_InitCard; extern sounddriver pWAV_InitCard;
extern sounddriver pAHI_InitCard; extern sounddriver pAHI_InitCard;
*/
struct soundcardinfo_s { //windows has one defined AFTER directsound struct soundcardinfo_s { //windows has one defined AFTER directsound
char name[256]; //a description of the card. char name[256]; //a description of the card.

View file

@ -11,6 +11,9 @@
#ifndef WIN32 #ifndef WIN32
#include <fcntl.h> #include <fcntl.h>
#include <sys/stat.h> #include <sys/stat.h>
#ifdef __unix__
#include <unistd.h>
#endif
#else #else
#include <direct.h> #include <direct.h>
#endif #endif

View file

@ -475,7 +475,7 @@ dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)
Con_Printf("Error ERROR_PROC_NOT_FOUND loading %s\n", name); Con_Printf("Error ERROR_PROC_NOT_FOUND loading %s\n", name);
break; break;
default: default:
Con_Printf("Error %u loading %s\n", err, name); Con_Printf("Error %u loading %s\n", (unsigned)err, name);
break; break;
} }
@ -1845,6 +1845,7 @@ char *Sys_GetClipboard(void)
{ {
cp = (cp&0x3ff)<<10; cp = (cp&0x3ff)<<10;
cp |= *clipWText++ & 0x3ff; cp |= *clipWText++ & 0x3ff;
cp += 0x10000;
} }
else else
cp = 0xFFFDu; cp = 0xFFFDu;
@ -4258,7 +4259,7 @@ void *WIN_CreateCursor(const char *filename, float hotx, float hoty, float scale
COM_FileExtension(filename, ext, sizeof(ext)); COM_FileExtension(filename, ext, sizeof(ext));
Q_strncatz(aname, "_alpha.", sizeof(aname)); Q_strncatz(aname, "_alpha.", sizeof(aname));
Q_strncatz(aname, ext, sizeof(aname)); Q_strncatz(aname, ext, sizeof(aname));
alphsize = FS_LoadFile(filename, &alph); alphsize = FS_LoadFile(filename, (void**)&alph);
if (alph) if (alph)
{ {
if ((alphadata = Read32BitImageFile(alph, alphsize, &alpha_width, &alpha_height, &hasalpha, aname))) if ((alphadata = Read32BitImageFile(alph, alphsize, &alpha_width, &alpha_height, &hasalpha, aname)))

View file

@ -1858,6 +1858,7 @@ msurface_t *Mod_GetSurfaceNearPoint(model_t *model, vec3_t point);
char *Shader_GetShaderBody(shader_t *s, char *fname, size_t fnamesize); char *Shader_GetShaderBody(shader_t *s, char *fname, size_t fnamesize);
extern vec3_t nametagorg[MAX_CLIENTS]; extern vec3_t nametagorg[MAX_CLIENTS];
extern qboolean nametagseen[MAX_CLIENTS]; extern qboolean nametagseen[MAX_CLIENTS];
extern cvar_t r_showshaders, r_showfields, r_projection;
void R_DrawNameTags(void) void R_DrawNameTags(void)
{ {
int i; int i;
@ -1866,8 +1867,6 @@ void R_DrawNameTags(void)
char *ourteam; char *ourteam;
int ourcolour; int ourcolour;
extern cvar_t r_showshaders, r_showfields, r_projection;
if (r_projection.ival) //we don't actually know how to transform the points unless the projection is coded in advance. and it isn't. if (r_projection.ival) //we don't actually know how to transform the points unless the projection is coded in advance. and it isn't.
return; return;
if (cls.protocol == CP_QUAKE2) if (cls.protocol == CP_QUAKE2)
@ -1985,6 +1984,7 @@ void R_DrawNameTags(void)
} }
} }
#if defined(CSQC_DAT) || !defined(CLIENTONLY)
if (r_showshaders.ival && cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADED) if (r_showshaders.ival && cl.worldmodel && cl.worldmodel->loadstate == MLS_LOADED)
{ {
trace_t trace; trace_t trace;
@ -2012,6 +2012,7 @@ void R_DrawNameTags(void)
str = "hit nothing"; str = "hit nothing";
R_DrawTextField(r_refdef.vrect.x + r_refdef.vrect.width/4, r_refdef.vrect.y, r_refdef.vrect.width/2, r_refdef.vrect.height, str, CON_WHITEMASK, CPRINT_LALIGN, font_default, scale); R_DrawTextField(r_refdef.vrect.x + r_refdef.vrect.width/4, r_refdef.vrect.y, r_refdef.vrect.width/2, r_refdef.vrect.height, str, CON_WHITEMASK, CPRINT_LALIGN, font_default, scale);
} }
#endif
if (((!r_refdef.playerview->spectator && !cls.demoplayback) || !scr_autoid.ival) && (!cl.teamplay || !scr_autoid_team.ival)) if (((!r_refdef.playerview->spectator && !cls.demoplayback) || !scr_autoid.ival) && (!cl.teamplay || !scr_autoid_team.ival))
return; return;

View file

@ -108,13 +108,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define AVAIL_OGGVORBIS #define AVAIL_OGGVORBIS
#endif #endif
#if !defined(NO_DIRECTX) && !defined(NODIRECTX) && defined(_WIN32) #ifdef WINRT
#define AVAIL_XAUDIO2
#define AVAIL_WASAPI
#elif !defined(NO_DIRECTX) && !defined(NODIRECTX) && defined(_WIN32)
#define AVAIL_DINPUT #define AVAIL_DINPUT
#define AVAIL_DSOUND #define AVAIL_DSOUND
#define AVAIL_WASAPI #define AVAIL_WASAPI
#endif //#define AVAIL_XAUDIO2 //gcc doesn't provide any headers
#ifdef WINRT
#define AVAIL_XAUDIO2
#endif #endif
#define AVAIL_XZDEC #define AVAIL_XZDEC
@ -568,7 +569,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#if defined(HAVE_GNUTLS) || defined(HAVE_WINSSPI) #if defined(HAVE_GNUTLS) || defined(HAVE_WINSSPI)
//FIXME: HAVE_WINSSPI does not work as a server. //FIXME: HAVE_WINSSPI does not work as a server.
//FIXME: advertising dtls without a valid certificate will probably bug out if a client tries to auto-upgrade. //FIXME: advertising dtls without a valid certificate will probably bug out if a client tries to auto-upgrade.
// #define HAVE_DTLS //FIXME: we don't cache server certs
#define HAVE_DTLS
#endif #endif
#if defined(USE_SQLITE) || defined(USE_MYSQL) #if defined(USE_SQLITE) || defined(USE_MYSQL)

View file

@ -2137,9 +2137,9 @@ struct cmdargcompletionctx_s
match_t *match; match_t *match;
const char *desc; const char *desc;
}; };
void Cmd_CompleteCheckArg(const char *value, void *vctx) //compare cumulative strings and join the result void Cmd_CompleteCheckArg(const char *value, struct xcommandargcompletioncb_s *vctx) //compare cumulative strings and join the result
{ {
struct cmdargcompletionctx_s *ctx = vctx; struct cmdargcompletionctx_s *ctx = (struct cmdargcompletionctx_s*)vctx;
match_t *match = ctx->match; match_t *match = ctx->match;
const char *desc = ctx->desc; const char *desc = ctx->desc;

View file

@ -2395,11 +2395,13 @@ unsigned int unicode_decode(int *error, const void *in, char **out, qboolean mar
unsigned int charcode; unsigned int charcode;
if (markup && ((char*)in)[0] == '^' && ((char*)in)[1] == 'U' && ishexcode(((char*)in)[2]) && ishexcode(((char*)in)[3]) && ishexcode(((char*)in)[4]) && ishexcode(((char*)in)[5])) if (markup && ((char*)in)[0] == '^' && ((char*)in)[1] == 'U' && ishexcode(((char*)in)[2]) && ishexcode(((char*)in)[3]) && ishexcode(((char*)in)[4]) && ishexcode(((char*)in)[5]))
{ {
*error = 0;
*out = (char*)in + 6; *out = (char*)in + 6;
charcode = (dehex(((char*)in)[2]) << 12) | (dehex(((char*)in)[3]) << 8) | (dehex(((char*)in)[4]) << 4) | (dehex(((char*)in)[5]) << 0); charcode = (dehex(((char*)in)[2]) << 12) | (dehex(((char*)in)[3]) << 8) | (dehex(((char*)in)[4]) << 4) | (dehex(((char*)in)[5]) << 0);
} }
else if (markup && ((char*)in)[0] == '^' && ((char*)in)[1] == '{') else if (markup && ((char*)in)[0] == '^' && ((char*)in)[1] == '{')
{ {
*error = 0;
*out = (char*)in + 2; *out = (char*)in + 2;
charcode = 0; charcode = 0;
while (ishexcode(**out)) while (ishexcode(**out))
@ -3344,10 +3346,6 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t
} }
continue; continue;
} }
else if (str[1] == 'a')
{
ext ^= CON_2NDCHARSETTEXT;
}
else if (str[1] == 'b') else if (str[1] == 'b')
{ {
ext ^= CON_BLINKTEXT; ext ^= CON_BLINKTEXT;
@ -3359,7 +3357,7 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t
else else
ext = defaultflags; ext = defaultflags;
} }
else if (str[1] == 'm') else if (str[1] == 'm'||str[1] == 'a')
ext ^= CON_2NDCHARSETTEXT; ext ^= CON_2NDCHARSETTEXT;
else if (str[1] == 'h') else if (str[1] == 'h')
ext ^= CON_HALFALPHA; ext ^= CON_HALFALPHA;

View file

@ -734,8 +734,12 @@ qbyte COM_BlockSequenceCheckByte (qbyte *base, int length, int sequence, unsigne
qbyte COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence); qbyte COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence);
qbyte Q2COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence); qbyte Q2COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence);
int SHA1(char *digest, int maxdigestsize, const char *string, int stringlen); typedef size_t hashfunc_t(unsigned char *digest, size_t maxdigestsize, size_t numstrings, const unsigned char **strings, size_t *stringlens);
int SHA1_HMAC(unsigned char *digest, int maxdigestsize, const unsigned char *data, int datalen, const unsigned char *key, int keylen); hashfunc_t SHA1_m;
//int SHA1_m(char *digest, size_t maxdigestsize, size_t numstrings, const char **strings, size_t *stringlens);
//#define SHA1(digest,maxdigestsize,string,stringlen) SHA1_m(digest, maxdigestsize, 1, &string, &stringlen)
int SHA1(unsigned char *digest, int maxdigestsize, const unsigned char *string, size_t stringlen);
size_t HMAC(hashfunc_t *hashfunc, unsigned char *digest, size_t maxdigestsize, const unsigned char *data, size_t datalen, const unsigned char *key, size_t keylen);
int version_number(void); int version_number(void);
char *version_string(void); char *version_string(void);
@ -769,6 +773,7 @@ void Log_Init(void);
void Log_ShutDown(void); void Log_ShutDown(void);
void IPLog_Add(const char *ip, const char *name); //for associating player ip addresses with names. void IPLog_Add(const char *ip, const char *name); //for associating player ip addresses with names.
qboolean IPLog_Merge_File(const char *fname); qboolean IPLog_Merge_File(const char *fname);
qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize);
/*used by and for botlib and q3 gamecode*/ /*used by and for botlib and q3 gamecode*/

View file

@ -44,6 +44,7 @@
#define MULTITHREAD //misc basic multithreading - dsound, downloads, basic stuff that's unlikely to have race conditions. #define MULTITHREAD //misc basic multithreading - dsound, downloads, basic stuff that's unlikely to have race conditions.
#endif #endif
#define LOADERTHREAD //worker threads for loading misc stuff. falls back on main thread if not supported. #define LOADERTHREAD //worker threads for loading misc stuff. falls back on main thread if not supported.
//#define USEAREAGRID //world collision optimisation. REQUIRED for performance with xonotic. hopefully it helps a few other mods too.
#define NOBUILTINMENUS #define NOBUILTINMENUS
#define NOLEGACY //just spike trying to kill off crappy crap... #define NOLEGACY //just spike trying to kill off crappy crap...

View file

@ -139,6 +139,7 @@ typedef struct console_s
char name[128]; char name[128];
char title[128]; char title[128];
char prompt[128]; char prompt[128];
char icon[MAX_QPATH]; //should really dynamically allocate this stuff.
char backimage[MAX_QPATH]; char backimage[MAX_QPATH];
shader_t *backshader; shader_t *backshader;
float wnd_x; float wnd_x;

View file

@ -856,7 +856,7 @@ uInt dem_uncompress_block(decodectx_t *dc)
if (cfields & 4) { cam2 += getlong(inptr); inptr += 4; } if (cfields & 4) { cam2 += getlong(inptr); inptr += 4; }
outlen = 0; outlen = 0;
insert_msg(&a1,4); a1 = 0/*length*/; insert_msg(&a1,4);
a1 = cnvlong(cam0); insert_msg(&a1,4); a1 = cnvlong(cam0); insert_msg(&a1,4);
a1 = cnvlong(cam1); insert_msg(&a1,4); a1 = cnvlong(cam1); insert_msg(&a1,4);
a1 = cnvlong(cam2); insert_msg(&a1,4); a1 = cnvlong(cam2); insert_msg(&a1,4);

View file

@ -250,7 +250,11 @@ static int QDECL VFSW32_WriteBytes (struct vfsfile_s *file, const void *buffer,
} }
if (!WriteFile(intfile->hand, buffer, bytestoread, &written, NULL)) if (!WriteFile(intfile->hand, buffer, bytestoread, &written, NULL))
{
// DWORD err = GetLastError();
// ERROR_INVALID_USER_BUFFER or ERROR_NOT_ENOUGH_MEMORY
return 0; return 0;
}
return written; return written;
} }
static qboolean QDECL VFSW32_Seek (struct vfsfile_s *file, qofs_t pos) static qboolean QDECL VFSW32_Seek (struct vfsfile_s *file, qofs_t pos)

View file

@ -613,6 +613,171 @@ static void IPLog_Merge_f(void)
if (!IPLog_Merge_File(fname)) if (!IPLog_Merge_File(fname))
Con_Printf("unable to read %s\n", fname); Con_Printf("unable to read %s\n", fname);
} }
#ifndef SERVERONLY
struct certlog_s
{
link_t l;
char *hostname;
size_t certsize;
qbyte cert[1];
};
static link_t certlog;
static struct certlog_s *CertLog_Find(const char *hostname)
{
struct certlog_s *l;
for (l = (struct certlog_s*)certlog.next ; l != (struct certlog_s*)&certlog ; l = (struct certlog_s*)l->l.next)
{
if (!strcmp(l->hostname, hostname))
return l;
}
return NULL;
}
static void CertLog_Update(const char *hostname, const void *cert, size_t certsize)
{
struct certlog_s *l = CertLog_Find(hostname);
if (l)
{
RemoveLink(&l->l);
Z_Free(l);
}
l = Z_Malloc(sizeof(*l) + certsize + strlen(hostname));
l->certsize = certsize;
l->hostname = l->cert + l->certsize;
memcpy(l->cert, cert, certsize);
strcpy(l->hostname, hostname);
InsertLinkAfter(&l->l, &certlog);
}
static void CertLog_Write(void)
{
struct certlog_s *l;
vfsfile_t *f = NULL;//FS_OpenVFS("knowncerts.txt", "wb", FS_ROOT);
if (f)
{
VFS_PRINTF(f, "version 1.0\n");
for (l=(struct certlog_s*)certlog.next ; l != (struct certlog_s*)&certlog ; l = (struct certlog_s*)l->l.next)
{
char certhex[32768];
size_t i;
const char *hex = "0123456789abcdef";
for (i = 0; i < l->certsize; i++)
{
certhex[i*2+0] = hex[l->cert[i]>>4];
certhex[i*2+1] = hex[l->cert[i]&0xf];
}
certhex[i*2] = 0;
VFS_PRINTF(f, "%s \"%s\"\n", l->hostname, certhex);
}
}
}
static void CertLog_Purge(void)
{
while (certlog.next != &certlog)
{
struct certlog_s *l = (struct certlog_s*)certlog.next;
RemoveLink(&l->l);
Z_Free(l);
}
}
static int hexdecode(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
else if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
else if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return 0;
}
static void CertLog_Import(const char *filename)
{
char addressstring[512];
char certhex[32768];
char certdata[16384];
char line[65536], *l;
size_t i, certsize;
vfsfile_t *f = FS_OpenVFS(filename, "rb", FS_ROOT);
//CertLog_Purge();
VFS_GETS(f, line, sizeof(line));
if (strncmp(line, "version 1.", 10))
return; //unsupported...
while (VFS_GETS(f, line, sizeof(line)))
{
l = line;
l = COM_ParseOut(l, addressstring, sizeof(addressstring));
l = COM_ParseOut(l, certhex, sizeof(certhex));
certsize = 0;
for (i = 0; certsize < sizeof(certdata); i++)
{
if (!certhex[(i<<1)+0] || !certhex[(i<<1)+1])
break;
certdata[certsize++] = (hexdecode(certhex[(i<<1)+0])<<4)|hexdecode(certhex[(i<<1)+1]);
}
CertLog_Update(addressstring, certdata, certsize);
}
}
static void CertLog_UntrustAll_f(void)
{
CertLog_Purge();
}
static void CertLog_Import_f(void)
{
const char *fname = Cmd_Argv(1);
if (!*fname)
fname = "knowncerts.txt";
CertLog_Import(fname);
}
struct certprompt_s
{
char *hostname;
size_t certsize;
qbyte cert[1];
};
static struct certprompt_s *certlog_curprompt;
static void CertLog_Add_Prompted(void *vctx, int button)
{
struct certprompt_s *ctx = vctx;
if (button == 0) //button_yes / button_left
{
CertLog_Update(ctx->hostname, ctx->cert, ctx->certsize);
CertLog_Write();
}
else
CL_Disconnect();
certlog_curprompt = NULL;
}
qboolean CertLog_ConnectOkay(const char *hostname, void *cert, size_t certsize)
{
struct certlog_s *l = CertLog_Find(hostname);
if (certlog_curprompt)
return false;
if (!l || l->certsize != certsize || memcmp(l->cert, cert, certsize))
{
if (qrenderer)
{
struct certprompt_s *ctx = certlog_curprompt = Z_Malloc(sizeof(*ctx)+certsize + strlen(hostname));
ctx->hostname = ctx->cert + certsize;
ctx->certsize = certsize;
memcpy(ctx->cert, cert, certsize);
strcpy(ctx->hostname, hostname);
if (!l)
M_Menu_Prompt(CertLog_Add_Prompted, ctx, "Server certificate is new", "Trust", NULL, "Disconnect");
else
M_Menu_Prompt(CertLog_Add_Prompted, ctx, "^1Server certificate HAS CHANGED\nZomg\nFlee in Terror", "ReTrust", NULL, "Disconnect");
}
return false; //can't connect yet...
}
return true;
}
#endif
void Log_ShutDown(void) void Log_ShutDown(void)
{ {
IPLog_Dump("iplog.txt"); IPLog_Dump("iplog.txt");
@ -660,4 +825,10 @@ void Log_Init(void)
if (COM_CheckParm("-condebug")) if (COM_CheckParm("-condebug"))
Cvar_ForceSet(&log_enable[LOG_CONSOLE], "1"); Cvar_ForceSet(&log_enable[LOG_CONSOLE], "1");
#ifndef SERVERONLY
ClearLink(&certlog);
Cmd_AddCommand("dtls_untrustall", CertLog_UntrustAll_f);
Cmd_AddCommand("dtls_importtrust", CertLog_Import_f);
#endif
} }

View file

@ -467,7 +467,7 @@ static qboolean ICE_SendSpam(struct icestate_s *con)
data[2] = ((buf.cursize+4+sizeof(integ)-20)>>8)&0xff; //hashed header length is up to the end of the hmac attribute data[2] = ((buf.cursize+4+sizeof(integ)-20)>>8)&0xff; //hashed header length is up to the end of the hmac attribute
data[3] = ((buf.cursize+4+sizeof(integ)-20)>>0)&0xff; data[3] = ((buf.cursize+4+sizeof(integ)-20)>>0)&0xff;
//but the hash is to the start of the attribute's header //but the hash is to the start of the attribute's header
SHA1_HMAC(integ, sizeof(integ), data, buf.cursize, con->rpwd, strlen(con->rpwd)); HMAC(SHA1_m, integ, sizeof(integ), data, buf.cursize, con->rpwd, strlen(con->rpwd));
MSG_WriteShort(&buf, BigShort(0x8)); //MESSAGE-INTEGRITY MSG_WriteShort(&buf, BigShort(0x8)); //MESSAGE-INTEGRITY
MSG_WriteShort(&buf, BigShort(20)); //sha1 key length MSG_WriteShort(&buf, BigShort(20)); //sha1 key length
SZ_Write(&buf, integ, sizeof(integ)); //integrity data SZ_Write(&buf, integ, sizeof(integ)); //integrity data
@ -1369,7 +1369,7 @@ qboolean ICE_WasStun(netsrc_t netsrc)
char key[20]; char key[20];
//the hmac is a bit weird. the header length includes the integrity attribute's length, but the checksum doesn't even consider the attribute header. //the hmac is a bit weird. the header length includes the integrity attribute's length, but the checksum doesn't even consider the attribute header.
stun->msglen = BigShort(integritypos+sizeof(integrity) - (char*)stun - sizeof(*stun)); stun->msglen = BigShort(integritypos+sizeof(integrity) - (char*)stun - sizeof(*stun));
SHA1_HMAC(key, sizeof(key), (qbyte*)stun, integritypos-4 - (char*)stun, con->lpwd, strlen(con->lpwd)); HMAC(SHA1_m, key, sizeof(key), (qbyte*)stun, integritypos-4 - (char*)stun, con->lpwd, strlen(con->lpwd));
if (memcmp(key, integrity, sizeof(integrity))) if (memcmp(key, integrity, sizeof(integrity)))
{ {
Con_DPrintf("Integrity is bad! needed %x got %x\n", *(int*)key, *(int*)integrity); Con_DPrintf("Integrity is bad! needed %x got %x\n", *(int*)key, *(int*)integrity);
@ -1514,7 +1514,7 @@ qboolean ICE_WasStun(netsrc_t netsrc)
data[2] = ((buf.cursize+4+sizeof(integrity)-20)>>8)&0xff; //hashed header length is up to the end of the hmac attribute data[2] = ((buf.cursize+4+sizeof(integrity)-20)>>8)&0xff; //hashed header length is up to the end of the hmac attribute
data[3] = ((buf.cursize+4+sizeof(integrity)-20)>>0)&0xff; data[3] = ((buf.cursize+4+sizeof(integrity)-20)>>0)&0xff;
//but the hash is to the start of the attribute's header //but the hash is to the start of the attribute's header
SHA1_HMAC(integrity, sizeof(integrity), data, buf.cursize, con->lpwd, strlen(con->lpwd)); HMAC(SHA1_m, integrity, sizeof(integrity), data, buf.cursize, con->lpwd, strlen(con->lpwd));
MSG_WriteShort(&buf, BigShort(0x8)); //MESSAGE-INTEGRITY MSG_WriteShort(&buf, BigShort(0x8)); //MESSAGE-INTEGRITY
MSG_WriteShort(&buf, BigShort(sizeof(integrity))); //sha1 key length MSG_WriteShort(&buf, BigShort(sizeof(integrity))); //sha1 key length
SZ_Write(&buf, integrity, sizeof(integrity)); //integrity data SZ_Write(&buf, integrity, sizeof(integrity)); //integrity data

View file

@ -11,6 +11,8 @@
#endif #endif
#ifdef HAVE_GNUTLS #ifdef HAVE_GNUTLS
#define privname "privkey.pem"
#define pubname "cert.pem"
#if defined(_WIN32) && !defined(MINGW) && 0 #if defined(_WIN32) && !defined(MINGW) && 0
@ -95,6 +97,8 @@ typedef int (VARGS gnutls_certificate_verify_function)(gnutls_session_t session)
#else #else
#include <gnutls/gnutls.h> #include <gnutls/gnutls.h>
#include <gnutls/abstract.h>
#include <gnutls/x509.h>
#if GNUTLS_VERSION_MAJOR >= 3 && defined(HAVE_DTLS) #if GNUTLS_VERSION_MAJOR >= 3 && defined(HAVE_DTLS)
#include <gnutls/dtls.h> #include <gnutls/dtls.h>
#else #else
@ -155,6 +159,7 @@ static ssize_t (VARGS *qgnutls_record_recv)(gnutls_session_t session, void *data
static void (VARGS *qgnutls_certificate_set_verify_function)(gnutls_certificate_credentials_t cred, gnutls_certificate_verify_function *func); static void (VARGS *qgnutls_certificate_set_verify_function)(gnutls_certificate_credentials_t cred, gnutls_certificate_verify_function *func);
static void *(VARGS *qgnutls_session_get_ptr)(gnutls_session_t session); static void *(VARGS *qgnutls_session_get_ptr)(gnutls_session_t session);
static void (VARGS *qgnutls_session_set_ptr)(gnutls_session_t session, void *ptr); static void (VARGS *qgnutls_session_set_ptr)(gnutls_session_t session, void *ptr);
int (VARGS *qgnutls_session_channel_binding)(gnutls_session_t session, gnutls_channel_binding_t cbtype, gnutls_datum_t * cb);
#ifdef GNUTLS_HAVE_SYSTEMTRUST #ifdef GNUTLS_HAVE_SYSTEMTRUST
static int (VARGS *qgnutls_certificate_set_x509_system_trust)(gnutls_certificate_credentials_t cred); static int (VARGS *qgnutls_certificate_set_x509_system_trust)(gnutls_certificate_credentials_t cred);
#else #else
@ -172,6 +177,7 @@ static int (VARGS *qgnutls_x509_crt_import)(gnutls_x509_crt_t cert, const gnutls
#endif #endif
static const gnutls_datum_t *(VARGS *qgnutls_certificate_get_peers)(gnutls_session_t session, unsigned int * list_size); static const gnutls_datum_t *(VARGS *qgnutls_certificate_get_peers)(gnutls_session_t session, unsigned int * list_size);
static gnutls_certificate_type_t (VARGS *qgnutls_certificate_type_get)(gnutls_session_t session); static gnutls_certificate_type_t (VARGS *qgnutls_certificate_type_get)(gnutls_session_t session);
static void *(VARGS *qgnutls_malloc)(size_t);
static void (VARGS *qgnutls_free)(void * ptr); static void (VARGS *qgnutls_free)(void * ptr);
static int (VARGS *qgnutls_server_name_set)(gnutls_session_t session, gnutls_server_name_type_t type, const void * name, size_t name_length); static int (VARGS *qgnutls_server_name_set)(gnutls_session_t session, gnutls_server_name_type_t type, const void * name, size_t name_length);
@ -184,6 +190,27 @@ static void (VARGS *qgnutls_dtls_prestate_set)(gnutls_session_t session, gnutls_
static void (VARGS *qgnutls_dtls_set_mtu)(gnutls_session_t session, unsigned int mtu); static void (VARGS *qgnutls_dtls_set_mtu)(gnutls_session_t session, unsigned int mtu);
#endif #endif
static unsigned int (VARGS *qgnutls_sec_param_to_pk_bits)(gnutls_pk_algorithm_t algo, gnutls_sec_param_t param);
static int (VARGS *qgnutls_x509_crt_init)(gnutls_x509_crt_t * cert);
static void (VARGS *qgnutls_x509_crt_deinit)(gnutls_x509_crt_t cert);
static int (VARGS *qgnutls_x509_crt_set_version)(gnutls_x509_crt_t crt, unsigned int version);
static int (VARGS *qgnutls_x509_crt_set_activation_time)(gnutls_x509_crt_t cert, time_t act_time);
static int (VARGS *qgnutls_x509_crt_set_expiration_time)(gnutls_x509_crt_t cert, time_t exp_time);
static int (VARGS *qgnutls_x509_crt_set_serial)(gnutls_x509_crt_t cert, const void *serial, size_t serial_size);
static int (VARGS *qgnutls_x509_crt_set_dn)(gnutls_x509_crt_t crt, const char *dn, const char **err);
static int (VARGS *qgnutls_x509_crt_set_issuer_dn)(gnutls_x509_crt_t crt, const char *dn, const char **err);
static int (VARGS *qgnutls_x509_crt_set_key)(gnutls_x509_crt_t crt, gnutls_x509_privkey_t key);
static int (VARGS *qgnutls_x509_crt_export2)(gnutls_x509_crt_t cert, gnutls_x509_crt_fmt_t format, gnutls_datum_t * out);
static int (VARGS *qgnutls_x509_privkey_init)(gnutls_x509_privkey_t * key);
static void (VARGS *qgnutls_x509_privkey_deinit)(gnutls_x509_privkey_t key);
static int (VARGS *qgnutls_x509_privkey_generate)(gnutls_x509_privkey_t key, gnutls_pk_algorithm_t algo, unsigned int bits, unsigned int flags);
static int (VARGS *qgnutls_x509_privkey_export2)(gnutls_x509_privkey_t key, gnutls_x509_crt_fmt_t format, gnutls_datum_t * out);
static int (VARGS *qgnutls_x509_crt_privkey_sign)(gnutls_x509_crt_t crt, gnutls_x509_crt_t issuer, gnutls_privkey_t issuer_key, gnutls_digest_algorithm_t dig, unsigned int flags);
static int (VARGS *qgnutls_privkey_init)(gnutls_privkey_t * key);
static void (VARGS *qgnutls_privkey_deinit)(gnutls_privkey_t key);
static int (VARGS *qgnutls_privkey_import_x509)(gnutls_privkey_t pkey, gnutls_x509_privkey_t key, unsigned int flags);
static int (VARGS *qgnutls_certificate_set_x509_key_mem)(gnutls_certificate_credentials_t res, const gnutls_datum_t * cert, const gnutls_datum_t * key, gnutls_x509_crt_fmt_t type);
static qboolean Init_GNUTLS(void) static qboolean Init_GNUTLS(void)
{ {
#ifdef GNUTLS_HAVE_SYSTEMTRUST #ifdef GNUTLS_HAVE_SYSTEMTRUST
@ -217,6 +244,30 @@ static qboolean Init_GNUTLS(void)
#endif #endif
#define GNUTLS_X509_STUFF \
GNUTLS_FUNC(gnutls_sec_param_to_pk_bits) \
GNUTLS_FUNC(gnutls_x509_crt_init) \
GNUTLS_FUNC(gnutls_x509_crt_deinit) \
GNUTLS_FUNC(gnutls_x509_crt_set_version) \
GNUTLS_FUNC(gnutls_x509_crt_set_activation_time) \
GNUTLS_FUNC(gnutls_x509_crt_set_expiration_time) \
GNUTLS_FUNC(gnutls_x509_crt_set_serial) \
GNUTLS_FUNC(gnutls_x509_crt_set_dn) \
GNUTLS_FUNC(gnutls_x509_crt_set_issuer_dn) \
GNUTLS_FUNC(gnutls_x509_crt_set_key) \
GNUTLS_FUNC(gnutls_x509_crt_export2) \
GNUTLS_FUNC(gnutls_x509_privkey_init) \
GNUTLS_FUNC(gnutls_x509_privkey_deinit) \
GNUTLS_FUNC(gnutls_x509_privkey_generate2) \
GNUTLS_FUNC(gnutls_x509_privkey_export2) \
GNUTLS_FUNC(gnutls_x509_crt_privkey_sign) \
GNUTLS_FUNC(gnutls_privkey_init) \
GNUTLS_FUNC(gnutls_privkey_deinit) \
GNUTLS_FUNC(gnutls_privkey_import_x509) \
GNUTLS_FUNC(gnutls_certificate_set_x509_key_mem)
#define GNUTLS_FUNCS \ #define GNUTLS_FUNCS \
GNUTLS_FUNC(gnutls_bye) \ GNUTLS_FUNC(gnutls_bye) \
GNUTLS_FUNC(gnutls_perror) \ GNUTLS_FUNC(gnutls_perror) \
@ -243,7 +294,8 @@ static qboolean Init_GNUTLS(void)
GNUTLS_FUNC(gnutls_certificate_type_get) \ GNUTLS_FUNC(gnutls_certificate_type_get) \
GNUTLS_FUNC(gnutls_free) \ GNUTLS_FUNC(gnutls_free) \
GNUTLS_FUNC(gnutls_server_name_set) \ GNUTLS_FUNC(gnutls_server_name_set) \
GNUTLS_DTLS_STUFF GNUTLS_DTLS_STUFF \
GNUTLS_X509_STUFF
#ifdef GNUTLS_DYNAMIC #ifdef GNUTLS_DYNAMIC
dllhandle_t *hmod; dllhandle_t *hmod;
@ -274,6 +326,7 @@ static qboolean Init_GNUTLS(void)
{(void**)&qgnutls_certificate_set_verify_function, "gnutls_certificate_set_verify_function"}, {(void**)&qgnutls_certificate_set_verify_function, "gnutls_certificate_set_verify_function"},
{(void**)&qgnutls_session_get_ptr, "gnutls_session_get_ptr"}, {(void**)&qgnutls_session_get_ptr, "gnutls_session_get_ptr"},
{(void**)&qgnutls_session_set_ptr, "gnutls_session_set_ptr"}, {(void**)&qgnutls_session_set_ptr, "gnutls_session_set_ptr"},
{(void**)&qgnutls_session_channel_binding, "gnutls_session_channel_binding"},
#ifdef GNUTLS_HAVE_SYSTEMTRUST #ifdef GNUTLS_HAVE_SYSTEMTRUST
{(void**)&qgnutls_certificate_set_x509_system_trust, "gnutls_certificate_set_x509_system_trust"}, {(void**)&qgnutls_certificate_set_x509_system_trust, "gnutls_certificate_set_x509_system_trust"},
#else #else
@ -291,6 +344,7 @@ static qboolean Init_GNUTLS(void)
#endif #endif
{(void**)&qgnutls_certificate_get_peers, "gnutls_certificate_get_peers"}, {(void**)&qgnutls_certificate_get_peers, "gnutls_certificate_get_peers"},
{(void**)&qgnutls_certificate_type_get, "gnutls_certificate_type_get"}, {(void**)&qgnutls_certificate_type_get, "gnutls_certificate_type_get"},
{(void**)&qgnutls_malloc, "gnutls_malloc"},
{(void**)&qgnutls_free, "gnutls_free"}, {(void**)&qgnutls_free, "gnutls_free"},
{(void**)&qgnutls_server_name_set, "gnutls_server_name_set"}, {(void**)&qgnutls_server_name_set, "gnutls_server_name_set"},
@ -303,6 +357,27 @@ static qboolean Init_GNUTLS(void)
{(void**)&qgnutls_dtls_set_mtu, "gnutls_dtls_set_mtu"}, {(void**)&qgnutls_dtls_set_mtu, "gnutls_dtls_set_mtu"},
#endif #endif
{(void**)&qgnutls_sec_param_to_pk_bits, "gnutls_sec_param_to_pk_bits"},
{(void**)&qgnutls_x509_crt_init, "gnutls_x509_crt_init"},
{(void**)&qgnutls_x509_crt_deinit, "gnutls_x509_crt_deinit"},
{(void**)&qgnutls_x509_crt_set_version, "gnutls_x509_crt_set_version"},
{(void**)&qgnutls_x509_crt_set_activation_time, "gnutls_x509_crt_set_activation_time"},
{(void**)&qgnutls_x509_crt_set_expiration_time, "gnutls_x509_crt_set_expiration_time"},
{(void**)&qgnutls_x509_crt_set_serial, "gnutls_x509_crt_set_serial"},
{(void**)&qgnutls_x509_crt_set_dn, "gnutls_x509_crt_set_dn"},
{(void**)&qgnutls_x509_crt_set_issuer_dn, "gnutls_x509_crt_set_issuer_dn"},
{(void**)&qgnutls_x509_crt_set_key, "gnutls_x509_crt_set_key"},
{(void**)&qgnutls_x509_crt_export2, "gnutls_x509_crt_export2"},
{(void**)&qgnutls_x509_privkey_init, "gnutls_x509_privkey_init"},
{(void**)&qgnutls_x509_privkey_deinit, "gnutls_x509_privkey_deinit"},
{(void**)&qgnutls_x509_privkey_generate, "gnutls_x509_privkey_generate"},
{(void**)&qgnutls_x509_privkey_export2, "gnutls_x509_privkey_export2"},
{(void**)&qgnutls_x509_crt_privkey_sign, "gnutls_x509_crt_privkey_sign"},
{(void**)&qgnutls_privkey_init, "gnutls_privkey_init"},
{(void**)&qgnutls_privkey_deinit, "gnutls_privkey_deinit"},
{(void**)&qgnutls_privkey_import_x509, "gnutls_privkey_import_x509"},
{(void**)&qgnutls_certificate_set_x509_key_mem, "gnutls_certificate_set_x509_key_mem"},
{NULL, NULL} {NULL, NULL}
}; };
@ -375,6 +450,36 @@ static struct
{"triptohell.info", sizeof(triptohell_certdata), triptohell_certdata}, {"triptohell.info", sizeof(triptohell_certdata), triptohell_certdata},
{"fte.triptohell.info", sizeof(fte_triptohell_certdata), fte_triptohell_certdata}, {"fte.triptohell.info", sizeof(fte_triptohell_certdata), fte_triptohell_certdata},
}; };
static int SSL_CheckUserTrust(gnutls_session_t session, gnutlsfile_t *file, int ret)
{
#ifndef SERVERONLY
//when using dtls, we expect self-signed certs and persistent trust.
if (file->datagram)
{
qbyte *certdata;
size_t certsize;
unsigned int certcount, j;
const gnutls_datum_t *const certlist = qgnutls_certificate_get_peers(session, &certcount);
for (certsize = 0, j = 0; j < certcount; j++)
certsize += certlist[j].size;
certdata = malloc(certsize);
for (certsize = 0, j = 0; j < certcount; j++)
{
memcpy(certdata+certsize, certlist[j].data, certlist[j].size);
certsize += certlist[j].size;
}
if (CertLog_ConnectOkay(file->certname, certdata, certsize))
ret = 0; //user has previously authorised it.
else
ret = GNUTLS_E_CERTIFICATE_ERROR; //user didn't trust it yet
free(certdata);
}
#endif
return ret;
}
static int QDECL SSL_CheckCert(gnutls_session_t session) static int QDECL SSL_CheckCert(gnutls_session_t session)
{ {
gnutlsfile_t *file = qgnutls_session_get_ptr (session); gnutlsfile_t *file = qgnutls_session_get_ptr (session);
@ -438,6 +543,8 @@ static int QDECL SSL_CheckCert(gnutls_session_t session)
if (preverified && (certstatus&~GNUTLS_CERT_EXPIRED) == (GNUTLS_CERT_INVALID|GNUTLS_CERT_SIGNER_NOT_FOUND)) if (preverified && (certstatus&~GNUTLS_CERT_EXPIRED) == (GNUTLS_CERT_INVALID|GNUTLS_CERT_SIGNER_NOT_FOUND))
return 0; return 0;
if (certstatus == 0) if (certstatus == 0)
return SSL_CheckUserTrust(session, file, 0);
if (certstatus == (GNUTLS_CERT_INVALID|GNUTLS_CERT_SIGNER_NOT_FOUND) && SSL_CheckUserTrust(session, file, GNUTLS_E_CERTIFICATE_ERROR))
return 0; return 0;
type = qgnutls_certificate_type_get (session); type = qgnutls_certificate_type_get (session);
@ -463,6 +570,8 @@ static int QDECL SSL_CheckCert(gnutls_session_t session)
if (preverified && certstatus == (GNUTLS_CERT_INVALID|GNUTLS_CERT_SIGNER_NOT_FOUND)) if (preverified && certstatus == (GNUTLS_CERT_INVALID|GNUTLS_CERT_SIGNER_NOT_FOUND))
return 0; return 0;
if (certstatus == 0) if (certstatus == 0)
return SSL_CheckUserTrust(session, file, 0);
if (certstatus == (GNUTLS_CERT_INVALID|GNUTLS_CERT_SIGNER_NOT_FOUND) && SSL_CheckUserTrust(session, file, GNUTLS_E_CERTIFICATE_ERROR))
return 0; return 0;
if (certstatus & GNUTLS_CERT_SIGNER_NOT_FOUND) if (certstatus & GNUTLS_CERT_SIGNER_NOT_FOUND)
@ -708,6 +817,137 @@ static gnutls_certificate_credentials_t xcred[2];
static gnutls_datum_t cookie_key; static gnutls_datum_t cookie_key;
#endif #endif
qboolean SSL_LoadPrivateCert(gnutls_certificate_credentials_t cred)
{
int ret = -1;
gnutls_datum_t priv, pub;
vfsfile_t *privf = FS_OpenVFS(privname, "rb", FS_ROOT);
vfsfile_t *pubf = FS_OpenVFS(pubname, "rb", FS_ROOT);
memset(&priv, 0, sizeof(priv));
memset(&pub, 0, sizeof(pub));
if (!privf || !pubf)
{ //not found? generate a new one.
//FIXME: how to deal with race conditions with multiple servers on the same host?
//delay till the first connection? we at least write both files at the sameish time.
//even so they might get different certs the first time the server(s) run.
gnutls_x509_privkey_t key;
gnutls_x509_crt_t cert;
char serial[64];
const char *errstr;
gnutls_pk_algorithm_t privalgo = GNUTLS_PK_RSA;
if (privf)VFS_CLOSE(privf);privf=NULL;
if (pubf)VFS_CLOSE(pubf);pubf=NULL;
Con_Printf("Generating new GNUTLS key+cert...\n");
qgnutls_x509_privkey_init(&key);
ret = qgnutls_x509_privkey_generate(key, privalgo, qgnutls_sec_param_to_pk_bits(privalgo, GNUTLS_SEC_PARAM_HIGH), 0);
if (ret < 0)
Con_Printf("gnutls_x509_privkey_generate2 failed: %i\n", ret);
ret = qgnutls_x509_privkey_export2(key, GNUTLS_X509_FMT_PEM, &priv);
if (ret < 0)
Con_Printf("gnutls_x509_privkey_export2 failed: %i\n", ret);
//stoopid browsers insisting that serial numbers are different even on throw-away self-signed certs.
//we should probably just go and make our own root ca/master. post it a cert and get a signed one (with sequential serial) back or something.
//we'll probably want something like that for client certs anyway, for stat tracking.
Q_snprintfz(serial, sizeof(serial), "%u", (unsigned)time(NULL));
qgnutls_x509_crt_init(&cert);
qgnutls_x509_crt_set_version(cert, 1);
qgnutls_x509_crt_set_activation_time(cert, time(NULL)-1);
qgnutls_x509_crt_set_expiration_time(cert, time(NULL)+(time_t)10*365*24*60*60);
qgnutls_x509_crt_set_serial(cert, serial, strlen(serial));
if (qgnutls_x509_crt_set_dn(cert, "CN=localhost", &errstr) < 0)
Con_Printf("gnutls_x509_crt_set_dn failed: %s\n", errstr);
if (qgnutls_x509_crt_set_issuer_dn(cert, "CN=localhost", &errstr) < 0)
Con_Printf("gnutls_x509_crt_set_issuer_dn failed: %s\n", errstr);
// qgnutls_x509_crt_set_key_usage(cert, GNUTLS_KEY_KEY_ENCIPHERMENT|GNUTLS_KEY_DATA_ENCIPHERMENT|);
qgnutls_x509_crt_set_key(cert, key);
/*sign it with our private key*/
{
gnutls_privkey_t akey;
qgnutls_privkey_init(&akey);
qgnutls_privkey_import_x509(akey, key, GNUTLS_PRIVKEY_IMPORT_COPY);
ret = qgnutls_x509_crt_privkey_sign(cert, cert, akey, GNUTLS_DIG_SHA256, 0);
if (ret < 0)
Con_Printf("gnutls_x509_crt_privkey_sign failed: %i\n", ret);
qgnutls_privkey_deinit(akey);
}
ret = qgnutls_x509_crt_export2(cert, GNUTLS_X509_FMT_PEM, &pub);
if (ret < 0)
Con_Printf("gnutls_x509_crt_export2 failed: %i\n", ret);
qgnutls_x509_crt_deinit(cert);
qgnutls_x509_privkey_deinit(key);
if (priv.size && pub.size)
{
privf = FS_OpenVFS(privname, "wb", FS_ROOT);
if (privf)
{
Con_Printf("Wrote %s\n", privname);
VFS_WRITE(privf, priv.data, priv.size);
VFS_CLOSE(privf);
}
memset(priv.data, 0, priv.size);
qgnutls_free(priv.data);
memset(&priv, 0, sizeof(priv));
pubf = FS_OpenVFS(pubname, "wb", FS_ROOT);
if (pubf)
{
Con_Printf("Wrote %s\n", pubname);
VFS_WRITE(pubf, pub.data, pub.size);
VFS_CLOSE(pubf);
}
qgnutls_free(pub.data);
memset(&pub, 0, sizeof(pub));
privf = FS_OpenVFS(privname, "rb", FS_ROOT);
pubf = FS_OpenVFS(pubname, "rb", FS_ROOT);
Con_Printf("Certificate generated\n");
}
}
if (privf && pubf)
{
//read the two files now
priv.size = VFS_GETLEN(privf);
priv.data = qgnutls_malloc(priv.size+1);
if (priv.size != VFS_READ(privf, priv.data, priv.size))
priv.size = 0;
priv.data[priv.size] = 0;
pub.size = VFS_GETLEN(pubf);
pub.data = qgnutls_malloc(pub.size+1);
if (pub.size != VFS_READ(pubf, pub.data, pub.size))
pub.size = 0;
pub.data[pub.size] = 0;
}
//FIXME: extend the expiration time if its old?
if (priv.size && pub.size)
{ //submit them to gnutls
ret = qgnutls_certificate_set_x509_key_mem(cred, &pub, &priv, GNUTLS_X509_FMT_PEM);
if (ret < 0)
Con_Printf("gnutls_certificate_set_x509_key_mem failed: %i\n", ret);
}
else
Con_Printf("Unable to read/generate cert\n");
memset(priv.data, 0, priv.size);//just in case. FIXME: we didn't scrub the filesystem code. libc has its own caches etc. lets hope that noone comes up with some way to scrape memory remotely (although if they can inject code then we've lost either way so w/e)
qgnutls_free(priv.data);
qgnutls_free(pub.data);
return ret>=0;
}
qboolean SSL_InitGlobal(qboolean isserver) qboolean SSL_InitGlobal(qboolean isserver)
{ {
static int initstatus[2]; static int initstatus[2];
@ -742,6 +982,10 @@ qboolean SSL_InitGlobal(qboolean isserver)
if (isserver) if (isserver)
{ {
#if 1
if (!SSL_LoadPrivateCert(xcred[isserver]))
initstatus[isserver] = -1;
#else
int ret = -1; int ret = -1;
char keyfile[MAX_OSPATH]; char keyfile[MAX_OSPATH];
char certfile[MAX_OSPATH]; char certfile[MAX_OSPATH];
@ -754,6 +998,7 @@ qboolean SSL_InitGlobal(qboolean isserver)
Con_Printf("No certificate or key was found in %s and %s\n", certfile, keyfile); Con_Printf("No certificate or key was found in %s and %s\n", certfile, keyfile);
initstatus[isserver] = -1; initstatus[isserver] = -1;
} }
#endif
} }
else else
qgnutls_certificate_set_verify_function (xcred[isserver], SSL_CheckCert); qgnutls_certificate_set_verify_function (xcred[isserver], SSL_CheckCert);
@ -846,6 +1091,28 @@ vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean isserver
return &newf->funcs; return &newf->funcs;
} }
int TLS_GetChannelBinding(vfsfile_t *vf, qbyte *binddata, size_t *bindsize)
{
gnutls_datum_t cb;
gnutlsfile_t *f = (gnutlsfile_t*)vf;
if (vf->Close != SSL_Close)
return -1; //err, not a tls connection.
if (qgnutls_session_channel_binding(f->session, GNUTLS_CB_TLS_UNIQUE, &cb))
{ //error of some kind
//if the error is because of the other side not supporting it, then we should return 0 here.
return -1;
}
else
{
if (cb.size > *bindsize)
return 0; //overflow
*bindsize = cb.size;
memcpy(binddata, cb.data, cb.size);
return 1;
}
}
#ifdef HAVE_DTLS #ifdef HAVE_DTLS
@ -1017,13 +1284,13 @@ static const dtlsfuncs_t dtlsfuncs_gnutls =
GNUDTLS_Received, GNUDTLS_Received,
GNUDTLS_Timeouts, GNUDTLS_Timeouts,
}; };
const dtlsfuncs_t *DTLS_InitServer(void) const dtlsfuncs_t *GNUDTLS_InitServer(void)
{ {
if (!SSL_InitGlobal(true)) if (!SSL_InitGlobal(true))
return NULL; //unable to init a server certificate. don't allow dtls to init. return NULL; //unable to init a server certificate. don't allow dtls to init.
return &dtlsfuncs_gnutls; return &dtlsfuncs_gnutls;
} }
const dtlsfuncs_t *DTLS_InitClient(void) const dtlsfuncs_t *GNUDTLS_InitClient(void)
{ {
return &dtlsfuncs_gnutls; return &dtlsfuncs_gnutls;
} }

View file

@ -150,7 +150,7 @@ typedef struct {
#ifdef HAVE_DTLS #ifdef HAVE_DTLS
void *cbctx; void *cbctx;
void (*transmit)(void *cbctx, qbyte *data, size_t datasize); neterr_t (*transmit)(void *cbctx, const qbyte *data, size_t datasize);
#endif #endif
} sslfile_t; } sslfile_t;
@ -407,11 +407,15 @@ static DWORD VerifyKnownCertificates(DWORD status, wchar_t *domain, qbyte *data,
int i; int i;
if (datagram) if (datagram)
{ {
Con_Printf("FIXME: Ring of trust not yet implemented\n"); if (status == CERT_E_UNTRUSTEDROOT || SUCCEEDED(status))
if (status == CERT_E_UNTRUSTEDROOT)
{ {
Con_Printf("Allowing (probably) self-signed cert.\n"); #ifndef SERVERONLY
char realdomain[256];
if (CertLog_ConnectOkay(narrowen(realdomain, sizeof(realdomain), domain), data, datasize))
status = SEC_E_OK; status = SEC_E_OK;
else
#endif
status = TRUST_E_FAIL;
} }
return status; return status;
} }
@ -1003,7 +1007,8 @@ vfsfile_t *FS_OpenSSL(const char *servername, vfsfile_t *source, qboolean server
int i = 0; int i = 0;
int err; int err;
unsigned int c; unsigned int c;
const char *localname, *peername; // const char *localname;
const char *peername;
if (!source || !SSL_Inited()) if (!source || !SSL_Inited())
{ {
@ -1013,12 +1018,12 @@ vfsfile_t *FS_OpenSSL(const char *servername, vfsfile_t *source, qboolean server
} }
if (server) if (server)
{ {
localname = servername; // localname = servername;
peername = ""; peername = "";
} }
else else
{ {
localname = ""; // localname = "";
peername = servername; peername = servername;
} }
@ -1071,6 +1076,46 @@ vfsfile_t *FS_OpenSSL(const char *servername, vfsfile_t *source, qboolean server
return &newf->funcs; return &newf->funcs;
} }
#ifndef SECPKG_ATTR_UNIQUE_BINDINGS
#define SECPKG_ATTR_UNIQUE_BINDINGS 25
typedef struct _SecPkgContext_Bindings
{
unsigned long BindingsLength;
SEC_CHANNEL_BINDINGS *Bindings;
} SecPkgContext_Bindings, *PSecPkgContext_Bindings;
#endif
int TLS_GetChannelBinding(vfsfile_t *vf, qbyte *binddata, size_t *bindsize)
{
int ret;
sslfile_t *f = (sslfile_t*)vf;
SecPkgContext_Bindings bindings;
if (vf->Close != SSPI_Close)
return -2; //not one of ours.
bindings.BindingsLength = 0;
bindings.Bindings = NULL;
ret = 0;
switch(secur.pQueryContextAttributesA(&f->sechnd, SECPKG_ATTR_UNIQUE_BINDINGS, &bindings))
{
case SEC_E_OK:
if (bindings.Bindings->cbApplicationDataLength <= *bindsize)
{
//will contain 'tls-unique:BINARYDATA'
*bindsize = bindings.Bindings->cbApplicationDataLength-11;
memcpy(binddata, ((unsigned char*) bindings.Bindings) + bindings.Bindings->dwApplicationDataOffset+11, bindings.Bindings->cbApplicationDataLength-11);
ret = 1;
}
//FIXME: leak
//secur.pFreeContextBuffer(bindings.Bindings);
break;
case SEC_E_UNSUPPORTED_FUNCTION:
ret = -1; //schannel doesn't support it. too old an OS, I guess.
break;
default:
break;
}
return ret;
}
#include "netinc.h" #include "netinc.h"
#if 0 #if 0

View file

@ -5033,7 +5033,7 @@ ftenet_generic_connection_t *FTENET_TCPConnect_EstablishConnection(qboolean isse
ftenet_tcpconnect_connection_t *newcon; ftenet_tcpconnect_connection_t *newcon;
unsigned long _true = true; unsigned long _true = true;
int newsocket; SOCKET newsocket;
qboolean tls = (adr.prot == NP_TLS || adr.prot == NP_WSS); qboolean tls = (adr.prot == NP_TLS || adr.prot == NP_WSS);
#ifndef HAVE_SSL #ifndef HAVE_SSL

View file

@ -347,6 +347,7 @@ qboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *na
int NET_EnumerateAddresses(ftenet_connections_t *collection, struct ftenet_generic_connection_s **con, unsigned int *adrflags, netadr_t *addresses, int maxaddresses); int NET_EnumerateAddresses(ftenet_connections_t *collection, struct ftenet_generic_connection_s **con, unsigned int *adrflags, netadr_t *addresses, int maxaddresses);
vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server); vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server);
int TLS_GetChannelBinding(vfsfile_t *stream, qbyte *data, size_t *datasize); //datasize should be preinitialised to the max length allowed. -1 for not implemented. 0 for peer problems. 1 for success
#ifdef HAVE_PACKET #ifdef HAVE_PACKET
vfsfile_t *FS_OpenTCPSocket(SOCKET socket, qboolean conpending, const char *peername); //conpending allows us to reject any writes until the connection has succeeded vfsfile_t *FS_OpenTCPSocket(SOCKET socket, qboolean conpending, const char *peername); //conpending allows us to reject any writes until the connection has succeeded
#endif #endif

View file

@ -1093,6 +1093,36 @@ qintptr_t VARGS Plug_Net_SetTLSClient(void *offset, quintptr_t mask, const qintp
} }
return 0; return 0;
} }
qintptr_t VARGS Plug_Net_GetTLSBinding(void *offset, quintptr_t mask, const qintptr_t *arg)
{
pluginstream_t *stream;
unsigned int handle = VM_LONG(arg[0]);
qbyte *binddata = VM_POINTER(arg[1]);
unsigned int *bindsize = VM_POINTER(arg[2]);
size_t sz;
int r;
if (VM_OOB(arg[2], sizeof(int)))
return -2;
if (VM_OOB(arg[1], *bindsize))
return -2;
if (handle < 0 || handle >= pluginstreamarraylen || pluginstreamarray[handle].plugin != currentplug)
{
Con_Printf("Plug_Net_GetTLSBinding: socket does not belong to you (or is invalid)\n");
return -2;
}
stream = &pluginstreamarray[handle];
if (stream->type != STREAM_VFS)
{ //not a socket - invalid
Con_Printf("Plug_Net_GetTLSBinding: Not a socket handle\n");
return -2;
}
sz = *bindsize;
r = TLS_GetChannelBinding(stream->vfs, binddata, &sz);
*bindsize = sz;
return r;
}
#endif #endif
#endif #endif
@ -1607,6 +1637,7 @@ void Plug_Initialise(qboolean fromgamedir)
Plug_RegisterBuiltin("Net_TCPConnect", Plug_Net_TCPConnect, 0); Plug_RegisterBuiltin("Net_TCPConnect", Plug_Net_TCPConnect, 0);
#ifdef HAVE_SSL #ifdef HAVE_SSL
Plug_RegisterBuiltin("Net_SetTLSClient", Plug_Net_SetTLSClient, 0); Plug_RegisterBuiltin("Net_SetTLSClient", Plug_Net_SetTLSClient, 0);
Plug_RegisterBuiltin("Net_GetTLSBinding", Plug_Net_GetTLSBinding, 0);
#endif #endif
Plug_RegisterBuiltin("Net_Recv", Plug_Net_Recv, 0); Plug_RegisterBuiltin("Net_Recv", Plug_Net_Recv, 0);
Plug_RegisterBuiltin("Net_Send", Plug_Net_Send, 0); Plug_RegisterBuiltin("Net_Send", Plug_Net_Send, 0);
@ -1746,7 +1777,7 @@ qboolean Plug_ConsoleLinkMouseOver(float x, float y, char *text, char *info)
char *ptr; char *ptr;
ptr = (char*)COM_QuotedString(text, buffer, sizeof(buffer)-10, false); ptr = (char*)COM_QuotedString(text, buffer, sizeof(buffer)-10, false);
ptr += strlen(ptr); ptr += strlen(ptr);
*ptr = ' '; *ptr++ = ' ';
COM_QuotedString(info, ptr, sizeof(buffer)-(ptr-buffer), false); COM_QuotedString(info, ptr, sizeof(buffer)-(ptr-buffer), false);
Cmd_TokenizeString(buffer, false, false); Cmd_TokenizeString(buffer, false, false);

View file

@ -4502,14 +4502,14 @@ void QCBUILTIN PF_uri_get (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
G_FLOAT(OFS_RETURN) = 0; G_FLOAT(OFS_RETURN) = 0;
#else #else
world_t *w = prinst->parms->user; world_t *w = prinst->parms->user;
struct dl_download *dl; struct dl_download *dl = NULL;
const unsigned char *url = PR_GetStringOfs(prinst, OFS_PARM0); const unsigned char *url = PR_GetStringOfs(prinst, OFS_PARM0);
float id = G_FLOAT(OFS_PARM1); float id = G_FLOAT(OFS_PARM1);
const char *mimetype = (prinst->callargc >= 3)?PR_GetStringOfs(prinst, OFS_PARM2):""; const char *mimetype = (prinst->callargc >= 3)?PR_GetStringOfs(prinst, OFS_PARM2):"";
const char *dataorsep = (prinst->callargc >= 4)?PR_GetStringOfs(prinst, OFS_PARM3):""; const char *dataorsep = (prinst->callargc >= 4)?PR_GetStringOfs(prinst, OFS_PARM3):"";
int strbufid = (prinst->callargc >= 5)?G_FLOAT(OFS_PARM4):0; int strbufid = (prinst->callargc >= 5)?G_FLOAT(OFS_PARM4):0;
//float cryptokey = (prinst->callargc >= 5)?G_FLOAT(OFS_PARM5):0; //DP feature, not supported in FTE. //float cryptokeyidx = (prinst->callargc >= 5)?G_FLOAT(OFS_PARM5):0; //DP feature, not supported in FTE. adds a X-D0-Blind-ID-Detached-Signature header signing [postdata\0]querystring
if (!pr_enable_uriget.ival) if (!pr_enable_uriget.ival)
{ {
@ -4528,20 +4528,57 @@ void QCBUILTIN PF_uri_get (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
if (*mimetype) if (*mimetype)
{ {
const char *data;
Con_DPrintf("PF_uri_post(%s,%g)\n", url, id); Con_DPrintf("PF_uri_post(%s,%g)\n", url, id);
if (strbufid) if (strbufid)
{ {
//convert the string buffer into a simple string using dataorsep as a separator size_t bufno = strbufid-BUFSTRBASE;
//not supported at this time if (bufno < strbufmax && strbuflist[bufno].prinst == prinst)
dl = NULL; {
Con_DPrintf("PF_uri_post: stringbuffers not supported\n"); const char *glue = dataorsep;
unsigned int gluelen = strlen(dataorsep);
size_t datalen, l, i;
char **strings;
char *data;
//count neededlength
strings = strbuflist[bufno].strings;
for (i = 0, datalen = 0; i < strbuflist[bufno].used; i++)
{
if (strings[i])
{
if (datalen)
datalen += gluelen;
datalen += strlen(strings[i]);
}
}
//concat it, with dataorsep separating each element
data = malloc(datalen+1);
for (i = 0, datalen = 0; i < strbuflist[bufno].used; i++)
{
if (strings[i])
{
if (datalen)
{
memcpy(data+datalen, glue, gluelen);
datalen += gluelen;
}
l = strlen(strings[i]);
memcpy(data+datalen, strings[i], l);
datalen += l;
}
}
//add the null and send it
data[datalen] = 0;
dl = HTTP_CL_Put(url, mimetype, data, strlen(data), PR_uri_get_callback);
free(data);
}
} }
else else
{ {
//simple data post. //simple data post.
data = dataorsep; dl = HTTP_CL_Put(url, mimetype, dataorsep, strlen(dataorsep), PR_uri_get_callback);
dl = HTTP_CL_Put(url, mimetype, data, strlen(data), PR_uri_get_callback);
} }
} }
else else

View file

@ -179,7 +179,7 @@ memset(&finalcount, 0, 8);
} }
int SHA1(char *digest, int maxdigestsize, const char *string, int stringlen) size_t SHA1(unsigned char *digest, size_t maxdigestsize, const unsigned char *string, size_t stringlen)
{ {
SHA1_CTX context; SHA1_CTX context;
if (maxdigestsize < DIGEST_SIZE) if (maxdigestsize < DIGEST_SIZE)
@ -192,6 +192,21 @@ int SHA1(char *digest, int maxdigestsize, const char *string, int stringlen)
return DIGEST_SIZE; return DIGEST_SIZE;
} }
size_t SHA1_m(unsigned char *digest, size_t maxdigestsize, size_t numstrings, const unsigned char **strings, size_t *stringlens)
{
size_t i;
SHA1_CTX context;
if (maxdigestsize < DIGEST_SIZE)
return 0;
SHA1Init(&context);
for (i = 0; i < numstrings; i++)
SHA1Update(&context, (unsigned char*) strings[i], stringlens[i]);
SHA1Final(digest, &context);
return DIGEST_SIZE;
}
/* hmac-sha1.c -- hashed message authentication codes /* hmac-sha1.c -- hashed message authentication codes
Copyright (C) 2005, 2006 Free Software Foundation, Inc. Copyright (C) 2005, 2006 Free Software Foundation, Inc.
@ -225,56 +240,45 @@ static void memxor(char *dest, const char *src, size_t length)
} }
} }
int SHA1_HMAC(unsigned char *digest, int maxdigestsize, typedef size_t hashfunc_t(unsigned char *digest, size_t maxdigestsize, size_t numstrings, const unsigned char **strings, size_t *stringlens);
const unsigned char *data, int datalen, size_t HMAC(hashfunc_t *hashfunc, unsigned char *digest, size_t maxdigestsize,
const unsigned char *key, int keylen) const unsigned char *data, size_t datalen,
const unsigned char *key, size_t keylen)
{ {
SHA1_CTX inner; #define HMAC_DIGEST_MAXSIZE 20
SHA1_CTX outer; char optkeybuf[HMAC_DIGEST_MAXSIZE];
char optkeybuf[DIGEST_SIZE]; char innerhash[HMAC_DIGEST_MAXSIZE];
char block[64];
char innerhash[DIGEST_SIZE];
if (maxdigestsize < DIGEST_SIZE) char block[64];
return 0; int innerhashsize;
/* Reduce the key's size, so that it is never larger than a block. */ /* Reduce the key's size, so that it is never larger than a block. */
if (keylen > 64) if (keylen > sizeof(block))
{ {
SHA1_CTX keyhash; keylen = hashfunc(optkeybuf, sizeof(optkeybuf), 1, &key, &keylen);
SHA1Init (&keyhash);
SHA1Update (&keyhash, key, keylen);
SHA1Final (optkeybuf, &keyhash);
key=optkeybuf; key=optkeybuf;
keylen = sizeof(optkeybuf);
} }
/* Compute INNERHASH from KEY and IN. */ /* Compute INNERHASH from KEY and IN. */
SHA1Init (&inner);
memset (block, IPAD, sizeof (block)); memset (block, IPAD, sizeof (block));
memxor (block, key, keylen); memxor (block, key, keylen);
SHA1Update (&inner, block, 64); {
SHA1Update (&inner, data, datalen); const unsigned char *strings_i[2] = {block, data};
size_t stringlens_i[2] = {sizeof(block), datalen};
SHA1Final (innerhash, &inner); innerhashsize = hashfunc(innerhash, sizeof(innerhash), 2, strings_i, stringlens_i);
}
/* Compute result from KEY and INNERHASH. */ /* Compute result from KEY and INNERHASH. */
SHA1Init (&outer);
memset (block, OPAD, sizeof (block)); memset (block, OPAD, sizeof (block));
memxor (block, key, keylen); memxor (block, key, keylen);
SHA1Update (&outer, block, 64); {
SHA1Update (&outer, innerhash, 20); const unsigned char *strings_o[2] = {block, innerhash};
size_t stringlens_o[2] = {sizeof(block), innerhashsize};
SHA1Final (digest, &outer); return hashfunc(digest, maxdigestsize, 2, strings_o, stringlens_o);
}
return DIGEST_SIZE;
} }

View file

@ -304,6 +304,9 @@ void D3D11_UploadLightmap(lightmapinfo_t *lm)
case TF_RGBA32: case TF_RGBA32:
mips.encoding = PTI_RGBX8; mips.encoding = PTI_RGBX8;
break; break;
default:
Sys_Error("D3D11_UploadLightmap: Unsupported format");
return;
} }
mips.mipcount = 1; mips.mipcount = 1;
D3D11_LoadTextureMips(tex, &mips); D3D11_LoadTextureMips(tex, &mips);

View file

@ -2,7 +2,7 @@
#include "glquake.h" #include "glquake.h"
#include "gl_draw.h" #include "gl_draw.h"
#ifdef D3D8QUAKE #ifdef D3D8QUAKE
//#define FIXME //#define FIXME //for Eukara to fix, if he ever wants to get the d3d8 renderer working fully.
#include "shader.h" #include "shader.h"
#if !defined(HMONITOR_DECLARED) && (WINVER < 0x0500) #if !defined(HMONITOR_DECLARED) && (WINVER < 0x0500)
#define HMONITOR_DECLARED #define HMONITOR_DECLARED
@ -48,6 +48,7 @@ extern float d3d_trueprojection[16];
static void BE_RotateForEntity (const entity_t *e, const model_t *mod); static void BE_RotateForEntity (const entity_t *e, const model_t *mod);
/*========================================== tables for deforms =====================================*/ /*========================================== tables for deforms =====================================*/
#define R_FastSin(x) sin((x)*(2*M_PI))
#define frand() (rand()*(1.0/RAND_MAX)) #define frand() (rand()*(1.0/RAND_MAX))
#define FTABLE_SIZE 1024 #define FTABLE_SIZE 1024
#define FTABLE_CLAMP(x) (((int)((x)*FTABLE_SIZE) & (FTABLE_SIZE-1))) #define FTABLE_CLAMP(x) (((int)((x)*FTABLE_SIZE) & (FTABLE_SIZE-1)))
@ -59,6 +60,7 @@ static float r_squaretable[FTABLE_SIZE];
static float r_sawtoothtable[FTABLE_SIZE]; static float r_sawtoothtable[FTABLE_SIZE];
static float r_inversesawtoothtable[FTABLE_SIZE]; static float r_inversesawtoothtable[FTABLE_SIZE];
#if FIXME
static float *FTableForFunc ( unsigned int func ) static float *FTableForFunc ( unsigned int func )
{ {
switch (func) switch (func)
@ -82,6 +84,7 @@ static float *FTableForFunc ( unsigned int func )
//bad values allow us to crash (so I can debug em) //bad values allow us to crash (so I can debug em)
return NULL; return NULL;
} }
#endif
static void FTable_Init(void) static void FTable_Init(void)
{ {
@ -110,6 +113,7 @@ static void FTable_Init(void)
} }
} }
#if FIXME
typedef vec3_t mat3_t[3]; typedef vec3_t mat3_t[3];
static mat3_t axisDefault={{1, 0, 0}, static mat3_t axisDefault={{1, 0, 0},
{0, 1, 0}, {0, 1, 0},
@ -139,6 +143,7 @@ static int Matrix3_Compare(const mat3_t in, const mat3_t out)
{ {
return !memcmp(in, out, sizeof(mat3_t)); return !memcmp(in, out, sizeof(mat3_t));
} }
#endif
/*================================================*/ /*================================================*/
@ -708,6 +713,7 @@ static void SelectPassTexture(unsigned int tu, shaderpass_t *pass)
} }
} }
#if FIXME
static void colourgenbyte(const shaderpass_t *pass, int cnt, byte_vec4_t *srcb, vec4_t *srcf, byte_vec4_t *dst, const mesh_t *mesh) static void colourgenbyte(const shaderpass_t *pass, int cnt, byte_vec4_t *srcb, vec4_t *srcf, byte_vec4_t *dst, const mesh_t *mesh)
{ {
D3DCOLOR block; D3DCOLOR block;
@ -1025,9 +1031,11 @@ static unsigned int BE_GenerateColourMods(unsigned int vertcount, const shaderpa
#endif #endif
return ret; return ret;
} }
#endif
/*********************************************************************************************************/ /*********************************************************************************************************/
/*========================================== texture coord generation =====================================*/ /*========================================== texture coord generation =====================================*/
#if FIXME
static void tcgen_environment(float *st, unsigned int numverts, float *xyz, float *normal) static void tcgen_environment(float *st, unsigned int numverts, float *xyz, float *normal)
{ {
int i; int i;
@ -1105,7 +1113,6 @@ static void tcmod(const tcmod_t *tcmod, int cnt, const float *src, float *dst, c
float t1, t2; float t1, t2;
float cost, sint; float cost, sint;
int j; int j;
#define R_FastSin(x) sin((x)*(2*M_PI))
switch (tcmod->type) switch (tcmod->type)
{ {
case SHADER_TCMOD_ROTATE: case SHADER_TCMOD_ROTATE:
@ -1188,7 +1195,7 @@ static void GenerateTCMods(const shaderpass_t *pass, float *dest)
mesh_t *mesh; mesh_t *mesh;
unsigned int mno; unsigned int mno;
// unsigned int fvertex = 0; //unused variable // unsigned int fvertex = 0; //unused variable
int i; // int i;
float *src; float *src;
float *out; float *out;
for (mno = 0; mno < shaderstate.nummeshes; mno++) for (mno = 0; mno < shaderstate.nummeshes; mno++)
@ -1459,7 +1466,7 @@ static void deformgen(const deformv_t *deformv, int cnt, vecV_t *src, vecV_t *ds
// break; // break;
} }
} }
#endif
/*does not do the draw call, does not consider indicies (except for billboard generation) */ /*does not do the draw call, does not consider indicies (except for billboard generation) */
@ -1489,7 +1496,7 @@ static qboolean BE_DrawMeshChain_SetupPass(shaderpass_t *pass, unsigned int vert
/*all meshes in a chain must have the same features*/ /*all meshes in a chain must have the same features*/
vdec = D3DFVF_QVBO; vdec = D3DFVF_QVBO;
allocvertexbuffer(shaderstate.dynvbo_buff, shaderstate.dynvbo_size, &shaderstate.dynvbo_offs, &map, vertcount*sizeof(*map)); allocvertexbuffer(shaderstate.dynvbo_buff, shaderstate.dynvbo_size, &shaderstate.dynvbo_offs, (void**)&map, vertcount*sizeof(*map));
*vertfirst = (shaderstate.dynvbo_offs - vertcount*sizeof(*map))/sizeof(*map); *vertfirst = (shaderstate.dynvbo_offs - vertcount*sizeof(*map))/sizeof(*map);
@ -1590,6 +1597,7 @@ static void BE_SubmitMeshChain(unsigned int firstvert, unsigned int vertcount, u
RQuantAdd(RQUANT_PRIMITIVEINDICIES, idxcount); RQuantAdd(RQUANT_PRIMITIVEINDICIES, idxcount);
} }
#ifdef FIXME
static void R_FetchPlayerColour(unsigned int cv, vec3_t rgb) static void R_FetchPlayerColour(unsigned int cv, vec3_t rgb)
{ {
int i; int i;
@ -1623,7 +1631,6 @@ static void R_FetchPlayerColour(unsigned int cv, vec3_t rgb)
}*/ }*/
} }
#ifdef FIXME
static void BE_ApplyUniforms(program_t *prog, int permu) static void BE_ApplyUniforms(program_t *prog, int permu)
{ {
struct programpermu_s *perm = &prog->permu[permu]; struct programpermu_s *perm = &prog->permu[permu];
@ -2364,7 +2371,7 @@ static void D3D8BE_GenBatchVBOs(vbo_t **vbochain, batch_t *firstbatch, batch_t *
for (i = 0; i < m->numvertexes; i++) for (i = 0; i < m->numvertexes; i++)
{ {
VectorCopy(m->xyz_array[i], vbovdata->coord); VectorCopy(m->xyz_array[i], vbovdata->coord);
vbovdata->coord[3] = 1; //vbovdata->coord[3] = 1;
Vector2Copy(m->st_array[i], vbovdata->tc[0]); Vector2Copy(m->st_array[i], vbovdata->tc[0]);
Vector2Copy(m->lmst_array[0][i], vbovdata->tc[1]); Vector2Copy(m->lmst_array[0][i], vbovdata->tc[1]);
Vector4Scale(m->colors4f_array[0][i], 255, vbovdata->colorsb); Vector4Scale(m->colors4f_array[0][i], 255, vbovdata->colorsb);

View file

@ -23,7 +23,7 @@
> >
<Tool <Tool
Name="VCNMakeTool" Name="VCNMakeTool"
BuildCommandLine="cd $(InputDir)\.. &amp;&amp; vcify make web-dbg" BuildCommandLine="cd $(InputDir)\.. &amp;&amp; vcify make web-dbg DEPCC="
ReBuildCommandLine="" ReBuildCommandLine=""
CleanCommandLine="cd $(InputDir)\.. &amp;&amp; vcify make clean -j8" CleanCommandLine="cd $(InputDir)\.. &amp;&amp; vcify make clean -j8"
Output="" Output=""

View file

@ -319,6 +319,7 @@ Global
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.GLRelease|Win32.Build.0 = Release|Win32 {32B12987-DF8C-4E40-B07C-B18586A4CA65}.GLRelease|Win32.Build.0 = Release|Win32
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.GLRelease|x64.ActiveCfg = Release|Win32 {32B12987-DF8C-4E40-B07C-B18586A4CA65}.GLRelease|x64.ActiveCfg = Release|Win32
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.MDebug|Win32.ActiveCfg = Debug|Win32 {32B12987-DF8C-4E40-B07C-B18586A4CA65}.MDebug|Win32.ActiveCfg = Debug|Win32
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.MDebug|Win32.Build.0 = Debug|Win32
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.MDebug|x64.ActiveCfg = Release|Win32 {32B12987-DF8C-4E40-B07C-B18586A4CA65}.MDebug|x64.ActiveCfg = Release|Win32
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.MinGLDebug|Win32.ActiveCfg = Debug|Win32 {32B12987-DF8C-4E40-B07C-B18586A4CA65}.MinGLDebug|Win32.ActiveCfg = Debug|Win32
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.MinGLDebug|x64.ActiveCfg = Debug|x64 {32B12987-DF8C-4E40-B07C-B18586A4CA65}.MinGLDebug|x64.ActiveCfg = Debug|x64

View file

@ -202,7 +202,7 @@ struct vk_rendertarg vk_rt_bloom[2][MAXLEVELS], vk_rt_filter;
void VK_R_BloomBlend (texid_t source, int x, int y, int w, int h) void VK_R_BloomBlend (texid_t source, int x, int y, int w, int h)
{ {
int i; int i;
struct vk_rendertarg *oldfbo = vk.rendertarg; // struct vk_rendertarg *oldfbo = vk.rendertarg;
texid_t intex; texid_t intex;
int pixels = 1; int pixels = 1;
int targetpixels = r_bloom_size.value * vid.pixelwidth / 320; int targetpixels = r_bloom_size.value * vid.pixelwidth / 320;

View file

@ -39,6 +39,10 @@ extern unsigned int r2d_be_flags;
#include <ft2build.h> #include <ft2build.h>
#include FT_FREETYPE_H #include FT_FREETYPE_H
#ifndef FT_LOAD_COLOR
#define FT_LOAD_COLOR (1L<<20)
#endif
static FT_Library fontlib; static FT_Library fontlib;
@ -48,11 +52,22 @@ FT_Error (VARGS *pFT_Init_FreeType) (FT_Library *alibrary);
FT_Error (VARGS *pFT_Load_Char) (FT_Face face, FT_ULong char_code, FT_Int32 load_flags); FT_Error (VARGS *pFT_Load_Char) (FT_Face face, FT_ULong char_code, FT_Int32 load_flags);
FT_UInt (VARGS *pFT_Get_Char_Index) (FT_Face face, FT_ULong charcode); FT_UInt (VARGS *pFT_Get_Char_Index) (FT_Face face, FT_ULong charcode);
FT_Error (VARGS *pFT_Set_Pixel_Sizes) (FT_Face face, FT_UInt pixel_width, FT_UInt pixel_height); FT_Error (VARGS *pFT_Set_Pixel_Sizes) (FT_Face face, FT_UInt pixel_width, FT_UInt pixel_height);
FT_Error (VARGS *pFT_Select_Size) (FT_Face face, FT_Int strike_index);
FT_Error (VARGS *pFT_New_Face) (FT_Library library, const char *pathname, FT_Long face_index, FT_Face *aface); FT_Error (VARGS *pFT_New_Face) (FT_Library library, const char *pathname, FT_Long face_index, FT_Face *aface);
FT_Error (VARGS *pFT_New_Memory_Face) (FT_Library library, const FT_Byte* file_base, FT_Long file_size, FT_Long face_index, FT_Face *aface); FT_Error (VARGS *pFT_New_Memory_Face) (FT_Library library, const FT_Byte* file_base, FT_Long file_size, FT_Long face_index, FT_Face *aface);
FT_Error (VARGS *pFT_Done_Face) (FT_Face face); FT_Error (VARGS *pFT_Done_Face) (FT_Face face);
#else
typedef unsigned int FT_Pixel_Mode; //for consistency even without freetype support.
#endif #endif
#ifndef FT_PIXEL_MODE_GRAY
#define FT_PIXEL_MODE_GRAY 2
#endif
#ifndef FT_PIXEL_MODE_BGRA
#define FT_PIXEL_MODE_BGRA 7 //added in FT 2.5
#endif
#define FT_PIXEL_MODE_RGBA_SA (~(FT_Pixel_Mode)0) //RGBA, straight alpha. not in freetype.
static const char *imgs[] = static const char *imgs[] =
{ {
//0xe10X //0xe10X
@ -158,18 +173,25 @@ static const char *imgs[] =
"e16f" "e16f"
}; };
#define FONTCHARS (1<<16) #define FONT_MAXCHARS 0x110000 //as defined by UTF-16, and thus applied to all unicode because UTF-16 is the crappy limited one.
#define FONTPLANES (1<<2) //this is total, not per font. #define FONTBLOCKS ((FONT_MAXCHARS+FONTBLOCKSIZE-1)/FONTBLOCKSIZE)
#define PLANEIDXTYPE unsigned short #define FONTBLOCKSIZE 0x100 //must be power-of-two
#define CHARIDXTYPE unsigned short #define FONTBLOCKMASK (FONTBLOCKSIZE-1)
#define FONTIMAGES (1<<2) //this is total, not per font.
#define FIMAGEIDXTYPE unsigned char
#define CHARIDXTYPE unsigned int
#define INVALIDPLANE ((1<<(8*sizeof(PLANEIDXTYPE)))-1) #define INVALIDPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-1)
#define BITMAPPLANE ((1<<(8*sizeof(PLANEIDXTYPE)))-2) #define BITMAPPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-2)
#define DEFAULTPLANE ((1<<(8*sizeof(PLANEIDXTYPE)))-3) #define DEFAULTPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-3)
#define SINGLEPLANE ((1<<(8*sizeof(PLANEIDXTYPE)))-4) #define SINGLEPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-4)
#define TRACKERIMAGE ((1<<(8*sizeof(PLANEIDXTYPE)))-5) #define TRACKERIMAGE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-5)
#define PLANEWIDTH (1<<8) #define FIMAGEWIDTH (1<<10)
#define PLANEHEIGHT PLANEWIDTH #define FIMAGEHEIGHT FIMAGEWIDTH
#define FONTPLANES FONTIMAGES
#define PLANEWIDTH FIMAGEWIDTH
#define PLANEHEIGHT FIMAGEHEIGHT
#ifdef AVAIL_FREETYPE #ifdef AVAIL_FREETYPE
//windows' font linking allows linking multiple extra fonts to a main font. //windows' font linking allows linking multiple extra fonts to a main font.
@ -203,18 +225,20 @@ typedef struct font_s
{ {
struct charcache_s *nextchar; struct charcache_s *nextchar;
PLANEIDXTYPE texplane; FIMAGEIDXTYPE texplane;
unsigned char advance; //how wide this char is, when drawn unsigned char advance; //how wide this char is, when drawn
char pad; unsigned short block; //to quickly find the char again
unsigned char bmx; //glyph offset+sizes. I guess these are not strictly needed, but whatever
unsigned char bmy; unsigned short bmx;
unsigned short bmy;
unsigned char bmw; unsigned char bmw;
unsigned char bmh; unsigned char bmh;
//positions inside the atlas
short top; short top;
short left; short left;
} chars[FONTCHARS]; } *chars[FONTBLOCKS];
char name[MAX_OSPATH]; char name[MAX_OSPATH];
short charheight; short charheight;
@ -231,15 +255,15 @@ typedef struct font_s
//shared between fonts. //shared between fonts.
typedef struct { typedef struct {
texid_t texnum[FONTPLANES]; texid_t texnum[FONTIMAGES];
texid_t defaultfont; texid_t defaultfont;
texid_t trackerimage; texid_t trackerimage;
unsigned char plane[PLANEWIDTH*PLANEHEIGHT][4]; //tracks the current plane unsigned char plane[PLANEWIDTH*PLANEHEIGHT][4]; //tracks the current plane
PLANEIDXTYPE activeplane; FIMAGEIDXTYPE activeplane;
unsigned char planerowx; unsigned short planerowx;
unsigned char planerowy; unsigned short planerowy;
unsigned char planerowh; unsigned short planerowh;
qboolean planechanged; qboolean planechanged;
struct charcache_s *oldestchar; struct charcache_s *oldestchar;
@ -492,14 +516,54 @@ void Font_FlushPlane(void)
fontplanes.newestchar = NULL; fontplanes.newestchar = NULL;
} }
static struct charcache_s *Font_GetCharIfLoaded(font_t *f, unsigned int charidx)
{
struct charcache_s *c = f->chars[charidx/FONTBLOCKSIZE];
if (c)
{
c += charidx&FONTBLOCKMASK;
if (c->texplane == INVALIDPLANE)
c = NULL;
}
return c;
}
static struct charcache_s *Font_GetCharStore(font_t *f, unsigned int charidx)
{ //should only be called if generating a char cache
struct charcache_s *c;
size_t i;
c = f->chars[charidx/FONTBLOCKSIZE];
if (!c)
{
c = Z_Malloc(sizeof(struct charcache_s) * FONTBLOCKSIZE);
f->chars[charidx/FONTBLOCKSIZE] = c;
for (i = 0; i < FONTBLOCKSIZE; i++)
{
c[i].texplane = INVALIDPLANE;
}
}
c += charidx&FONTBLOCKMASK;
c->block = charidx/FONTBLOCKSIZE;
return c;
}
static struct charcache_s *Font_CopyChar(font_t *f, unsigned int oldcharidx, unsigned int newcharidx)
{
struct charcache_s *new, *old = Font_GetCharIfLoaded(f, oldcharidx);
if (!old)
return NULL;
new = Font_GetCharStore(f, newcharidx);
*new = *old;
new->block = newcharidx/FONTBLOCKSIZE;
return new;
}
//loads a new image into a given character slot for the given font. //loads a new image into a given character slot for the given font.
//note: make sure it doesn't already exist or things will get cyclic //note: make sure it doesn't already exist or things will get cyclic
//alphaonly says if its a greyscale image. false means rgba. //alphaonly says if its a greyscale image. false means rgba.
static struct charcache_s *Font_LoadGlyphData(font_t *f, CHARIDXTYPE charidx, int alphaonly, void *data, unsigned int bmw, unsigned int bmh, unsigned int pitch) static struct charcache_s *Font_LoadGlyphData(font_t *f, CHARIDXTYPE charidx, FT_Pixel_Mode pixelmode, void *data, unsigned int bmw, unsigned int bmh, unsigned int pitch)
{ {
int x, y; int x, y;
unsigned char *out; unsigned char *out;
struct charcache_s *c = &f->chars[charidx]; struct charcache_s *c = Font_GetCharStore(f, charidx);
int pad = 0; int pad = 0;
#define BORDERCOLOUR 0 #define BORDERCOLOUR 0
@ -534,8 +598,8 @@ static struct charcache_s *Font_LoadGlyphData(font_t *f, CHARIDXTYPE charidx, in
fontplanes.planerowx += bmw+pad*2; fontplanes.planerowx += bmw+pad*2;
out = (unsigned char *)&fontplanes.plane[c->bmx+((int)c->bmy-pad)*PLANEHEIGHT]; out = (unsigned char *)&fontplanes.plane[c->bmx+((int)c->bmy-pad)*PLANEHEIGHT];
if (alphaonly) if (pixelmode == FT_PIXEL_MODE_GRAY)
{ { //8bit font
for (y = -pad; y < 0; y++) for (y = -pad; y < 0; y++)
{ {
for (x = -pad; x < bmw+pad; x++) for (x = -pad; x < bmw+pad; x++)
@ -563,8 +627,8 @@ static struct charcache_s *Font_LoadGlyphData(font_t *f, CHARIDXTYPE charidx, in
out += PLANEWIDTH*4; out += PLANEWIDTH*4;
} }
} }
else else if (pixelmode == FT_PIXEL_MODE_RGBA_SA)
{ { //rgba font
pitch*=4; pitch*=4;
for (y = -pad; y < 0; y++) for (y = -pad; y < 0; y++)
{ {
@ -590,10 +654,132 @@ static struct charcache_s *Font_LoadGlyphData(font_t *f, CHARIDXTYPE charidx, in
out += PLANEWIDTH*4; out += PLANEWIDTH*4;
} }
} }
else if (pixelmode == FT_PIXEL_MODE_BGRA)
{ //bgra srgb font, already premultiplied
for (y = -pad; y < 0; y++)
{
for (x = -pad; x < bmw+pad; x++)
*(unsigned int *)&out[x*4] = BORDERCOLOUR;
out += PLANEWIDTH*4;
}
for (; y < bmh; y++)
{
for (x = -pad; x < 0; x++)
*(unsigned int *)&out[x*4] = BORDERCOLOUR;
for (; x < bmw; x++)
{
out[x*4+0] = ((unsigned char*)data)[x*4+2];
out[x*4+1] = ((unsigned char*)data)[x*4+1];
out[x*4+2] = ((unsigned char*)data)[x*4+0];
out[x*4+3] = ((unsigned char*)data)[x*4+3];
}
for (; x < bmw+pad; x++)
*(unsigned int *)&out[x*4] = BORDERCOLOUR;
data = (char*)data + pitch;
out += PLANEWIDTH*4;
}
for (; y < bmh+pad; y++)
{
for (x = -pad; x < bmw+pad; x++)
*(unsigned int *)&out[x*4] = BORDERCOLOUR;
out += PLANEWIDTH*4;
}
}
fontplanes.planechanged = true; fontplanes.planechanged = true;
return c; return c;
} }
unsigned short hex[16] = {
/*0*/ (7<<0)|(5<<3)|(5<<6)|(5<<9)|(7<<12),
/*1*/ (2<<0)|(2<<3)|(2<<6)|(2<<9)|(2<<12),
/*2*/ (7<<0)|(1<<3)|(7<<6)|(4<<9)|(7<<12),
/*3*/ (7<<0)|(1<<3)|(3<<6)|(1<<9)|(7<<12),
/*4*/ (5<<0)|(5<<3)|(7<<6)|(1<<9)|(1<<12),
/*5*/ (7<<0)|(4<<3)|(7<<6)|(1<<9)|(7<<12),
/*6*/ (4<<0)|(4<<3)|(7<<6)|(5<<9)|(7<<12),
/*7*/ (7<<0)|(1<<3)|(1<<6)|(1<<9)|(1<<12),
/*8*/ (7<<0)|(5<<3)|(7<<6)|(5<<9)|(7<<12),
/*9*/ (7<<0)|(5<<3)|(7<<6)|(1<<9)|(1<<12),
/*A*/ (7<<0)|(5<<3)|(7<<6)|(5<<9)|(5<<12),
/*B*/ (6<<0)|(5<<3)|(7<<6)|(5<<9)|(6<<12),
/*C*/ (7<<0)|(5<<3)|(4<<6)|(5<<9)|(7<<12),
/*D*/ (6<<0)|(5<<3)|(5<<6)|(5<<9)|(6<<12),
/*E*/ (7<<0)|(4<<3)|(6<<6)|(4<<9)|(7<<12),
/*F*/ (7<<0)|(4<<3)|(6<<6)|(4<<9)|(4<<12)
};
static struct charcache_s *Font_LoadPlaceholderGlyph(font_t *f, CHARIDXTYPE charidx)
{
struct charcache_s *c;
unsigned int out[169*4], i, o, g, b, w, h, d, n;
d = (f->charheight >= 11);
if (d)
{ //two rows
h = 11;
if (charidx > 0xffff)
n = 3;
else if (charidx > 0xff)
n = 2;
else
n = 1;
w = n*4-1;
n*=2;
}
else
{ //single row. bye bye fixed-width.
if (charidx > 0xfffff)
n = 6;
else if (charidx > 0xffff)
n = 5;
else if (charidx > 0xfff)
n = 4;
else if (charidx > 0xff)
n = 3;
else
n = 2;
w = n*4-1;
h = 5;
}
//figure out if we can get away with giving it a little more border to boost readability
b = (h+2 < f->charheight);
w += b*2;
h += b*2;
memset(out, 0xff, sizeof(out));
for (i = 0; i < n; i++)
{
g = hex[0xf & (charidx>>(i<<2))];
o = b + w*b;
if (d)
{ //stradle them over the two rows.
if (i >= (n>>1))
o += 4*(n-i-1);
else
{
o += 4*((n>>1)-i-1);
o += w * 6;
}
}
else
o += 4*(n-i-1); //just arrange them in order
for (; g; g>>=3, o+=w)
{
if (g & 4) out[o+0] = 0xff0000ff;
if (g & 2) out[o+1] = 0xff0000ff;
if (g & 1) out[o+2] = 0xff0000ff;
}
}
c = Font_LoadGlyphData(f, charidx, FT_PIXEL_MODE_RGBA_SA, out, w, h, w);
if (c)
{
c->advance = w+1;
c->left = 0;
c->top = (f->charheight-h-1)/2;
}
return c;
}
//loads the given charidx for the given font, importing from elsewhere if needed. //loads the given charidx for the given font, importing from elsewhere if needed.
static struct charcache_s *Font_TryLoadGlyph(font_t *f, CHARIDXTYPE charidx) static struct charcache_s *Font_TryLoadGlyph(font_t *f, CHARIDXTYPE charidx)
{ {
@ -629,7 +815,7 @@ static struct charcache_s *Font_TryLoadGlyph(font_t *f, CHARIDXTYPE charidx)
} }
s+=128; s+=128;
} }
c = Font_LoadGlyphData(f, charidx, false, img, 8*scale, 8*scale, 8*scale); c = Font_LoadGlyphData(f, charidx, FT_PIXEL_MODE_RGBA_SA, img, 8*scale, 8*scale, 8*scale);
if (c) if (c)
{ {
c->advance = 8*scale; c->advance = 8*scale;
@ -691,7 +877,7 @@ static struct charcache_s *Font_TryLoadGlyph(font_t *f, CHARIDXTYPE charidx)
} }
} }
c = Font_LoadGlyphData(f, charidx, false, img, nw, nh, 64); c = Font_LoadGlyphData(f, charidx, FT_PIXEL_MODE_RGBA_SA, img, nw, nh, 64);
if (c) if (c)
{ {
c->left = 0; c->left = 0;
@ -704,7 +890,7 @@ static struct charcache_s *Font_TryLoadGlyph(font_t *f, CHARIDXTYPE charidx)
/*make tab invisible*/ /*make tab invisible*/
if (charidx == '\t' || charidx == '\n') if (charidx == '\t' || charidx == '\n')
{ {
c = &f->chars[charidx]; c = &f->chars[charidx/FONTBLOCKSIZE][charidx&FONTBLOCKMASK];
c->left = 0; c->left = 0;
c->advance = f->charheight; c->advance = f->charheight;
c->top = 0; c->top = 0;
@ -724,20 +910,40 @@ static struct charcache_s *Font_TryLoadGlyph(font_t *f, CHARIDXTYPE charidx)
for (file = 0; file < f->ftfaces; file++) for (file = 0; file < f->ftfaces; file++)
{ {
FT_Face face = f->face[file]->face; FT_Face face = f->face[file]->face;
// if (f->face[file]->activeheight)
if (f->face[file]->activeheight != f->charheight) if (f->face[file]->activeheight != f->charheight)
{ {
f->face[file]->activeheight = f->charheight; f->face[file]->activeheight = f->charheight;
// if (FT_HAS_FIXED_SIZES(face))
// pFT_Select_Size(face, 0);
// else
pFT_Set_Pixel_Sizes(face, 0, f->charheight); pFT_Set_Pixel_Sizes(face, 0, f->charheight);
} }
if (charidx == 0xfffe || pFT_Get_Char_Index(face, charidx)) //ignore glyph 0 (undefined) if (charidx == 0xfffe || pFT_Get_Char_Index(face, charidx)) //ignore glyph 0 (undefined)
if (pFT_Load_Char(face, charidx, FT_LOAD_RENDER) == 0) if (pFT_Load_Char(face, charidx, FT_LOAD_RENDER|FT_LOAD_COLOR) == 0)
{ {
FT_GlyphSlot slot; FT_GlyphSlot slot;
FT_Bitmap *bm; FT_Bitmap *bm;
slot = face->glyph; slot = face->glyph;
bm = &slot->bitmap; bm = &slot->bitmap;
c = Font_LoadGlyphData(f, charidx, true, bm->buffer, bm->width, bm->rows, bm->pitch); if (!f->face[file]->activeheight && bm->pixel_mode == FT_PIXEL_MODE_BGRA)
{
unsigned int *out = alloca(f->charheight*f->charheight*4);
Image_ResampleTexture((void*)bm->buffer, bm->width, bm->rows, out, f->charheight, f->charheight);
c = Font_LoadGlyphData(f, charidx, bm->pixel_mode, out, f->charheight, f->charheight, f->charheight*4);
if (c)
{
c->advance = f->charheight;
c->left = 0;
c->top = 0;
return c;
}
}
else
{
c = Font_LoadGlyphData(f, charidx, bm->pixel_mode, bm->buffer, bm->width, bm->rows, bm->pitch);
if (c) if (c)
{ {
@ -749,16 +955,11 @@ static struct charcache_s *Font_TryLoadGlyph(font_t *f, CHARIDXTYPE charidx)
} }
} }
} }
}
#endif #endif
if (charidx == '\r') if (charidx == '\r')
{ return Font_CopyChar(f, charidx|0xe000, charidx);
if (f->chars[charidx|0xe000].texplane != INVALIDPLANE)
{
f->chars[charidx] = f->chars[charidx|0xe000];
return &f->chars[charidx];
}
}
return NULL; return NULL;
} }
@ -768,12 +969,12 @@ static struct charcache_s *Font_GetChar(font_t *f, unsigned int codepoint)
{ {
CHARIDXTYPE charidx; CHARIDXTYPE charidx;
struct charcache_s *c; struct charcache_s *c;
if (codepoint > CON_CHARMASK) if (codepoint > FONT_MAXCHARS)
charidx = 0xfffd; charidx = 0xfffd;
else else
charidx = codepoint; charidx = codepoint;
c = &f->chars[charidx]; c = Font_GetCharIfLoaded(f, charidx);
if (c->texplane == INVALIDPLANE) if (!c)
{ {
if (charidx >= TRACKERFIRST && charidx < TRACKERFIRST+100) if (charidx >= TRACKERFIRST && charidx < TRACKERFIRST+100)
{ {
@ -806,23 +1007,25 @@ static struct charcache_s *Font_GetChar(font_t *f, unsigned int codepoint)
charidx = wc2koi_table[charidx - 0x400]; charidx = wc2koi_table[charidx - 0x400];
if (charidx != '?') if (charidx != '?')
{ {
c = &f->chars[charidx]; c = Font_GetCharIfLoaded(f, charidx);
if (c->texplane == INVALIDPLANE) if (!c)
c = Font_TryLoadGlyph(f, charidx); c = Font_TryLoadGlyph(f, charidx);
} }
} }
if (!c)
c = Font_LoadPlaceholderGlyph(f, charidx);
if (!c) if (!c)
{ {
charidx = 0xfffd; //unicode's replacement char charidx = 0xfffd; //unicode's replacement char
c = &f->chars[charidx]; c = Font_GetCharIfLoaded(f, charidx);
if (c->texplane == INVALIDPLANE) if (!c)
c = Font_TryLoadGlyph(f, charidx); c = Font_TryLoadGlyph(f, charidx);
} }
if (!c) if (!c)
{ {
charidx = '?'; //meh charidx = '?'; //meh
c = &f->chars[charidx]; c = Font_GetCharIfLoaded(f, charidx);
if (c->texplane == INVALIDPLANE) if (!c)
c = Font_TryLoadGlyph(f, charidx); c = Font_TryLoadGlyph(f, charidx);
} }
} }
@ -840,6 +1043,9 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, const char *fontfil
if (!*fontfilename) if (!*fontfilename)
return false; return false;
if (height < 1)
height = 1;
//ran out of font slots. //ran out of font slots.
if (f->ftfaces == MAX_FTFACES) if (f->ftfaces == MAX_FTFACES)
return false; return false;
@ -856,12 +1062,24 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, const char *fontfil
if (!fontlib) if (!fontlib)
{ {
#if 0
pFT_Init_FreeType = FT_Init_FreeType;
pFT_Load_Char = FT_Load_Char;
pFT_Get_Char_Index = FT_Get_Char_Index;
pFT_Set_Pixel_Sizes = FT_Set_Pixel_Sizes;
pFT_Select_Size = FT_Select_Size;
pFT_New_Face = FT_New_Face;
pFT_New_Memory_Face = FT_New_Memory_Face;
pFT_Init_FreeType = FT_Init_FreeType;
pFT_Done_Face = FT_Done_Face;
#else
dllfunction_t ft2funcs[] = dllfunction_t ft2funcs[] =
{ {
{(void**)&pFT_Init_FreeType, "FT_Init_FreeType"}, {(void**)&pFT_Init_FreeType, "FT_Init_FreeType"},
{(void**)&pFT_Load_Char, "FT_Load_Char"}, {(void**)&pFT_Load_Char, "FT_Load_Char"},
{(void**)&pFT_Get_Char_Index, "FT_Get_Char_Index"}, {(void**)&pFT_Get_Char_Index, "FT_Get_Char_Index"},
{(void**)&pFT_Set_Pixel_Sizes, "FT_Set_Pixel_Sizes"}, {(void**)&pFT_Set_Pixel_Sizes, "FT_Set_Pixel_Sizes"},
{(void**)&pFT_Select_Size, "FT_Select_Size"},
{(void**)&pFT_New_Face, "FT_New_Face"}, {(void**)&pFT_New_Face, "FT_New_Face"},
{(void**)&pFT_New_Memory_Face, "FT_New_Memory_Face"}, {(void**)&pFT_New_Memory_Face, "FT_New_Memory_Face"},
{(void**)&pFT_Init_FreeType, "FT_Init_FreeType"}, {(void**)&pFT_Init_FreeType, "FT_Init_FreeType"},
@ -873,6 +1091,8 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, const char *fontfil
triedtoloadfreetype = true; triedtoloadfreetype = true;
#ifdef _WIN32 #ifdef _WIN32
fontmodule = Sys_LoadLibrary("libfreetype-6", ft2funcs);
if (!fontmodule)
fontmodule = Sys_LoadLibrary("freetype6", ft2funcs); fontmodule = Sys_LoadLibrary("freetype6", ft2funcs);
#else #else
fontmodule = Sys_LoadLibrary("libfreetype.so.6", ft2funcs); fontmodule = Sys_LoadLibrary("libfreetype.so.6", ft2funcs);
@ -882,6 +1102,7 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, const char *fontfil
Con_DPrintf("Couldn't load freetype library.\n"); Con_DPrintf("Couldn't load freetype library.\n");
return false; return false;
} }
#endif
error = pFT_Init_FreeType(&fontlib); error = pFT_Init_FreeType(&fontlib);
if (error) if (error)
{ {
@ -952,6 +1173,12 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, const char *fontfil
#endif #endif
if (!error) if (!error)
{ {
if (FT_HAS_FIXED_SIZES(face))
{
height = 0; //will need to rescale manually I guess
error = pFT_Select_Size(face, 0);
}
else
error = pFT_Set_Pixel_Sizes(face, 0, height); error = pFT_Set_Pixel_Sizes(face, 0, height);
if (!error) if (!error)
{ {
@ -1181,11 +1408,13 @@ static texid_t Font_LoadDefaultConchars(void)
COM_WorkerPartialSync(tex, &tex->status, TEX_LOADING); COM_WorkerPartialSync(tex, &tex->status, TEX_LOADING);
if (TEXLOADED(tex)) if (TEXLOADED(tex))
return tex; return tex;
#ifdef HEXEN2
tex = Font_LoadHexen2Conchars(true); tex = Font_LoadHexen2Conchars(true);
if (tex && tex->status == TEX_LOADING) if (tex && tex->status == TEX_LOADING)
COM_WorkerPartialSync(tex, &tex->status, TEX_LOADING); COM_WorkerPartialSync(tex, &tex->status, TEX_LOADING);
if (TEXLOADED(tex)) if (TEXLOADED(tex))
return tex; return tex;
#endif
tex = Font_LoadFallbackConchars(); tex = Font_LoadFallbackConchars();
if (tex && tex->status == TEX_LOADING) if (tex && tex->status == TEX_LOADING)
COM_WorkerPartialSync(tex, &tex->status, TEX_LOADING); COM_WorkerPartialSync(tex, &tex->status, TEX_LOADING);
@ -1246,6 +1475,7 @@ struct font_s *Font_LoadFont(float vheight, const char *fontfilename)
char *parms; char *parms;
int height = ((vheight * vid.rotpixelheight)/vid.height) + 0.5; int height = ((vheight * vid.rotpixelheight)/vid.height) + 0.5;
char facename[MAX_QPATH]; char facename[MAX_QPATH];
struct charcache_s *c;
Q_strncpy(facename, fontfilename, sizeof(facename)); Q_strncpy(facename, fontfilename, sizeof(facename));
@ -1391,39 +1621,37 @@ struct font_s *Font_LoadFont(float vheight, const char *fontfilename)
for (i = 0x00; i <= 0xff; i++) for (i = 0x00; i <= 0xff; i++)
{ {
f->chars[i].advance = (height*3)/4; c = Font_GetCharStore(f, i);
f->chars[i].left = 0; c->advance = (height*3)/4;
f->chars[i].top = 0; c->left = 0;
f->chars[i].nextchar = 0; //these chars are not linked in c->top = 0;
f->chars[i].pad = 0; c->nextchar = 0; //these chars are not linked in
f->chars[i].texplane = BITMAPPLANE; /*if its a 'raster' font, don't use the default chars, always use the raster images*/ c->texplane = BITMAPPLANE; /*if its a 'raster' font, don't use the default chars, always use the raster images*/
if (i >= 'a' && i <= 'z') if (i >= 'a' && i <= 'z')
{ {
f->chars[i].bmx = ((i - 64)&15)*8; c->bmx = ((i - 64)&15)*8;
f->chars[i].bmy = ((i - 64)/16)*8; c->bmy = ((i - 64)/16)*8;
f->chars[i].bmh = 8; c->bmh = 8;
f->chars[i].bmw = 8; c->bmw = 8;
} }
else if (i >= 32 && i < 96) else if (i >= 32 && i < 96)
{ {
f->chars[i].bmx = ((i - 32)&15)*8; c->bmx = ((i - 32)&15)*8;
f->chars[i].bmy = ((i - 32)/16)*8; c->bmy = ((i - 32)/16)*8;
f->chars[i].bmh = 8; c->bmh = 8;
f->chars[i].bmw = 8; c->bmw = 8;
} }
else else
{ {
f->chars[i].bmh = 0; c->bmh = 0;
f->chars[i].bmw = 0; c->bmw = 0;
f->chars[i].bmx = 0; c->bmx = 0;
f->chars[i].bmy = 0; c->bmy = 0;
} }
}
for (i = 0xe000; i <= 0xe0ff; i++) Font_CopyChar(f, i, i|0xe0ff);
{
f->chars[i] = f->chars[i&0xff];
} }
return f; return f;
} }
@ -1486,21 +1714,22 @@ struct font_s *Font_LoadFont(float vheight, const char *fontfilename)
for ( ; i < 32; i++) for ( ; i < 32; i++)
{ {
f->chars[i].texplane = INVALIDPLANE; // f->chars[i].texplane = INVALIDPLANE;
} }
/*force it to load, even if there's nothing there*/ /*force it to load, even if there's nothing there*/
for ( ; i < 128; i++) for ( ; i < 128; i++)
{ {
f->chars[i].advance = f->charheight; c = Font_GetCharStore(f, i);
f->chars[i].bmh = PLANEWIDTH/16;
f->chars[i].bmw = PLANEWIDTH/16; c->advance = f->charheight;
f->chars[i].bmx = (i&15)*(PLANEWIDTH/16); c->bmh = PLANEWIDTH/16;
f->chars[i].bmy = (i/16)*(PLANEWIDTH/16); c->bmw = PLANEWIDTH/16;
f->chars[i].left = 0; c->bmx = (i&15)*(PLANEWIDTH/16);
f->chars[i].top = 0; c->bmy = (i/16)*(PLANEWIDTH/16);
f->chars[i].nextchar = 0; //these chars are not linked in c->left = 0;
f->chars[i].pad = 0; c->top = 0;
f->chars[i].texplane = BITMAPPLANE; c->nextchar = 0; //these chars are not linked in
c->texplane = BITMAPPLANE;
} }
} }
@ -1521,24 +1750,19 @@ struct font_s *Font_LoadFont(float vheight, const char *fontfilename)
f->singletexture = fontplanes.defaultfont; f->singletexture = fontplanes.defaultfont;
} }
for (; i < FONTCHARS; i++)
{
f->chars[i].texplane = INVALIDPLANE;
}
/*pack the default chars into it*/ /*pack the default chars into it*/
for (i = 0xe000; i <= 0xe0ff; i++) for (i = 0xe000; i <= 0xe0ff; i++)
{ {
f->chars[i].advance = f->charheight; c = Font_GetCharStore(f, i);
f->chars[i].bmh = PLANEWIDTH/16; c->advance = f->charheight;
f->chars[i].bmw = PLANEWIDTH/16; c->bmh = PLANEWIDTH/16;
f->chars[i].bmx = (i&15)*(PLANEWIDTH/16); c->bmw = PLANEWIDTH/16;
f->chars[i].bmy = ((i/16)*(PLANEWIDTH/16)) & 0xff; c->bmx = ((i&15))*(PLANEWIDTH/16);
f->chars[i].left = 0; c->bmy = ((i&0xf0)/16)*(PLANEWIDTH/16);
f->chars[i].top = 0; c->left = 0;
f->chars[i].nextchar = 0; //these chars are not linked in c->top = 0;
f->chars[i].pad = 0; c->nextchar = 0; //these chars are not linked in
f->chars[i].texplane = defaultplane; c->texplane = defaultplane;
} }
return f; return f;
} }
@ -1559,7 +1783,7 @@ void Font_Free(struct font_s *f)
for (link = &fontplanes.oldestchar; *link; ) for (link = &fontplanes.oldestchar; *link; )
{ {
c = *link; c = *link;
if (c >= f->chars && c <= f->chars + FONTCHARS) if (f->chars[c->block] && c >= f->chars[c->block] && c <= f->chars[c->block] + FONTBLOCKSIZE)
{ {
c = c->nextchar; c = c->nextchar;
if (!c) if (!c)

View file

@ -4871,6 +4871,11 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g
int idx = G_INT(OFS_PARM1); int idx = G_INT(OFS_PARM1);
int id; int id;
const char *newvals; const char *newvals;
if (idx >= MAX_EDICTS) //we need some sanity limit... many ents will get removed like lights so this one isn't quite correct, but it'll be in the right sort of ballpark.
{
G_INT(OFS_RETURN) = 0;
return;
}
//if there's no ents, then that's a problem. make sure that there's at least a worldspawn. //if there's no ents, then that's a problem. make sure that there's at least a worldspawn.
if (!mod->numentityinfo) if (!mod->numentityinfo)
Mod_ParseEntities(mod); Mod_ParseEntities(mod);
@ -4902,6 +4907,7 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g
else else
{ {
newvals = NULL; newvals = NULL;
if (idx < mod->numentityinfo)
mod->entityinfo[idx].id = 0; mod->entityinfo[idx].id = 0;
} }

View file

@ -712,7 +712,6 @@ qboolean R_ImportRTLights(const char *entlump)
//failing that, it will insert lights with some crappy fixed radius around only all 'classname light' entities, without any colours or anything, vanilla only. //failing that, it will insert lights with some crappy fixed radius around only all 'classname light' entities, without any colours or anything, vanilla only.
//such lights are ONLY created if they're not near some other existing light (like a static entity one). //such lights are ONLY created if they're not near some other existing light (like a static entity one).
//this can result in FTE having noticably more and bigger lights than tenebrae. shadowmapping doesn't help performance either. //this can result in FTE having noticably more and bigger lights than tenebrae. shadowmapping doesn't help performance either.
float lightmaplevel = -1;
COM_Parse(entlump); COM_Parse(entlump);
if (!strcmp(com_token, "Version")) if (!strcmp(com_token, "Version"))
@ -929,7 +928,7 @@ qboolean R_ImportRTLights(const char *entlump)
else if (entnum == 0 && !strcmp("lightmapbright", key)) else if (entnum == 0 && !strcmp("lightmapbright", key))
{ {
//tenebrae compat. this overrides r_shadow_realtime_world_lightmap //tenebrae compat. this overrides r_shadow_realtime_world_lightmap
lightmaplevel = atof(value); r_shadow_realtime_world_lightmaps.value = atof(value);
} }
} }
if (!islight) if (!islight)

View file

@ -2850,9 +2850,9 @@ static void Shaderpass_VideoMap (shader_t *shader, shaderpass_t *pass, char **pt
pass->cin = Media_StartCin(token); pass->cin = Media_StartCin(token);
if (!pass->cin) if (!pass->cin)
pass->cin = Media_StartCin(va("video/%s.roq", token)); {
if (!pass->cin)
Con_DPrintf (CON_WARNING "(shader %s) Couldn't load video %s\n", shader->name, token); Con_DPrintf (CON_WARNING "(shader %s) Couldn't load video %s\n", shader->name, token);
}
if (pass->cin) if (pass->cin)
{ {
@ -4362,6 +4362,7 @@ void Shader_Programify (shader_t *s)
char *prog = NULL; char *prog = NULL;
const char *mask; const char *mask;
char args[1024]; char args[1024];
qboolean eightbit = false;
/* enum /* enum
{ {
T_UNKNOWN, T_UNKNOWN,
@ -4437,11 +4438,19 @@ void Shader_Programify (shader_t *s)
else if (modellighting) else if (modellighting)
{ {
pass = modellighting; pass = modellighting;
eightbit = r_softwarebanding && (qrenderer == QR_OPENGL) && sh_config.progs_supported;
if (eightbit)
prog = "defaultskin#EIGHTBIT";
else
prog = "defaultskin"; prog = "defaultskin";
} }
else if (lightmap) else if (lightmap)
{ {
pass = modellighting; pass = modellighting;
eightbit = r_softwarebanding && (qrenderer == QR_OPENGL || qrenderer == QR_VULKAN) && sh_config.progs_supported;
if (eightbit)
prog = "defaultwall#EIGHTBIT";
else
prog = "defaultwall"; prog = "defaultwall";
} }
else if (vertexlighting) else if (vertexlighting)
@ -4487,6 +4496,12 @@ void Shader_Programify (shader_t *s)
} }
else else
{ {
if (eightbit)
{
s->passes[s->numpasses].anim_frames[0] = R_LoadColourmapImage();
s->passes[s->numpasses++].texgen = T_GEN_SINGLEMAP;
}
else
s->passes[s->numpasses++].texgen = T_GEN_DIFFUSE; s->passes[s->numpasses++].texgen = T_GEN_DIFFUSE;
s->flags |= SHADER_HASDIFFUSE; s->flags |= SHADER_HASDIFFUSE;
} }

View file

@ -186,6 +186,8 @@ void (APIENTRY *qglBufferDataARB)(GLenum target, GLsizei size, const void* data,
void (APIENTRY *qglBufferSubDataARB)(GLenum target, GLint offset, GLsizei size, void* data); void (APIENTRY *qglBufferSubDataARB)(GLenum target, GLint offset, GLsizei size, void* data);
void *(APIENTRY *qglMapBufferARB)(GLenum target, GLenum access); void *(APIENTRY *qglMapBufferARB)(GLenum target, GLenum access);
GLboolean (APIENTRY *qglUnmapBufferARB)(GLenum target); GLboolean (APIENTRY *qglUnmapBufferARB)(GLenum target);
void *(APIENTRY *qglMapBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
#endif #endif
void (APIENTRY *qglGenVertexArrays)(GLsizei n, GLuint *arrays); void (APIENTRY *qglGenVertexArrays)(GLsizei n, GLuint *arrays);
@ -855,6 +857,9 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name))
qglMapBufferARB = NULL; qglMapBufferARB = NULL;
qglUnmapBufferARB = NULL; qglUnmapBufferARB = NULL;
} }
//ARB_map_buffer_range: core in gl3.0/gles3.0, the extension is backported, and thus no ARB postfix on functions.
qglMapBufferRange = (void *)getglext("glMapBufferRange");
#endif #endif
#ifdef GL_STATIC #ifdef GL_STATIC
@ -2620,7 +2625,7 @@ static void GLSlang_ProgAutoFields(program_t *prog, const char *progname, cvar_t
} }
//the vid routines have initialised a window, and now they are giving us a reference to some of of GetProcAddress to get pointers to the funcs. //the vid routines have initialised a window, and now they are giving us a reference to some of of GetProcAddress to get pointers to the funcs.
void GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name)) qboolean GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name))
{ {
#ifndef GL_STATIC #ifndef GL_STATIC
qglBindTexture = (void *)getglcore("glBindTexture"); //for compleateness. core in 1.1. needed by fte. qglBindTexture = (void *)getglcore("glBindTexture"); //for compleateness. core in 1.1. needed by fte.
@ -2942,6 +2947,8 @@ void GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name))
sh_config.nv_tex_env_combine4 = gl_config.nv_tex_env_combine4; sh_config.nv_tex_env_combine4 = gl_config.nv_tex_env_combine4;
sh_config.env_add = gl_config.env_add; sh_config.env_add = gl_config.env_add;
} }
return true;
} }

View file

@ -84,8 +84,7 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
vid.activeapp = true; vid.activeapp = true;
GL_Init(info, GLES_GetSymbol); return GL_Init(info, GLES_GetSymbol);
return true;
} }
#else #else
@ -174,8 +173,7 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
vid.pixelwidth = w; vid.pixelwidth = w;
vid.pixelheight = h; vid.pixelheight = h;
GL_Init(info, GLES_GetSymbol); return GL_Init(info, GLES_GetSymbol);
return true;
} }
void GLVID_SwapBuffers(void) void GLVID_SwapBuffers(void)

View file

@ -2527,7 +2527,8 @@ qboolean X11VID_Init (rendererstate_t *info, unsigned char *palette, int psl)
return false; return false;
} }
GL_Init(info, &GLX_GetSymbol); if (!GL_Init(info, &GLX_GetSymbol))
return false;
break; break;
#ifdef USE_EGL #ifdef USE_EGL
case PSL_EGL: case PSL_EGL:
@ -2537,19 +2538,26 @@ qboolean X11VID_Init (rendererstate_t *info, unsigned char *palette, int psl)
GLVID_Shutdown(); GLVID_Shutdown();
return false; return false;
} }
GL_Init(info, &EGL_Proc); if (!GL_Init(info, &EGL_Proc))
return false;
break; break;
#endif #endif
#endif #endif
#ifdef VKQUAKE #ifdef VKQUAKE
case PSL_VULKAN: case PSL_VULKAN:
#ifdef VK_USE_PLATFORM_XLIB_KHR #ifdef VK_USE_PLATFORM_XLIB_KHR
if (VK_Init(info, VK_KHR_XLIB_SURFACE_EXTENSION_NAME, XVK_SetupSurface_XLib, NULL)) {
const char *extnames[] = {VK_KHR_XLIB_SURFACE_EXTENSION_NAME, NULL};
if (VK_Init(info, extnames, XVK_SetupSurface_XLib, NULL))
break; break;
}
#endif #endif
#ifdef VK_USE_PLATFORM_XCB_KHR #ifdef VK_USE_PLATFORM_XCB_KHR
if (x11xcb_initlib() && VK_Init(info, VK_KHR_XCB_SURFACE_EXTENSION_NAME, XVK_SetupSurface_XCB, NULL)) {
const char *extnames[] = {VK_KHR_XCB_SURFACE_EXTENSION_NAME, NULL};
if (x11xcb_initlib() && VK_Init(info, extnames, XVK_SetupSurface_XCB, NULL))
break; break;
}
#endif #endif
Con_Printf("Failed to create a vulkan context.\n"); Con_Printf("Failed to create a vulkan context.\n");
GLVID_Shutdown(); GLVID_Shutdown();

View file

@ -128,7 +128,6 @@ static qboolean VID_SetFullDIBMode (rendererstate_t *info); //-1 on bpp or hz fo
#endif #endif
#ifdef WTHREAD #ifdef WTHREAD
static HANDLE windowthread; static HANDLE windowthread;
static void *windowmutex;
#endif #endif
static DEVMODE gdevmode; static DEVMODE gdevmode;
@ -821,6 +820,7 @@ static void Win32VK_Present(struct vkframe *theframe)
static qboolean Win32VK_AttachVulkan (rendererstate_t *info) static qboolean Win32VK_AttachVulkan (rendererstate_t *info)
{ //make sure we can get a valid renderer. { //make sure we can get a valid renderer.
const char *extnames[] = {VK_KHR_WIN32_SURFACE_EXTENSION_NAME, NULL};
#ifdef VK_NO_PROTOTYPES #ifdef VK_NO_PROTOTYPES
hInstVulkan = NULL; hInstVulkan = NULL;
if (!hInstVulkan) if (!hInstVulkan)
@ -835,7 +835,7 @@ static qboolean Win32VK_AttachVulkan (rendererstate_t *info)
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) GetProcAddress(hInstVulkan, "vkGetInstanceProcAddr"); vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) GetProcAddress(hInstVulkan, "vkGetInstanceProcAddr");
#endif #endif
return VK_Init(info, VK_KHR_WIN32_SURFACE_EXTENSION_NAME, Win32VK_CreateSurface, Win32VK_Present); return VK_Init(info, extnames, Win32VK_CreateSurface, Win32VK_Present);
} }
#endif #endif
@ -1481,7 +1481,8 @@ static int GLVID_SetMode (rendererstate_t *info, unsigned char *palette)
} }
} }
GL_Init(info, getglfunc); if (!GL_Init(info, getglfunc))
return false;
qSwapBuffers(maindc); qSwapBuffers(maindc);
#ifdef VKQUAKE #ifdef VKQUAKE
@ -1501,7 +1502,8 @@ static int GLVID_SetMode (rendererstate_t *info, unsigned char *palette)
stat = EGL_Init (info, palette, mainwindow, maindc); stat = EGL_Init (info, palette, mainwindow, maindc);
if (stat) if (stat)
GL_Init(info, &EGL_Proc); if (GL_Init(info, &EGL_Proc))
return false;
} }
break; break;
#endif #endif
@ -2109,6 +2111,9 @@ void GLVID_SwapBuffers (void)
#endif #endif
#ifdef VKQUAKE #ifdef VKQUAKE
case MODE_VULKAN: case MODE_VULKAN:
#ifdef USE_WGL
case MODE_NVVULKAN:
#endif
//FIXME: force a buffer swap now (might be called while loading (eg: q3), where we won't get a chance to redraw for a bit) //FIXME: force a buffer swap now (might be called while loading (eg: q3), where we won't get a chance to redraw for a bit)
break; break;
#endif #endif

View file

@ -1,12 +1,25 @@
#include "quakedef.h" #include "quakedef.h"
#include "glquake.h"
#include <SDL.h> #include <SDL.h>
#include <SDL_syswm.h> #include <SDL_syswm.h>
#ifdef GLQUAKE
#include "glquake.h"
#define OPENGL_SDL
#endif
#if SDL_MAJOR_VERSION >= 2 #if SDL_MAJOR_VERSION >= 2
#if SDL_VERSION_ATLEAST(2,0,6)
#ifdef VKQUAKE
#include <SDL_vulkan.h>
#include "../vk/vkrenderer.h"
#define VULKAN_SDL
#endif
#endif
SDL_Window *sdlwindow; SDL_Window *sdlwindow;
#ifdef OPENGL_SDL
static SDL_GLContext *sdlcontext; static SDL_GLContext *sdlcontext;
#endif
#else #else
SDL_Surface *sdlsurf; SDL_Surface *sdlsurf;
#endif #endif
@ -34,7 +47,7 @@ unsigned short intitialgammaramps[3][256];
qboolean mouseactive; qboolean mouseactive;
extern qboolean mouseusedforgui; extern qboolean mouseusedforgui;
#ifdef OPENGL_SDL
static void *GLVID_getsdlglfunction(char *functionname) static void *GLVID_getsdlglfunction(char *functionname)
{ {
#ifdef GL_STATIC #ifdef GL_STATIC
@ -44,6 +57,7 @@ static void *GLVID_getsdlglfunction(char *functionname)
return SDL_GL_GetProcAddress(functionname); return SDL_GL_GetProcAddress(functionname);
#endif #endif
} }
#endif
#if SDL_MAJOR_VERSION >= 2 #if SDL_MAJOR_VERSION >= 2
void *GLVID_CreateCursor (const char *filename, float hotx, float hoty, float scale) void *GLVID_CreateCursor (const char *filename, float hotx, float hoty, float scale)
@ -101,7 +115,7 @@ void GLVID_DestroyCursor (void *cursor)
#endif #endif
qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette) static qboolean SDLVID_Init (rendererstate_t *info, unsigned char *palette, r_qrenderer_t qrenderer)
{ {
int flags = 0; int flags = 0;
@ -110,6 +124,9 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
SDL_SetVideoMode(0, 0, 0, 0); //to get around some SDL bugs SDL_SetVideoMode(0, 0, 0, 0); //to get around some SDL bugs
#endif #endif
#ifdef OPENGL_SDL
if (qrenderer == QR_OPENGL)
{
#if SDL_MAJOR_VERSION >= 2 #if SDL_MAJOR_VERSION >= 2
SDL_GL_LoadLibrary(NULL); SDL_GL_LoadLibrary(NULL);
#endif #endif
@ -171,11 +188,27 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, info->multisample); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, info->multisample);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
} }
}
#endif
#if SDL_MAJOR_VERSION >= 2 #if SDL_MAJOR_VERSION >= 2
switch(qrenderer)
{
default:
break;
#ifdef OPENGL_SDL
case QR_OPENGL:
flags |= SDL_WINDOW_OPENGL;
break;
#endif
#ifdef VULKAN_SDL
case QR_VULKAN:
flags |= SDL_WINDOW_VULKAN;
break;
#endif
}
if (info->fullscreen) if (info->fullscreen)
flags |= SDL_WINDOW_FULLSCREEN; flags |= SDL_WINDOW_FULLSCREEN;
flags |= SDL_WINDOW_OPENGL;
flags |= SDL_WINDOW_RESIZABLE; flags |= SDL_WINDOW_RESIZABLE;
flags |= SDL_WINDOW_INPUT_GRABBED; flags |= SDL_WINDOW_INPUT_GRABBED;
flags |= SDL_WINDOW_SHOWN; flags |= SDL_WINDOW_SHOWN;
@ -189,18 +222,37 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
return false; return false;
} }
CL_UpdateWindowTitle(); CL_UpdateWindowTitle();
#if SDL_PATCHLEVEL >= 1
SDL_GL_GetDrawableSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight); //get the proper physical size.
#else
SDL_GetWindowSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight);
#endif
switch(qrenderer)
{
#ifdef OPENGL_SDL
#if SDL_PATCHLEVEL >= 1
case QR_OPENGL:
SDL_GL_GetDrawableSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight); //get the proper physical size.
break;
#endif
#endif
#ifdef VULKAN_SDL
case QR_VULKAN:
SDL_Vulkan_GetDrawableSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight);
break;
#endif
default:
SDL_GetWindowSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight);
break;
}
#ifdef OPENGL_SDL
if (qrenderer == QR_OPENGL)
{
sdlcontext = SDL_GL_CreateContext(sdlwindow); sdlcontext = SDL_GL_CreateContext(sdlwindow);
if (!sdlcontext) if (!sdlcontext)
{ {
Con_Printf("Couldn't initialize GL context: %s\n", SDL_GetError()); Con_Printf("Couldn't initialize GL context: %s\n", SDL_GetError());
return false; return false;
} }
}
#endif
{ {
SDL_Surface *iconsurf; SDL_Surface *iconsurf;
@ -230,10 +282,6 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
#endif #endif
vid.activeapp = true; vid.activeapp = true;
GL_Init(info, GLVID_getsdlglfunction);
qglViewport (0, 0, vid.pixelwidth, vid.pixelheight);
mouseactive = false; mouseactive = false;
if (vid_isfullscreen) if (vid_isfullscreen)
IN_ActivateMouse(); IN_ActivateMouse();
@ -272,6 +320,17 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
return true; return true;
} }
#ifdef OPENGL_SDL
qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
{
if (SDLVID_Init(info, palette, QR_OPENGL))
{
return GL_Init(info, GLVID_getsdlglfunction);
}
return false;
}
#endif
void GLVID_DeInit (void) void GLVID_DeInit (void)
{ {
vid.activeapp = false; vid.activeapp = false;
@ -281,7 +340,21 @@ void GLVID_DeInit (void)
#if SDL_MAJOR_VERSION >= 2 #if SDL_MAJOR_VERSION >= 2
SDL_SetWindowGammaRamp(sdlwindow, NULL, NULL, NULL); SDL_SetWindowGammaRamp(sdlwindow, NULL, NULL, NULL);
switch(qrenderer)
{
#ifdef OPENGL_SDL
case QR_OPENGL:
SDL_GL_DeleteContext(sdlcontext); SDL_GL_DeleteContext(sdlcontext);
break;
#endif
#ifdef VULKAN_SDL
case QR_VULKAN:
break;
#endif
default:
break;
}
SDL_DestroyWindow(sdlwindow); SDL_DestroyWindow(sdlwindow);
sdlwindow = NULL; sdlwindow = NULL;
#else #else
@ -294,6 +367,10 @@ void GLVID_DeInit (void)
void GLVID_SwapBuffers (void) void GLVID_SwapBuffers (void)
{ {
switch(qrenderer)
{
#ifdef OPENGL_SDL
case QR_OPENGL:
#if SDL_MAJOR_VERSION >= 2 #if SDL_MAJOR_VERSION >= 2
if (vid_vsync.modified) if (vid_vsync.modified)
{ {
@ -310,6 +387,11 @@ void GLVID_SwapBuffers (void)
#else #else
SDL_GL_SwapBuffers(); SDL_GL_SwapBuffers();
#endif #endif
break;
#endif
default:
break;
}
if (!vid_isfullscreen) if (!vid_isfullscreen)
@ -381,6 +463,7 @@ qboolean GLVID_ApplyGammaRamps (unsigned int gammarampsize, unsigned short *ramp
return true; return true;
} }
#endif #endif
return false;
} }
void GLVID_SetCaption(const char *text) void GLVID_SetCaption(const char *text)
@ -394,3 +477,87 @@ void GLVID_SetCaption(const char *text)
#ifdef VULKAN_SDL
static qboolean VKSDL_CreateSurface(void)
{
return SDL_Vulkan_CreateSurface(sdlwindow, vk.instance, &vk.surface);
}
static qboolean VKVID_Init (rendererstate_t *info, unsigned char *palette)
{
unsigned extcount;
const char **extnames;
if (!SDLVID_Init(info, palette, QR_VULKAN))
return false;
if (!SDL_Vulkan_GetInstanceExtensions(sdlwindow, &extcount, NULL))
return false;
extnames = alloca(sizeof(*extnames)*(extcount+1));
if (!SDL_Vulkan_GetInstanceExtensions(sdlwindow, &extcount, extnames))
return false;
vkGetInstanceProcAddr = SDL_Vulkan_GetVkGetInstanceProcAddr();
if (!VK_Init(info, extnames, VKSDL_CreateSurface, NULL))
{
SDL_ShowSimpleMessageBox(0, "FTEQuake", extnames[1], sdlwindow);
return false;
}
return true;
}
rendererinfo_t vkrendererinfo =
{
"Vulkan-SDL",
{
"vk",
"Vulkan"
},
QR_VULKAN,
VK_Draw_Init,
VK_Draw_Shutdown,
VK_UpdateFiltering,
VK_LoadTextureMips,
VK_DestroyTexture,
VK_R_Init,
VK_R_DeInit,
VK_R_RenderView,
VKVID_Init,
GLVID_DeInit,
GLVID_SwapBuffers,
GLVID_ApplyGammaRamps,
NULL,
NULL,
NULL,
GLVID_SetCaption,
VKVID_GetRGBInfo,
VK_SCR_UpdateScreen,
VKBE_SelectMode,
VKBE_DrawMesh_List,
VKBE_DrawMesh_Single,
VKBE_SubmitBatch,
VKBE_GetTempBatch,
VKBE_DrawWorld,
VKBE_Init,
VKBE_GenBrushModelVBO,
VKBE_ClearVBO,
VKBE_UploadAllLightmaps,
VKBE_SelectEntity,
VKBE_SelectDLight,
VKBE_Scissor,
VKBE_LightCullModel,
VKBE_VBO_Begin,
VKBE_VBO_Data,
VKBE_VBO_Finish,
VKBE_VBO_Destroy,
VKBE_RenderToTextureUpdate2d,
"no more"
};
#else
rendererinfo_t vkrendererinfo;
#endif

View file

@ -713,6 +713,13 @@ extern void (APIENTRY *qglBufferSubDataARB)(GLenum target, GLint offset, GLsizei
extern void *(APIENTRY *qglMapBufferARB)(GLenum target, GLenum access); extern void *(APIENTRY *qglMapBufferARB)(GLenum target, GLenum access);
extern GLboolean (APIENTRY *qglUnmapBufferARB)(GLenum target); extern GLboolean (APIENTRY *qglUnmapBufferARB)(GLenum target);
#define GLintptr qintptr_t
#define GLsizeiptr quintptr_t
#ifndef GL_MAP_READ_BIT
#define GL_MAP_READ_BIT 1
#endif
void *(APIENTRY *qglMapBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
#endif #endif
extern void (APIENTRY *qglGenQueriesARB)(GLsizei n, GLuint *ids); extern void (APIENTRY *qglGenQueriesARB)(GLsizei n, GLuint *ids);
extern void (APIENTRY *qglDeleteQueriesARB)(GLsizei n, const GLuint *ids); extern void (APIENTRY *qglDeleteQueriesARB)(GLsizei n, const GLuint *ids);
@ -1078,7 +1085,7 @@ void GL_SelectProgram(int program);
void GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name)); qboolean GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name));
#endif #endif

View file

@ -21,11 +21,11 @@ static void PPAPI_audio_callback(void *sample_buffer, uint32_t len,
if (sc) if (sc)
{ {
int curtime = S_GetMixerTime(sc); int curtime = S_GetMixerTime(sc);
framesz = sc->sn.numchannels * sc->sn.samplebits/8; framesz = sc->sn.numchannels * sc->sn.samplebytes;
//might as well dump it directly... //might as well dump it directly...
sc->sn.buffer = sample_buffer; sc->sn.buffer = sample_buffer;
sc->sn.samples = len / (sc->sn.samplebits/8); sc->sn.samples = len / sc->sn.samplebytes;
S_PaintChannels (sc, curtime + (len / framesz)); S_PaintChannels (sc, curtime + (len / framesz));
sc->sn.samples = 0; sc->sn.samples = 0;
sc->sn.buffer = NULL; sc->sn.buffer = NULL;
@ -42,7 +42,7 @@ static void PPAPI_Shutdown(soundcardinfo_t *sc)
static unsigned int PPAPI_GetDMAPos(soundcardinfo_t *sc) static unsigned int PPAPI_GetDMAPos(soundcardinfo_t *sc)
{ {
sc->sn.samplepos = sc->snd_sent / (sc->sn.samplebits/8); sc->sn.samplepos = sc->snd_sent / sc->sn.samplebytes;
return sc->sn.samplepos; return sc->sn.samplepos;
} }
@ -60,22 +60,23 @@ static void PPAPI_Submit(soundcardinfo_t *sc, int start, int end)
{ {
} }
int PPAPI_InitCard (soundcardinfo_t *sc, int cardnum) static qboolean PPAPI_InitCard (soundcardinfo_t *sc, const char *cardname)
{ {
PP_Resource config; PP_Resource config;
int framecount; int framecount;
/*I'm not aware of any limits on the number of 'devices' we can create, but virtual devices on the same physical device are utterly pointless, so don't load more than one*/ /*I'm not aware of any limits on the number of 'devices' we can create, but virtual devices on the same physical device are utterly pointless, so don't load more than one*/
if (cardnum != 0) if (cardname && *cardname)
return 2; return false; //only use the default device
/*the docs say only two sample rates are allowed*/ /*the docs say only two sample rates are allowed*/
if (sc->sn.speed <= 44100) if (sc->sn.speed <= 44100)
sc->sn.speed = 44100; sc->sn.speed = 44100;
else else
sc->sn.speed = 48000; sc->sn.speed = 48000;
/*we can't choose these two settings*/ /*we can't choose these settings at all*/
sc->sn.samplebits = 16; sc->sn.samplebytes = 2;
sc->sn.sampleformat = QSF_S16;
sc->sn.numchannels = 2; sc->sn.numchannels = 2;
#ifdef PPB_AUDIO_CONFIG_INTERFACE_1_1 #ifdef PPB_AUDIO_CONFIG_INTERFACE_1_1
@ -107,10 +108,15 @@ int PPAPI_InitCard (soundcardinfo_t *sc, int cardnum)
if (sc->handle) if (sc->handle)
{ {
if (audio_interface->StartPlayback((PP_Resource)sc->handle)) if (audio_interface->StartPlayback((PP_Resource)sc->handle))
return 1; return true;
} }
} }
return 0; return false;
} }
int (*pPPAPI_InitCard) (soundcardinfo_t *sc, int cardnum) = &PPAPI_InitCard; sounddriver_t PPAPI_AudioOutput =
{
"Pepper",
PPAPI_InitCard,
NULL
};

View file

@ -1555,7 +1555,6 @@ static int PR_NoDebugVM(progfuncs_t *fte_restrict progfuncs)
ofs = strlen(stack); ofs = strlen(stack);
PR_SaveCallStack (progfuncs, stack, &ofs, sizeof(stack)); PR_SaveCallStack (progfuncs, stack, &ofs, sizeof(stack));
PR_RunError (&progfuncs->funcs, stack); PR_RunError (&progfuncs->funcs, stack);
free(stack);
return -1; return -1;
} }
#endif #endif

View file

@ -512,7 +512,7 @@ typedef struct
pbool used:1; pbool used:1;
pbool evil:1; pbool evil:1;
pbool varg:1; pbool varg:1;
char *fromfile; const char *fromfile;
int fromline; int fromline;
int namelen; int namelen;

View file

@ -14244,13 +14244,13 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal)
/*aliasof =*/ QCC_PR_ParseName(); /*aliasof =*/ QCC_PR_ParseName();
else if (pr_token_type == tt_immediate) else if (pr_token_type == tt_immediate)
{ {
/*aliasof =*/ pr_immediate_string; //aliasof = copy(pr_immediate_string);
QCC_PR_Lex(); QCC_PR_Lex();
} }
QCC_PR_Expect(")"); QCC_PR_Expect(")");
} }
else if (QCC_PR_CheckName("accumulate")) else if (QCC_PR_CheckName("accumulate"))
doweak = dowrap = true; doweak = dowrap = true; //FIXME: should instead append to the previous function, I think, which requires not finishing it properly until later, or something.
else else
{ {
QCC_PR_ParseWarning(0, "Unknown attribute \"%s\"", pr_token); QCC_PR_ParseWarning(0, "Unknown attribute \"%s\"", pr_token);

View file

@ -2931,7 +2931,7 @@ so if present, the preceeding \\\n and following \\\n must become an actual \n i
cnst->evil = true; cnst->evil = true;
preprocessorhack = true; preprocessorhack = true;
} }
else// if (preprocessorhack) else if (preprocessorhack)
{ {
*d++ = '\n'; *d++ = '\n';
preprocessorhack = false; preprocessorhack = false;

View file

@ -4589,14 +4589,18 @@ void (PNGAPI *qpng_read_end) PNGARG((png_structp png_ptr, png_infop info_ptr)) P
void (PNGAPI *qpng_read_image) PNGARG((png_structp png_ptr, png_bytepp image)) PSTATIC(png_read_image); void (PNGAPI *qpng_read_image) PNGARG((png_structp png_ptr, png_bytepp image)) PSTATIC(png_read_image);
png_byte (PNGAPI *qpng_get_bit_depth) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_bit_depth); png_byte (PNGAPI *qpng_get_bit_depth) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_bit_depth);
png_byte (PNGAPI *qpng_get_channels) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_channels); png_byte (PNGAPI *qpng_get_channels) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_channels);
#if PNG_LIBPNG_VER < 10400
png_uint_32 (PNGAPI *qpng_get_rowbytes) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_rowbytes);
#else
png_size_t (PNGAPI *qpng_get_rowbytes) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_rowbytes); png_size_t (PNGAPI *qpng_get_rowbytes) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_rowbytes);
#endif
void (PNGAPI *qpng_read_update_info) PNGARG((png_structp png_ptr, png_infop info_ptr)) PSTATIC(png_read_update_info); void (PNGAPI *qpng_read_update_info) PNGARG((png_structp png_ptr, png_infop info_ptr)) PSTATIC(png_read_update_info);
void (PNGAPI *qpng_set_strip_16) PNGARG((png_structp png_ptr)) PSTATIC(png_set_strip_16); void (PNGAPI *qpng_set_strip_16) PNGARG((png_structp png_ptr)) PSTATIC(png_set_strip_16);
void (PNGAPI *qpng_set_expand) PNGARG((png_structp png_ptr)) PSTATIC(png_set_expand); void (PNGAPI *qpng_set_expand) PNGARG((png_structp png_ptr)) PSTATIC(png_set_expand);
void (PNGAPI *qpng_set_gray_to_rgb) PNGARG((png_structp png_ptr)) PSTATIC(png_set_gray_to_rgb); void (PNGAPI *qpng_set_gray_to_rgb) PNGARG((png_structp png_ptr)) PSTATIC(png_set_gray_to_rgb);
void (PNGAPI *qpng_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)) PSTATIC(png_set_tRNS_to_alpha); void (PNGAPI *qpng_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)) PSTATIC(png_set_tRNS_to_alpha);
png_uint_32 (PNGAPI *qpng_get_valid) PNGARG((png_const_structp png_ptr, png_const_infop info_ptr, png_uint_32 flag)) PSTATIC(png_get_valid); png_uint_32 (PNGAPI *qpng_get_valid) PNGARG((png_const_structp png_ptr, png_const_infop info_ptr, png_uint_32 flag)) PSTATIC(png_get_valid);
#if PNG_LIBPNG_VER > 10400 #if PNG_LIBPNG_VER >= 10400
void (PNGAPI *qpng_set_expand_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)) PSTATIC(png_set_expand_gray_1_2_4_to_8); void (PNGAPI *qpng_set_expand_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)) PSTATIC(png_set_expand_gray_1_2_4_to_8);
#else #else
void (PNGAPI *qpng_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)) PSTATIC(png_set_gray_1_2_4_to_8); void (PNGAPI *qpng_set_gray_1_2_4_to_8) PNGARG((png_structp png_ptr)) PSTATIC(png_set_gray_1_2_4_to_8);

View file

@ -6894,12 +6894,21 @@ void log(string name, float console, string text)
static void QCBUILTIN PF_logtext(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) static void QCBUILTIN PF_logtext(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals)
{ {
char name[MAX_OSPATH]; char name[MAX_OSPATH];
const char *text; const char *otext, *text;
vfsfile_t *file; vfsfile_t *file;
char unitext[8192], *out = unitext;
int err;
snprintf(name, MAX_OSPATH, "%s.log", PR_GetStringOfs(prinst, OFS_PARM0)); snprintf(name, MAX_OSPATH, "%s.log", PR_GetStringOfs(prinst, OFS_PARM0));
text = PF_VarString(prinst, 2, pr_globals); otext = text = PF_VarString(prinst, 2, pr_globals);
PR_CleanText(text); while (*text)
{
int cp = unicode_decode(&err, text, (char**)&text, false);
if ((cp >= 0xe000 && cp < 0xe100) || cp == '\r')
cp = readable2[cp&0xff]; //dequake it
out += utf8_encode(out, cp, sizeof(unitext)-1-(out-unitext));
}
*out++ = 0;
file = FS_OpenVFS(name, "ab", FS_GAME); file = FS_OpenVFS(name, "ab", FS_GAME);
if (file == NULL) if (file == NULL)
@ -6908,12 +6917,12 @@ static void QCBUILTIN PF_logtext(pubprogfuncs_t *prinst, struct globalvars_s *pr
} }
else else
{ {
VFS_WRITE(file, text, strlen(text)); VFS_WRITE(file, unitext, out-unitext);
VFS_CLOSE (file); VFS_CLOSE (file);
} }
if (G_FLOAT(OFS_PARM1)) if (G_FLOAT(OFS_PARM1))
Con_Printf("%s", text); Con_Printf("%s", otext);
} }
#endif #endif
@ -10654,10 +10663,10 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"strreplace", PF_strreplace, 0, 0, 0, 484, "string(string search, string replace, string subject)"},//DP_QC_STRREPLACE {"strreplace", PF_strreplace, 0, 0, 0, 484, "string(string search, string replace, string subject)"},//DP_QC_STRREPLACE
{"strireplace", PF_strireplace, 0, 0, 0, 485, "string(string search, string replace, string subject)"},//DP_QC_STRREPLACE {"strireplace", PF_strireplace, 0, 0, 0, 485, "string(string search, string replace, string subject)"},//DP_QC_STRREPLACE
{"getsurfacepointattribute",PF_getsurfacepointattribute,0,0,0, 486, "vector(entity e, float s, float n, float a)"},//DP_QC_GETSURFACEPOINTATTRIBUTE {"getsurfacepointattribute",PF_getsurfacepointattribute,0,0,0, 486, "vector(entity e, float s, float n, float a)"},//DP_QC_GETSURFACEPOINTATTRIBUTE
{"gecko_create", PF_Fixme, 0, 0, 0, 487, D("float(string name)", "Create a new 'browser tab' shader with the specified name that can then be drawn via drawpic (shader should not already exist - including from map/model textures or disk). In order to function correctly, this builtin depends upon external plugins being available. Use gecko_navigate to navigate it to a page of your choosing.")},//DP_GECKO_SUPPORT {"gecko_create", PF_Fixme, 0, 0, 0, 487, D("float(string name, optional string initialURI)", "Create a new 'browser tab' shader with the specified name that can then be drawn via drawpic (shader should not already exist - including from map/model textures or disk). In order to function correctly, this builtin depends upon external plugins being available. Use gecko_navigate to navigate it to a page of your choosing.")},//DP_GECKO_SUPPORT
{"gecko_destroy", PF_Fixme, 0, 0, 0, 488, D("void(string name)", "Destroy a shader.")},//DP_GECKO_SUPPORT {"gecko_destroy", PF_Fixme, 0, 0, 0, 488, D("void(string name)", "Destroy a shader.")},//DP_GECKO_SUPPORT
{"gecko_navigate", PF_Fixme, 0, 0, 0, 489, D("void(string name, string URI)", "Sends a command to the media decoder attached to the specified shader. In the case of a browser decoder, this changes the url that the browser displays. 'cmd:[un]focus' will tell the decoder that it has focus.")},//DP_GECKO_SUPPORT {"gecko_navigate", PF_Fixme, 0, 0, 0, 489, D("void(string name, string URI)", "Sends a command to the media decoder attached to the specified shader. In the case of a browser decoder, this changes the url that the browser displays. 'cmd:[un]focus' will tell the decoder that it has focus.")},//DP_GECKO_SUPPORT
{"gecko_keyevent", PF_Fixme, 0, 0, 0, 490, D("float(string name, float key, float eventtype)", "Send a key event to a media decoder. This applies only to interactive decoders like browsers.")},//DP_GECKO_SUPPORT {"gecko_keyevent", PF_Fixme, 0, 0, 0, 490, D("float(string name, float key, float eventtype, optional float charcode)", "Send a key event to a media decoder. This applies only to interactive decoders like browsers.")},//DP_GECKO_SUPPORT
{"gecko_mousemove", PF_Fixme, 0, 0, 0, 491, D("void(string name, float x, float y)", "Sets a media decoder shader's mouse position. Values should be 0-1.")},//DP_GECKO_SUPPORT {"gecko_mousemove", PF_Fixme, 0, 0, 0, 491, D("void(string name, float x, float y)", "Sets a media decoder shader's mouse position. Values should be 0-1.")},//DP_GECKO_SUPPORT
{"gecko_resize", PF_Fixme, 0, 0, 0, 492, D("void(string name, float w, float h)", "Request to resize a media decoder.")},//DP_GECKO_SUPPORT {"gecko_resize", PF_Fixme, 0, 0, 0, 492, D("void(string name, float w, float h)", "Request to resize a media decoder.")},//DP_GECKO_SUPPORT
{"gecko_get_texture_extent",PF_Fixme, 0, 0, 0, 493, D("vector(string name)", "Retrieves a media decoder current image pixel sizes.")},//DP_GECKO_SUPPORT {"gecko_get_texture_extent",PF_Fixme, 0, 0, 0, 493, D("vector(string name)", "Retrieves a media decoder current image pixel sizes.")},//DP_GECKO_SUPPORT
@ -10691,7 +10700,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs
{"uri_unescape", PF_uri_unescape, 0, 0, 0, 511, "string(string in)"},//DP_QC_URI_ESCAPE {"uri_unescape", PF_uri_unescape, 0, 0, 0, 511, "string(string in)"},//DP_QC_URI_ESCAPE
{"num_for_edict", PF_num_for_edict, 0, 0, 0, 512, "float(entity ent)"},//DP_QC_NUM_FOR_EDICT {"num_for_edict", PF_num_for_edict, 0, 0, 0, 512, "float(entity ent)"},//DP_QC_NUM_FOR_EDICT
{"uri_get", PF_uri_get, 0, 0, 0, 513, D("#define uri_post uri_get\nfloat(string uril, float id, optional string postmimetype, optional string postdata)", "uri_get() gets content from an URL and calls a callback \"uri_get_callback\" with it set as string; an unique ID of the transfer is returned\nreturns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string\nFor a POST request, you will typically want the postmimetype set to application/x-www-form-urlencoded.\nFor a GET request, omit the mime+data entirely.\nConsult your webserver/php/etc documentation for best-practise.")},//DP_QC_URI_GET {"uri_get", PF_uri_get, 0, 0, 0, 513, D("#define uri_post uri_get\nfloat(string uril, float id, optional string postmimetype, optional string postdata)", "uri_get() gets content from an URL and calls a callback \"uri_get_callback\" with it set as string; an unique ID of the transfer is returned\nreturns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string\nFor a POST request, you will typically want the postmimetype set to application/x-www-form-urlencoded.\nFor a GET request, omit the mime+data entirely.\nConsult your webserver/php/etc documentation for best-practise.")},//DP_QC_URI_GET
{"uri_post", PF_uri_get, 0, 0, 0, 513, D("float(string uril, float id, optional string postmimetype, optional string postdata)", "uri_get() gets content from an URL and calls a callback \"uri_get_callback\" with it set as string; an unique ID of the transfer is returned\nreturns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string"), true},//DP_QC_URI_POST {"uri_post", PF_uri_get, 0, 0, 0, 513, D("float(string uril, float id, optional string postmimetype, optional string postdata, optional float strbuf)", "uri_get() gets content from an URL and calls a callback \"uri_get_callback\" with it set as string; an unique ID of the transfer is returned\nreturns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string"), true},//DP_QC_URI_POST
{"tokenize_console",PF_tokenize_console,0, 0, 0, 514, D("float(string str)", "Tokenize a string exactly as the console's tokenizer would do so. The regular tokenize builtin became bastardized for convienient string parsing, which resulted in a large disparity that can be exploited to bypass checks implemented in a naive SV_ParseClientCommand function, therefore you can use this builtin to make sure it exactly matches.")}, {"tokenize_console",PF_tokenize_console,0, 0, 0, 514, D("float(string str)", "Tokenize a string exactly as the console's tokenizer would do so. The regular tokenize builtin became bastardized for convienient string parsing, which resulted in a large disparity that can be exploited to bypass checks implemented in a naive SV_ParseClientCommand function, therefore you can use this builtin to make sure it exactly matches.")},
{"argv_start_index",PF_argv_start_index,0, 0, 0, 515, D("float(float idx)", "Returns the character index that the tokenized arg started at.")}, {"argv_start_index",PF_argv_start_index,0, 0, 0, 515, D("float(float idx)", "Returns the character index that the tokenized arg started at.")},
{"argv_end_index", PF_argv_end_index, 0, 0, 0, 516, D("float(float idx)", "Returns the character index that the tokenized arg stopped at.")}, {"argv_end_index", PF_argv_end_index, 0, 0, 0, 516, D("float(float idx)", "Returns the character index that the tokenized arg stopped at.")},
@ -11539,6 +11548,8 @@ void PR_DumpPlatform_f(void)
{"CSQC_Parse_TempEntity", "float()", CS, "Please don't use this. Use CSQC_Parse_Event and multicasts instead."}, {"CSQC_Parse_TempEntity", "float()", CS, "Please don't use this. Use CSQC_Parse_Event and multicasts instead."},
{"GameCommand", "void(string cmdtext)", CS|MENU}, {"GameCommand", "void(string cmdtext)", CS|MENU},
{"Cef_GeneratePage", "string(string uri, string method, string postdata, __in string requestheaders, __inout string responseheaders)", QW|NQ|CS|MENU, "Provides an entrypoint to generate pages for the CEF plugin from within QC. Headers are \n-separated key/value pairs (use tokenizebyseparator)."},
// {"HTTP_GeneratePage", "string(string uri, string method, string postdata, __in string requestheaders, __inout string responseheaders)", QW|NQ, "Provides an entrypoint to generate pages for pages requested over http (sv_listen_tcp+net_enable_http). Headers are \n-separated key/value pairs (use tokenizebyseparator)."},
{"init", "void(float prevprogs)", QW|NQ|CS, "Part of FTE_MULTIPROGS. Called as soon as a progs is loaded, called at a time when entities are not valid. This is the only time when it is safe to call addprogs without field assignment. As it is also called as part of addprogs, this also gives you a chance to hook functions in modules that are already loaded (via externget+externget)."}, {"init", "void(float prevprogs)", QW|NQ|CS, "Part of FTE_MULTIPROGS. Called as soon as a progs is loaded, called at a time when entities are not valid. This is the only time when it is safe to call addprogs without field assignment. As it is also called as part of addprogs, this also gives you a chance to hook functions in modules that are already loaded (via externget+externget)."},
{"initents", "void()", QW|NQ|CS, "Part of FTE_MULTIPROGS. Called after fields have been finalized. This is the first point at which it is safe to call spawn(), and is called before any entity fields have been parsed. You can use this entrypoint to send notifications to other modules."}, {"initents", "void()", QW|NQ|CS, "Part of FTE_MULTIPROGS. Called after fields have been finalized. This is the first point at which it is safe to call spawn(), and is called before any entity fields have been parsed. You can use this entrypoint to send notifications to other modules."},

View file

@ -1808,7 +1808,10 @@ static void SV_Status_f (void)
float pi, po, bi, bo; float pi, po, bi, bo;
int columns = 80; int columns = 80;
extern cvar_t sv_listen_qw, sv_listen_nq, sv_listen_dp; extern cvar_t sv_listen_qw;
#ifdef NQPROT
extern cvar_t sv_listen_nq, sv_listen_dp;
#endif
#ifdef QWOVERQ3 #ifdef QWOVERQ3
extern cvar_t sv_listen_q3; extern cvar_t sv_listen_q3;
#endif #endif
@ -1874,8 +1877,8 @@ static void SV_Status_f (void)
if (sv_listen_q3.ival) Con_Printf(" Q3"); if (sv_listen_q3.ival) Con_Printf(" Q3");
#endif #endif
#ifdef HAVE_DTLS #ifdef HAVE_DTLS
if (sv_listen_dtls.ival >= 2) if (sv_listen_dtls.ival >= 3)
Con_Printf(" +DTLS"); Con_Printf(" DTLS-only");
else if (sv_listen_dtls.ival) else if (sv_listen_dtls.ival)
Con_Printf(" DTLS"); Con_Printf(" DTLS");
#endif #endif

View file

@ -1933,7 +1933,6 @@ void SVDP_EmitEntitiesUpdate (client_t *client, client_frame_t *frame, packet_en
{ {
unsigned int bits; unsigned int bits;
int outno, outmax = frame->maxresend; int outno, outmax = frame->maxresend;
qboolean overflow = false;
struct resendinfo_s *resend = frame->resend; struct resendinfo_s *resend = frame->resend;
MSG_WriteByte(msg, svcdp_entities); MSG_WriteByte(msg, svcdp_entities);
@ -1950,10 +1949,7 @@ void SVDP_EmitEntitiesUpdate (client_t *client, client_frame_t *frame, packet_en
if (!bits) if (!bits)
continue; continue;
if (msg->cursize + 50 > msg->maxsize) if (msg->cursize + 50 > msg->maxsize)
{
overflow = true;
break; /*give up if it gets full. FIXME: bone data is HUGE.*/ break; /*give up if it gets full. FIXME: bone data is HUGE.*/
}
if (outno >= outmax) if (outno >= outmax)
{ //expand the frames. may need some copying... { //expand the frames. may need some copying...
SV_ExpandNackFrames(client, outno+1); SV_ExpandNackFrames(client, outno+1);

View file

@ -115,6 +115,7 @@ cvar_t allow_download_other = CVARD("allow_download_other", "0", "0 blocks down
extern cvar_t sv_allow_splitscreen; extern cvar_t sv_allow_splitscreen;
cvar_t sv_guidhash = CVARD("sv_guidkey", "", "If set, clients will calculate their GUID values against this string instead of the server's IP address. This allows consistency between multiple servers (for stats tracking), but do NOT treat the client's GUID as something that is secure.");
cvar_t sv_serverip = CVARD("sv_serverip", "", "Set this cvar to the server's public ip address if the server is behind a firewall and cannot detect its own public address. Providing a port is required if the firewall/nat remaps it, but is otherwise optional."); cvar_t sv_serverip = CVARD("sv_serverip", "", "Set this cvar to the server's public ip address if the server is behind a firewall and cannot detect its own public address. Providing a port is required if the firewall/nat remaps it, but is otherwise optional.");
cvar_t sv_public = CVAR("sv_public", "0"); cvar_t sv_public = CVAR("sv_public", "0");
cvar_t sv_listen_qw = CVARAFD("sv_listen_qw", "1", "sv_listen", 0, "Specifies whether normal clients are allowed to connect."); cvar_t sv_listen_qw = CVARAFD("sv_listen_qw", "1", "sv_listen", 0, "Specifies whether normal clients are allowed to connect.");
@ -124,7 +125,7 @@ cvar_t sv_listen_dp = CVARD("sv_listen_dp", "0", "Allows the server to respond
cvar_t sv_listen_q3 = CVAR("sv_listen_q3", "0"); cvar_t sv_listen_q3 = CVAR("sv_listen_q3", "0");
#endif #endif
#ifdef HAVE_DTLS #ifdef HAVE_DTLS
cvar_t sv_listen_dtls = CVARCD("net_enable_dtls", "", SV_Listen_Dtls_Changed, "Controls serverside dtls support.\n0: dtls blocked, not advertised.\n1: available in desired.\n2: used where possible.\n3: disallow non-dtls clients (sv_port_tcp should be eg tls://[::]:27500 to also disallow unencrypted tcp connections)."); cvar_t sv_listen_dtls = CVARCD("net_enable_dtls", "", SV_Listen_Dtls_Changed, "Controls serverside dtls support.\n0: dtls blocked, not advertised.\n1: available in desired.\n2: used where possible (recommended setting).\n3: disallow non-dtls clients (sv_port_tcp should be eg tls://[::]:27500 to also disallow unencrypted tcp connections).");
#endif #endif
cvar_t sv_reportheartbeats = CVAR("sv_reportheartbeats", "1"); cvar_t sv_reportheartbeats = CVAR("sv_reportheartbeats", "1");
cvar_t sv_highchars = CVAR("sv_highchars", "1"); cvar_t sv_highchars = CVAR("sv_highchars", "1");
@ -1633,6 +1634,24 @@ qboolean SVC_GetChallenge (qboolean respond_dp)
over+=sizeof(lng); over+=sizeof(lng);
} }
#endif #endif
if (*sv_guidhash.string
#ifdef HAVE_DTLS
&& (sv_listen_dtls.ival < 3 || net_from.prot == NP_DTLS)
#endif
)
{
lng = LittleLong(PROTOCOL_VERSION_VARLENGTH);
memcpy(over, &lng, sizeof(lng));
over+=sizeof(lng);
lng = strlen(sv_guidhash.string);
memcpy(over, &lng, sizeof(lng));
over+=sizeof(lng);
lng = LittleLong(PROTOCOL_INFO_GUID);
memcpy(over, &lng, sizeof(lng));
over+=sizeof(lng);
memcpy(over, sv_guidhash.string, strlen(sv_guidhash.string));
over+=strlen(sv_guidhash.string);
}
} }
if (respond_dp) if (respond_dp)
@ -1661,6 +1680,7 @@ qboolean SVC_GetChallenge (qboolean respond_dp)
return true; return true;
} }
#ifdef SVRANKING
static void VARGS SV_OutOfBandPrintf (int q2, netadr_t *adr, char *format, ...) static void VARGS SV_OutOfBandPrintf (int q2, netadr_t *adr, char *format, ...)
{ {
va_list argptr; va_list argptr;
@ -1705,6 +1725,7 @@ static void VARGS SV_OutOfBandTPrintf (int q2, netadr_t *adr, int language, tran
Netchan_OutOfBand (NS_SERVER, adr, strlen(string), (qbyte *)string); Netchan_OutOfBand (NS_SERVER, adr, strlen(string), (qbyte *)string);
} }
#endif
qboolean SV_ChallengePasses(int challenge) qboolean SV_ChallengePasses(int challenge)
{ {
@ -1721,6 +1742,7 @@ qboolean SV_ChallengePasses(int challenge)
return false; return false;
} }
#ifdef NQPROT
//DP sends us a getchallenge followed by a CCREQ_CONNECT at about the same time. //DP sends us a getchallenge followed by a CCREQ_CONNECT at about the same time.
//this means that DP clients tend to connect as generic NQ clients. //this means that DP clients tend to connect as generic NQ clients.
//and because DP _REQUIRES_ sv_bigcoords, they tend to end up being given fitz/rmq protocols //and because DP _REQUIRES_ sv_bigcoords, they tend to end up being given fitz/rmq protocols
@ -1739,6 +1761,7 @@ static qboolean SV_ChallengeRecent(void)
} }
return false; return false;
} }
#endif
void VARGS SV_RejectMessage(int protocol, char *format, ...) void VARGS SV_RejectMessage(int protocol, char *format, ...)
{ {
@ -5154,11 +5177,11 @@ void SV_InitLocal (void)
Cvar_Register(&sv_autosave, cvargroup_servercontrol); Cvar_Register(&sv_autosave, cvargroup_servercontrol);
#endif #endif
#endif #endif
Cmd_AddCommand ("savegame_legacy", SV_LegacySavegame_f); Cmd_AddCommandAD ("savegame_legacy", SV_LegacySavegame_f, SV_Savegame_c, "Saves the game in a format compatible with vanilla Quake. Anything not supported by that format will be lost.");
Cmd_AddCommandAD ("savegame", SV_Savegame_f, SV_Savegame_c, NULL); Cmd_AddCommandAD ("savegame", SV_Savegame_f, SV_Savegame_c, "Saves the game to the named location.");
Cmd_AddCommandAD ("loadgame", SV_Loadgame_f, SV_Savegame_c, NULL); Cmd_AddCommandAD ("loadgame", SV_Loadgame_f, SV_Savegame_c, "Loads an existing saved game.");
Cmd_AddCommandAD ("save", SV_Savegame_f, SV_Savegame_c, NULL); Cmd_AddCommandAD ("save", SV_Savegame_f, SV_Savegame_c, "Saves the game to the named location.");
Cmd_AddCommandAD ("load", SV_Loadgame_f, SV_Savegame_c, NULL); Cmd_AddCommandAD ("load", SV_Loadgame_f, SV_Savegame_c, "Loads an existing saved game.");
SV_MVDInit(); SV_MVDInit();

View file

@ -2026,6 +2026,7 @@ void SV_Begin_Core(client_t *split)
} }
} }
#ifndef NOLEGACY
split->dp_ping = NULL; split->dp_ping = NULL;
split->dp_pl = NULL; split->dp_pl = NULL;
if (progstype == PROG_NQ) if (progstype == PROG_NQ)
@ -2033,6 +2034,7 @@ void SV_Begin_Core(client_t *split)
split->dp_ping = (float*)sv.world.progs->GetEdictFieldValue(sv.world.progs, sv_player, "ping", ev_float, NULL); split->dp_ping = (float*)sv.world.progs->GetEdictFieldValue(sv.world.progs, sv_player, "ping", ev_float, NULL);
split->dp_pl = (float*)sv.world.progs->GetEdictFieldValue(sv.world.progs, sv_player, "ping_packetloss", ev_float, NULL); split->dp_pl = (float*)sv.world.progs->GetEdictFieldValue(sv.world.progs, sv_player, "ping_packetloss", ev_float, NULL);
} }
#endif
} }
/* /*

View file

@ -34,6 +34,7 @@ uniform mat4 l_cubematrix;
#endif #endif
uniform sampler2DShadow s_shadowmap; uniform sampler2DShadow s_shadowmap;
//FIXME: shadowmaps need to be atlased!
uniform vec4 l_shadowmapproj; //light projection matrix info uniform vec4 l_shadowmapproj; //light projection matrix info
uniform vec2 l_shadowmapscale; //xy are the texture scale, z is 1, w is the scale. uniform vec2 l_shadowmapscale; //xy are the texture scale, z is 1, w is the scale.
vec3 ShadowmapCoord(vec4 cubeproj) vec3 ShadowmapCoord(vec4 cubeproj)
@ -179,6 +180,9 @@ void main ()
float nDotL = dot(norm, lightDir); float nDotL = dot(norm, lightDir);
float lightDiffuse = max(0.0, nDotL) * atten; float lightDiffuse = max(0.0, nDotL) * atten;
/*calc specular term*/
//fixme
//fixme: apply fog //fixme: apply fog
//fixme: output a specular term //fixme: output a specular term
//fixme: cubemap filters //fixme: cubemap filters

View file

@ -1143,8 +1143,9 @@ qboolean VK_LoadBlob(program_t *prog, void *blobdata, const char *name)
prog->pipelines = NULL; //generated as needed, depending on blend states etc. prog->pipelines = NULL; //generated as needed, depending on blend states etc.
return true; return true;
} }
static void VKBE_ReallyDeleteProg(program_t *prog) static void VKBE_ReallyDeleteProg(void *vprog)
{ //nothing else is refering to this data any more, its safe to obliterate it. { //nothing else is refering to this data any more, its safe to obliterate it.
program_t *prog = vprog;
struct pipeline_s *pipe; struct pipeline_s *pipe;
while(prog->pipelines) while(prog->pipelines)
{ {
@ -5030,7 +5031,7 @@ static qboolean BE_GenerateRefraction(batch_t *batch, shader_t *bs)
extern cvar_t r_refractreflect_scale; extern cvar_t r_refractreflect_scale;
float oldil; float oldil;
int oldbem; int oldbem;
struct vk_rendertarg *targ; // struct vk_rendertarg *targ;
//these flags require rendering some view as an fbo //these flags require rendering some view as an fbo
if (r_refdef.recurse) if (r_refdef.recurse)
return false; return false;
@ -5042,7 +5043,7 @@ static qboolean BE_GenerateRefraction(batch_t *batch, shader_t *bs)
return false; //multisample rendering can't deal with this. return false; //multisample rendering can't deal with this.
oldbem = shaderstate.mode; oldbem = shaderstate.mode;
oldil = shaderstate.identitylighting; oldil = shaderstate.identitylighting;
targ = vk.rendertarg; // targ = vk.rendertarg;
if (bs->flags & SHADER_HASREFLECT) if (bs->flags & SHADER_HASREFLECT)
{ {
@ -5812,7 +5813,7 @@ void VKBE_DestroyShadowBuffer(struct vk_shadowbuffer *buf)
{ {
if (buf && buf->isstatic) if (buf && buf->isstatic)
{ {
struct vk_shadowbuffer_destroy *ctx = VK_AtFrameEnd(VKBE_DestroyShadowBuffer_Delayed, buf, sizeof(*buf)); VK_AtFrameEnd(VKBE_DestroyShadowBuffer_Delayed, buf, sizeof(*buf));
Z_Free(buf); Z_Free(buf);
} }
} }

View file

@ -17,6 +17,11 @@ extern cvar_t vk_khr_push_descriptor;
extern cvar_t vid_srgb, vid_vsync, vid_triplebuffer, r_stereo_method, vid_multisample; extern cvar_t vid_srgb, vid_vsync, vid_triplebuffer, r_stereo_method, vid_multisample;
void R2D_Console_Resize(void); void R2D_Console_Resize(void);
#ifndef MULTITHREAD
#define Sys_LockConditional(c)
#define Sys_UnlockConditional(c)
#endif
const char *vklayerlist[] = const char *vklayerlist[] =
{ {
#if 1 #if 1
@ -142,11 +147,16 @@ void VK_DestroyVkTexture(vk_image_t *img)
if (img->memory) if (img->memory)
vkFreeMemory(vk.device, img->memory, vkallocationcb); vkFreeMemory(vk.device, img->memory, vkallocationcb);
} }
static void VK_DestroyVkTexture_Delayed(void *w)
{
VK_DestroyVkTexture(w);
}
static void VK_DestroySwapChain(void) static void VK_DestroySwapChain(void)
{ {
uint32_t i; uint32_t i;
#ifdef MULTITHREAD
if (vk.submitcondition) if (vk.submitcondition)
{ {
Sys_LockConditional(vk.submitcondition); Sys_LockConditional(vk.submitcondition);
@ -159,6 +169,7 @@ static void VK_DestroySwapChain(void)
Sys_WaitOnThread(vk.submitthread); Sys_WaitOnThread(vk.submitthread);
vk.submitthread = NULL; vk.submitthread = NULL;
} }
#endif
while (vk.work) while (vk.work)
{ {
Sys_LockConditional(vk.submitcondition); Sys_LockConditional(vk.submitcondition);
@ -787,7 +798,7 @@ void VK_CreateSampler(unsigned int flags, vk_image_t *img)
VkAssert(vkCreateSampler(vk.device, &lmsampinfo, NULL, &img->sampler)); VkAssert(vkCreateSampler(vk.device, &lmsampinfo, NULL, &img->sampler));
} }
static void VK_DestroySampler(struct vk_frameend *w) static void VK_DestroySampler(void *w)
{ {
VkSampler s = *(VkSampler*)w; VkSampler s = *(VkSampler*)w;
vkDestroySampler(vk.device, s, vkallocationcb); vkDestroySampler(vk.device, s, vkallocationcb);
@ -1228,7 +1239,7 @@ qboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips)
tex->vkimage->encoding != mips->encoding || tex->vkimage->encoding != mips->encoding ||
tex->vkimage->type != mips->type) tex->vkimage->type != mips->type)
{ {
VK_AtFrameEnd(VK_DestroyVkTexture, tex->vkimage, sizeof(*tex->vkimage)); VK_AtFrameEnd(VK_DestroyVkTexture_Delayed, tex->vkimage, sizeof(*tex->vkimage));
// vkDeviceWaitIdle(vk.device); //erk, we can't cope with a commandbuffer poking the texture while things happen // vkDeviceWaitIdle(vk.device); //erk, we can't cope with a commandbuffer poking the texture while things happen
// VK_FencedCheck(); // VK_FencedCheck();
// VK_DestroyVkTexture(tex->vkimage); // VK_DestroyVkTexture(tex->vkimage);
@ -1322,7 +1333,6 @@ qboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips)
bci.size = 0; bci.size = 0;
for (i = 0; i < mipcount; i++) for (i = 0; i < mipcount; i++)
{ {
VkImageSubresource subres = {0};
VkBufferImageCopy region; VkBufferImageCopy region;
//figure out the number of 'blocks' in the image. //figure out the number of 'blocks' in the image.
//for non-compressed formats this is just the width directly. //for non-compressed formats this is just the width directly.
@ -2318,6 +2328,8 @@ void VKVID_QueueGetRGBData (void (*gotrgbdata) (void *rgbdata, intptr_t bytest
VkAssert(vkCreateBuffer(vk.device, &bci, vkallocationcb, &capt->buffer)); VkAssert(vkCreateBuffer(vk.device, &bci, vkallocationcb, &capt->buffer));
vkGetBufferMemoryRequirements(vk.device, capt->buffer, &mem_reqs); vkGetBufferMemoryRequirements(vk.device, capt->buffer, &mem_reqs);
memAllocInfo.allocationSize = mem_reqs.size; memAllocInfo.allocationSize = mem_reqs.size;
memAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT|VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
if (memAllocInfo.memoryTypeIndex == ~0u)
memAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); memAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &capt->memory)); VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &capt->memory));
VkAssert(vkBindBufferMemory(vk.device, capt->buffer, capt->memory, 0)); VkAssert(vkBindBufferMemory(vk.device, capt->buffer, capt->memory, 0));
@ -2849,6 +2861,7 @@ qboolean VK_SCR_UpdateScreen (void)
if (vk.neednewswapchain && !vk.frame) if (vk.neednewswapchain && !vk.frame)
{ {
#ifdef MULTITHREAD
//kill the thread //kill the thread
if (vk.submitthread) if (vk.submitthread)
{ {
@ -2858,6 +2871,7 @@ qboolean VK_SCR_UpdateScreen (void)
Sys_WaitOnThread(vk.submitthread); Sys_WaitOnThread(vk.submitthread);
vk.submitthread = NULL; vk.submitthread = NULL;
} }
#endif
//make sure any work is actually done BEFORE the swapchain gets destroyed //make sure any work is actually done BEFORE the swapchain gets destroyed
while (vk.work) while (vk.work)
{ {
@ -2871,10 +2885,12 @@ qboolean VK_SCR_UpdateScreen (void)
VK_CreateSwapChain(); VK_CreateSwapChain();
vk.neednewswapchain = false; vk.neednewswapchain = false;
#ifdef MULTITHREAD
if (vk.allowsubmissionthread && (vk_submissionthread.ival || !*vk_submissionthread.string)) if (vk.allowsubmissionthread && (vk_submissionthread.ival || !*vk_submissionthread.string))
{ {
vk.submitthread = Sys_CreateThread("vksubmission", VK_Submit_Thread, NULL, THREADP_HIGHEST, 0); vk.submitthread = Sys_CreateThread("vksubmission", VK_Submit_Thread, NULL, THREADP_HIGHEST, 0);
} }
#endif
} }
if (!VK_SCR_GrabBackBuffer()) if (!VK_SCR_GrabBackBuffer())
@ -3225,6 +3241,7 @@ static void VK_Submit_DoWork(void)
} }
} }
#ifdef MULTITHREAD
//oh look. a thread. //oh look. a thread.
//nvidia's drivers seem to like doing a lot of blocking in queuesubmit and queuepresent(despite the whole QUEUE thing). //nvidia's drivers seem to like doing a lot of blocking in queuesubmit and queuepresent(despite the whole QUEUE thing).
//so thread this work so the main thread doesn't have to block so much. //so thread this work so the main thread doesn't have to block so much.
@ -3241,6 +3258,7 @@ int VK_Submit_Thread(void *arg)
Sys_UnlockConditional(vk.submitcondition); Sys_UnlockConditional(vk.submitcondition);
return true; return true;
} }
#endif
void VK_Submit_Work(VkCommandBuffer cmdbuf, VkSemaphore semwait, VkPipelineStageFlags semwaitstagemask, VkSemaphore semsignal, VkFence fencesignal, struct vkframe *presentframe, struct vk_fencework *fencedwork) void VK_Submit_Work(VkCommandBuffer cmdbuf, VkSemaphore semwait, VkPipelineStageFlags semwaitstagemask, VkSemaphore semsignal, VkFence fencesignal, struct vkframe *presentframe, struct vk_fencework *fencedwork)
{ {
@ -3262,9 +3280,11 @@ void VK_Submit_Work(VkCommandBuffer cmdbuf, VkSemaphore semwait, VkPipelineStage
; ;
*link = work; *link = work;
#ifdef MULTITHREAD
if (vk.submitthread && !vk.neednewswapchain) if (vk.submitthread && !vk.neednewswapchain)
Sys_ConditionSignal(vk.submitcondition); Sys_ConditionSignal(vk.submitcondition);
else else
#endif
VK_Submit_DoWork(); VK_Submit_DoWork();
Sys_UnlockConditional(vk.submitcondition); Sys_UnlockConditional(vk.submitcondition);
} }
@ -3335,7 +3355,7 @@ void VK_CheckTextureFormats(void)
} }
//initialise the vulkan instance, context, device, etc. //initialise the vulkan instance, context, device, etc.
qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*createSurface)(void), void (*dopresent)(struct vkframe *theframe)) qboolean VK_Init(rendererstate_t *info, const char **sysextnames, qboolean (*createSurface)(void), void (*dopresent)(struct vkframe *theframe))
{ {
VkQueueFamilyProperties *queueprops; VkQueueFamilyProperties *queueprops;
VkResult err; VkResult err;
@ -3366,7 +3386,9 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat
for (e = 0; e < countof(knowndevexts); e++) for (e = 0; e < countof(knowndevexts); e++)
*knowndevexts[e].flag = false; *knowndevexts[e].flag = false;
#ifdef MULTITHREAD
vk.allowsubmissionthread = true; vk.allowsubmissionthread = true;
#endif
vk.neednewswapchain = true; vk.neednewswapchain = true;
vk.triplebuffer = info->triplebuffer; vk.triplebuffer = info->triplebuffer;
vk.vsync = info->wait; vk.vsync = info->wait;
@ -3390,7 +3412,7 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat
//try and enable some instance extensions... //try and enable some instance extensions...
{ {
qboolean surfext = false; qboolean surfext = false;
uint32_t count, i; uint32_t count, i, j;
VkExtensionProperties *ext; VkExtensionProperties *ext;
vkEnumerateInstanceExtensionProperties(NULL, &count, NULL); vkEnumerateInstanceExtensionProperties(NULL, &count, NULL);
ext = malloc(sizeof(*ext)*count); ext = malloc(sizeof(*ext)*count);
@ -3401,21 +3423,27 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat
extensions[extensions_count++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME; extensions[extensions_count++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME;
else if (!strcmp(ext[i].extensionName, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) else if (!strcmp(ext[i].extensionName, VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME))
extensions[extensions_count++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; extensions[extensions_count++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME;
else if (sysextname && !strcmp(ext[i].extensionName, sysextname)) else if (sysextnames && !strcmp(ext[i].extensionName, VK_KHR_SURFACE_EXTENSION_NAME))
{
extensions[extensions_count++] = sysextname;
vk.khr_swapchain = true;
}
else if (sysextname && !strcmp(ext[i].extensionName, VK_KHR_SURFACE_EXTENSION_NAME))
{ {
extensions[extensions_count++] = VK_KHR_SURFACE_EXTENSION_NAME; extensions[extensions_count++] = VK_KHR_SURFACE_EXTENSION_NAME;
surfext = true; surfext = true;
} }
else if (sysextnames)
{
for (j = 0; sysextnames[j]; j++)
{
if (!strcmp(ext[i].extensionName, sysextnames[j]))
{
extensions[extensions_count++] = sysextnames[j];
vk.khr_swapchain = true;
}
}
}
} }
free(ext); free(ext);
if (sysextname && (!vk.khr_swapchain || !surfext)) if (sysextnames && (!vk.khr_swapchain || !surfext))
{ {
Con_Printf("Vulkan instance driver lacks support for %s\n", sysextname); Con_Printf("Vulkan instance driver lacks support for %s\n", sysextnames[0]);
return false; return false;
} }
} }
@ -3873,7 +3901,9 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat
else //16bit depth is guarenteed in vulkan else //16bit depth is guarenteed in vulkan
vk.depthformat = VK_FORMAT_D16_UNORM; vk.depthformat = VK_FORMAT_D16_UNORM;
#ifdef MULTITHREAD
vk.submitcondition = Sys_CreateConditional(); vk.submitcondition = Sys_CreateConditional();
#endif
{ {
VkPipelineCacheCreateInfo pci = {VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO}; VkPipelineCacheCreateInfo pci = {VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO};
@ -3888,10 +3918,12 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat
{ {
vk.neednewswapchain = false; vk.neednewswapchain = false;
#ifdef MULTITHREAD
if (vk.allowsubmissionthread && (vk_submissionthread.ival || !*vk_submissionthread.string)) if (vk.allowsubmissionthread && (vk_submissionthread.ival || !*vk_submissionthread.string))
{ {
vk.submitthread = Sys_CreateThread("vksubmission", VK_Submit_Thread, NULL, THREADP_HIGHEST, 0); vk.submitthread = Sys_CreateThread("vksubmission", VK_Submit_Thread, NULL, THREADP_HIGHEST, 0);
} }
#endif
} }
return true; return true;
} }
@ -3935,8 +3967,10 @@ void VK_Shutdown(void)
vkDestroySurfaceKHR(vk.instance, vk.surface, vkallocationcb); vkDestroySurfaceKHR(vk.instance, vk.surface, vkallocationcb);
if (vk.instance) if (vk.instance)
vkDestroyInstance(vk.instance, vkallocationcb); vkDestroyInstance(vk.instance, vkallocationcb);
#ifdef MULTITHREAD
if (vk.submitcondition) if (vk.submitcondition)
Sys_DestroyConditional(vk.submitcondition); Sys_DestroyConditional(vk.submitcondition);
#endif
memset(&vk, 0, sizeof(vk)); memset(&vk, 0, sizeof(vk));

View file

@ -378,7 +378,7 @@ uint32_t vk_find_memory_require(uint32_t typeBits, VkFlags requirements_mask);
void VK_DoPresent(struct vkframe *theframe); void VK_DoPresent(struct vkframe *theframe);
qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*createSurface)(void), void (*dopresent)(struct vkframe *theframe)); qboolean VK_Init(rendererstate_t *info, const char **sysextname, qboolean (*createSurface)(void), void (*dopresent)(struct vkframe *theframe));
void VK_Shutdown(void); void VK_Shutdown(void);
void VK_R_BloomBlend (texid_t source, int x, int y, int w, int h); void VK_R_BloomBlend (texid_t source, int x, int y, int w, int h);

View file

@ -287,7 +287,8 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
vid.activeapp = true; vid.activeapp = true;
GL_Init(info, GLVID_getsdlglfunction); if (!GL_Init(info, GLVID_getsdlglfunction))
return false;
qglViewport (0, 0, vid.pixelwidth, vid.pixelheight); qglViewport (0, 0, vid.pixelwidth, vid.pixelheight);

View file

@ -7,6 +7,7 @@
#include "libavutil/imgutils.h" #include "libavutil/imgutils.h"
#define TARGET_FFMPEG (LIBAVFORMAT_VERSION_MICRO >= 100) #define TARGET_FFMPEG (LIBAVFORMAT_VERSION_MICRO >= 100)
#define HAVE_DECOUPLED_API (LIBAVCODEC_VERSION_MAJOR>57 || (LIBAVCODEC_VERSION_MAJOR==57&&LIBAVCODEC_VERSION_MINOR>=36))
//between av 52.31 and 54.35, lots of constants etc got renamed to gain an extra AV_ prefix. //between av 52.31 and 54.35, lots of constants etc got renamed to gain an extra AV_ prefix.
/* /*
@ -224,16 +225,29 @@ having them tied to the libavformat network IO.
if(avformat_find_stream_info(ctx->pFormatCtx, NULL)>=0) if(avformat_find_stream_info(ctx->pFormatCtx, NULL)>=0)
{ {
ctx->audioStream=-1; ctx->audioStream=-1;
for(i=0; i<ctx->pFormatCtx->nb_streams; i++) for(i=0; i<ctx->pFormatCtx->nb_streams && ctx->audioStream==-1; i++)
if(ctx->pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)
{ {
#if LIBAVFORMAT_VERSION_MAJOR >= 57
if(ctx->pFormatCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_AUDIO)
#else
if(ctx->pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO)
#endif
ctx->audioStream=i; ctx->audioStream=i;
break;
} }
if(ctx->audioStream!=-1) if(ctx->audioStream!=-1)
{ {
#if LIBAVFORMAT_VERSION_MAJOR >= 57
pCodec=avcodec_find_decoder(ctx->pFormatCtx->streams[ctx->audioStream]->codecpar->codec_id);
ctx->pACodecCtx = avcodec_alloc_context3(pCodec);
if (avcodec_parameters_to_context(ctx->pACodecCtx, ctx->pFormatCtx->streams[ctx->audioStream]->codecpar) < 0)
{
avcodec_free_context(&ctx->pACodecCtx);
pCodec = NULL;
}
#else
ctx->pACodecCtx=ctx->pFormatCtx->streams[ctx->audioStream]->codec; ctx->pACodecCtx=ctx->pFormatCtx->streams[ctx->audioStream]->codec;
pCodec=avcodec_find_decoder(ctx->pACodecCtx->codec_id); pCodec=avcodec_find_decoder(ctx->pACodecCtx->codec_id);
#endif
ctx->pAFrame=av_frame_alloc(); ctx->pAFrame=av_frame_alloc();
if(pCodec!=NULL && ctx->pAFrame && avcodec_open2(ctx->pACodecCtx, pCodec, NULL) >= 0) if(pCodec!=NULL && ctx->pAFrame && avcodec_open2(ctx->pACodecCtx, pCodec, NULL) >= 0)
@ -245,22 +259,32 @@ having them tied to the libavformat network IO.
} }
ctx->videoStream=-1; ctx->videoStream=-1;
for(i=0; i<ctx->pFormatCtx->nb_streams; i++) for(i=0; i<ctx->pFormatCtx->nb_streams && ctx->videoStream==-1; i++)
if(ctx->pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
{ {
#if LIBAVFORMAT_VERSION_MAJOR >= 57
if(ctx->pFormatCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO)
#else
if(ctx->pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)
#endif
ctx->videoStream=i; ctx->videoStream=i;
break;
} }
if(ctx->videoStream!=-1) if(ctx->videoStream!=-1)
{ {
// Get a pointer to the codec context for the video stream #if LIBAVFORMAT_VERSION_MAJOR >= 57
pCodec=avcodec_find_decoder(ctx->pFormatCtx->streams[ctx->videoStream]->codecpar->codec_id);
ctx->pVCodecCtx = avcodec_alloc_context3(pCodec);
if (avcodec_parameters_to_context(ctx->pVCodecCtx, ctx->pFormatCtx->streams[ctx->videoStream]->codecpar) < 0)
{
avcodec_free_context(&ctx->pVCodecCtx);
pCodec = NULL;
}
#else
ctx->pVCodecCtx=ctx->pFormatCtx->streams[ctx->videoStream]->codec; ctx->pVCodecCtx=ctx->pFormatCtx->streams[ctx->videoStream]->codec;
pCodec=avcodec_find_decoder(ctx->pVCodecCtx->codec_id);
#endif
ctx->num = ctx->pFormatCtx->streams[ctx->videoStream]->time_base.num; ctx->num = ctx->pFormatCtx->streams[ctx->videoStream]->time_base.num;
ctx->denum = ctx->pFormatCtx->streams[ctx->videoStream]->time_base.den; ctx->denum = ctx->pFormatCtx->streams[ctx->videoStream]->time_base.den;
// Find the decoder for the video stream
pCodec=avcodec_find_decoder(ctx->pVCodecCtx->codec_id);
// Open codec // Open codec
if(pCodec!=NULL && avcodec_open2(ctx->pVCodecCtx, pCodec, NULL) >= 0) if(pCodec!=NULL && avcodec_open2(ctx->pVCodecCtx, pCodec, NULL) >= 0)
{ {
@ -285,7 +309,10 @@ static qboolean VARGS AVDec_DisplayFrame(void *vctx, qboolean nosound, qboolean
{ {
struct decctx *ctx = (struct decctx*)vctx; struct decctx *ctx = (struct decctx*)vctx;
AVPacket packet; AVPacket packet;
#if HAVE_DECOUPLED_API
#else
int frameFinished; int frameFinished;
#endif
qboolean repainted = false; qboolean repainted = false;
int64_t curtime; int64_t curtime;
@ -304,6 +331,101 @@ static qboolean VARGS AVDec_DisplayFrame(void *vctx, qboolean nosound, qboolean
return false; return false;
} }
#if HAVE_DECOUPLED_API
if(packet.stream_index==ctx->videoStream)
{
avcodec_send_packet(ctx->pVCodecCtx, &packet);
while(0==avcodec_receive_frame(ctx->pVCodecCtx, ctx->pVFrame))
{
//rescale+convert it to what we're rendering (no more yuv)
ctx->pScaleCtx = sws_getCachedContext(ctx->pScaleCtx, ctx->pVCodecCtx->width, ctx->pVCodecCtx->height, ctx->pVCodecCtx->pix_fmt, ctx->width, ctx->height, AV_PIX_FMT_BGRA, SWS_POINT, 0, 0, 0);
sws_scale(ctx->pScaleCtx, (void*)ctx->pVFrame->data, ctx->pVFrame->linesize, 0, ctx->pVCodecCtx->height, &ctx->rgb_data, &ctx->rgb_linesize);
ctx->lasttime = av_frame_get_best_effort_timestamp(ctx->pVFrame);
repainted = true;
}
}
else if(packet.stream_index==ctx->audioStream && !nosound)
{
avcodec_send_packet(ctx->pACodecCtx, &packet);
while(0==avcodec_receive_frame(ctx->pACodecCtx, ctx->pAFrame))
{
int width = 2;
int channels = ctx->pACodecCtx->channels;
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)
{
default:
auddatasize = 0;
break;
case AV_SAMPLE_FMT_U8P:
auddatasize /= channels;
channels = 1;
case AV_SAMPLE_FMT_U8:
width = 1;
break;
case AV_SAMPLE_FMT_S16P:
auddatasize /= channels;
channels = 1;
case AV_SAMPLE_FMT_S16:
width = 2;
break;
case AV_SAMPLE_FMT_FLTP:
auddatasize /= channels;
channels = 1;
case AV_SAMPLE_FMT_FLT:
//FIXME: support float audio internally.
{
float *in = (void*)auddata;
signed short *out = (void*)auddata;
int v;
unsigned int i;
for (i = 0; i < auddatasize/sizeof(*in); i++)
{
v = (short)(in[i]*32767);
if (v < -32767)
v = -32767;
else if (v > 32767)
v = 32767;
out[i] = v;
}
auddatasize/=2;
width = 2;
}
case AV_SAMPLE_FMT_DBLP:
auddatasize /= channels;
channels = 1;
case AV_SAMPLE_FMT_DBL:
{
double *in = (double*)auddata;
signed short *out = (void*)auddata;
int v;
unsigned int i;
for (i = 0; i < auddatasize/sizeof(*in); i++)
{
v = (short)(in[i]*32767);
if (v < -32767)
v = -32767;
else if (v > 32767)
v = 32767;
out[i] = v;
}
auddatasize/=4;
width = 2;
}
break;
}
pS_RawAudio(-1, auddata, ctx->pACodecCtx->sample_rate, auddatasize/(channels*width), channels, width, 1);
}
}
av_packet_unref(&packet);
#else
// Is this a packet from the video stream? // Is this a packet from the video stream?
if(packet.stream_index==ctx->videoStream) if(packet.stream_index==ctx->videoStream)
{ {
@ -423,6 +545,7 @@ static qboolean VARGS AVDec_DisplayFrame(void *vctx, qboolean nosound, qboolean
// Free the packet that was allocated by av_read_frame // Free the packet that was allocated by av_read_frame
av_packet_unref(&packet); av_packet_unref(&packet);
#endif
} }
if (forcevideo || repainted) if (forcevideo || repainted)
@ -451,16 +574,11 @@ static void AVDec_ChangeStream(void *vctx, char *newstream)
static void AVDec_Rewind(void *vctx) static void AVDec_Rewind(void *vctx)
{ {
struct decctx *ctx = (struct decctx*)vctx; struct decctx *ctx = (struct decctx*)vctx;
if (ctx->videoStream >= 0) if (ctx->lasttime != -1)
{ {
av_seek_frame(ctx->pFormatCtx, ctx->videoStream, 0, AVSEEK_FLAG_FRAME|AVSEEK_FLAG_BACKWARD); av_seek_frame(ctx->pFormatCtx, -1, 0, AVSEEK_FLAG_FRAME|AVSEEK_FLAG_BACKWARD);
avcodec_flush_buffers(ctx->pVCodecCtx); avcodec_flush_buffers(ctx->pVCodecCtx);
} }
if (ctx->audioStream >= 0)
{
av_seek_frame(ctx->pFormatCtx, ctx->audioStream, 0, AVSEEK_FLAG_FRAME|AVSEEK_FLAG_BACKWARD);
avcodec_flush_buffers(ctx->pACodecCtx);
}
ctx->lasttime = -1; ctx->lasttime = -1;
} }

View file

@ -35,6 +35,14 @@
#define cef_v8context_get_current_context pcef_v8context_get_current_context #define cef_v8context_get_current_context pcef_v8context_get_current_context
#define cef_post_task pcef_post_task #define cef_post_task pcef_post_task
#define cef_request_context_create_context pcef_request_context_create_context #define cef_request_context_create_context pcef_request_context_create_context
#define cef_string_multimap_alloc pcef_string_multimap_alloc
#define cef_string_multimap_append pcef_string_multimap_append
#define cef_string_multimap_size pcef_string_multimap_size
#define cef_string_multimap_key pcef_string_multimap_key
#define cef_string_multimap_value pcef_string_multimap_value
#define cef_string_multimap_free pcef_string_multimap_free
#define cef_string_list_size pcef_string_list_size
#define cef_string_list_value pcef_string_list_value
#define cef_addref(ptr) (ptr)->base.add_ref(&(ptr)->base) #define cef_addref(ptr) (ptr)->base.add_ref(&(ptr)->base)
#define cef_release(ptr) (((ptr)->base.release)(&(ptr)->base)) #define cef_release(ptr) (((ptr)->base.release)(&(ptr)->base))
@ -62,6 +70,17 @@ static cef_process_message_t* (*cef_process_message_create)(const cef_string_t*
static cef_v8context_t* (*cef_v8context_get_current_context)(void); //typical C++ programmers omitted the void. static cef_v8context_t* (*cef_v8context_get_current_context)(void); //typical C++ programmers omitted the void.
static int (*cef_post_task)(cef_thread_id_t threadId, cef_task_t* task); static int (*cef_post_task)(cef_thread_id_t threadId, cef_task_t* task);
static cef_request_context_t* (*cef_request_context_create_context)(const cef_request_context_settings_t* settings, cef_request_context_handler_t* handler); static cef_request_context_t* (*cef_request_context_create_context)(const cef_request_context_settings_t* settings, cef_request_context_handler_t* handler);
static cef_string_multimap_t (*cef_string_multimap_alloc)(void);
static int (*cef_string_multimap_append)(cef_string_multimap_t map, const cef_string_t* key, const cef_string_t* value);
static size_t (*cef_string_multimap_size)(cef_string_multimap_t map);
static int (*cef_string_multimap_key)(cef_string_multimap_t map, size_t index, cef_string_t* key);
static int (*cef_string_multimap_value)(cef_string_multimap_t map, size_t index, cef_string_t* value);
static void (*cef_string_multimap_free)(cef_string_multimap_t map);
static size_t (*cef_string_list_size)(cef_string_list_t list);
static int (*cef_string_list_value)(cef_string_list_t list, size_t index, cef_string_t* value);
#ifndef CEF_VERSION //old builds lack this #ifndef CEF_VERSION //old builds lack this
#define CEF_VERSION "cef"STRINGIFY(CEF_VERSION_MAJOR)"."STRINGIFY(CEF_REVISION)"."STRINGIFY(CHROME_VERSION_BUILD) #define CEF_VERSION "cef"STRINGIFY(CEF_VERSION_MAJOR)"."STRINGIFY(CEF_REVISION)"."STRINGIFY(CHROME_VERSION_BUILD)
@ -164,16 +183,21 @@ vmcvar_t cef_devtools = {"cef_devtools", "0", "browser settings", 0};
static char plugname[MAX_OSPATH]; static char plugname[MAX_OSPATH];
static char *newconsole; static char *newconsole;
static void setcefstring(char *str, cef_string_t *r) /*static void setcefstring(char *str, cef_string_t *r)
{ {
cef_string_from_utf8(str, strlen(str), r); cef_string_from_utf8(str, strlen(str), r);
} }*/
static cef_string_t makecefstring(char *str) static cef_string_t makecefstring(char *str)
{ {
cef_string_t r = {NULL}; cef_string_t r = {NULL};
cef_string_from_utf8(str, strlen(str), &r); cef_string_from_utf8(str, strlen(str), &r);
return r; return r;
} }
static cef_string_t *makecefstringptr(char *str, cef_string_t *ptr)
{
cef_string_from_utf8(str, strlen(str), ptr);
return ptr;
}
static char *Info_JSONify (char *s, char *o, size_t outlen) static char *Info_JSONify (char *s, char *o, size_t outlen)
{ {
@ -247,6 +271,7 @@ typedef struct
cef_display_handler_t display_handler; cef_display_handler_t display_handler;
cef_request_handler_t request_handler; cef_request_handler_t request_handler;
cef_life_span_handler_t life_span_handler; cef_life_span_handler_t life_span_handler;
cef_context_menu_handler_t context_menu_handler;
cef_browser_t *thebrowser; cef_browser_t *thebrowser;
void *videodata; void *videodata;
@ -256,7 +281,9 @@ typedef struct
int desiredwidth; int desiredwidth;
int desiredheight; int desiredheight;
char *consolename; //for internal plugin use. char *consolename; //for internal plugin use.
qboolean fullscreen;
cef_string_utf8_t currenturl; cef_string_utf8_t currenturl;
cef_string_utf8_t currenticon;
cef_string_utf8_t currenttitle; cef_string_utf8_t currenttitle;
cef_string_utf8_t currentstatus; cef_string_utf8_t currentstatus;
@ -278,6 +305,7 @@ static int browser_release(browser_t *br)
if (br->videodata) if (br->videodata)
free(br->videodata); free(br->videodata);
cef_string_utf8_clear(&br->currenturl); cef_string_utf8_clear(&br->currenturl);
cef_string_utf8_clear(&br->currenticon);
cef_string_utf8_clear(&br->currenttitle); cef_string_utf8_clear(&br->currenttitle);
cef_string_utf8_clear(&br->currentstatus); cef_string_utf8_clear(&br->currentstatus);
@ -302,6 +330,7 @@ browser_subs(render_handler);
browser_subs(display_handler); browser_subs(display_handler);
browser_subs(request_handler); browser_subs(request_handler);
browser_subs(life_span_handler); browser_subs(life_span_handler);
browser_subs(context_menu_handler);
#undef browser_subs #undef browser_subs
//client methods //client methods
@ -317,6 +346,12 @@ static cef_life_span_handler_t *CEF_CALLBACK browser_get_life_span_handler(cef_c
cef_addref(&br->life_span_handler); cef_addref(&br->life_span_handler);
return &br->life_span_handler; return &br->life_span_handler;
} }
static cef_context_menu_handler_t *CEF_CALLBACK browser_get_context_menu_handler(cef_client_t *self)
{
browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, client));
cef_addref(&br->context_menu_handler);
return &br->context_menu_handler;
}
static cef_display_handler_t *CEF_CALLBACK browser_get_display_handler(cef_client_t *self) static cef_display_handler_t *CEF_CALLBACK browser_get_display_handler(cef_client_t *self)
{ {
browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, client)); browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, client));
@ -332,7 +367,9 @@ static cef_request_handler_t *CEF_CALLBACK browser_get_request_handler(cef_clien
static qboolean browser_handle_query(const char *req, char *buffer, size_t buffersize) static qboolean browser_handle_query(const char *req, char *buffer, size_t buffersize)
{ {
if (!strncmp(req, "getcvar_", 8)) if (!req)
return false;
else if (!strncmp(req, "getcvar_", 8))
{ {
Cvar_Update(&cef_allowcvars); Cvar_Update(&cef_allowcvars);
if (cef_allowcvars.value && pCvar_GetString(req+8, buffer, buffersize)) if (cef_allowcvars.value && pCvar_GetString(req+8, buffer, buffersize))
@ -462,7 +499,7 @@ static int CEF_CALLBACK browser_on_process_message_received(cef_client_t* self,
cef_string_t str = {NULL}; cef_string_t str = {NULL};
cef_list_value_t *args = message->get_argument_list(message); cef_list_value_t *args = message->get_argument_list(message);
cef_string_userfree_t cmdunusable = args->get_string(args, 0); cef_string_userfree_t cmdunusable = args->get_string(args, 0);
cef_string_to_utf8(cmdunusable->str, cmdunusable->length, &queryname); cef_string_to_utf8(cmdunusable?cmdunusable->str:NULL, cmdunusable?cmdunusable->length:0, &queryname);
id1 = args->get_int(args, 2); id1 = args->get_int(args, 2);
id2 = args->get_int(args, 3); id2 = args->get_int(args, 3);
@ -478,12 +515,13 @@ static int CEF_CALLBACK browser_on_process_message_received(cef_client_t* self,
{ {
str = makecefstring(buffer); str = makecefstring(buffer);
args->set_string(args, 1, &str); args->set_string(args, 1, &str);
cef_string_clear(&str);
} }
else else
args->set_null(args, 1); args->set_null(args, 1);
cef_release(args); cef_release(args);
cef_string_utf8_clear(&queryname); cef_string_utf8_clear(&queryname);
cef_string_clear(&str); if (cmdunusable)
cef_string_userfree_free(cmdunusable); cef_string_userfree_free(cmdunusable);
browser->send_process_message(browser, source_process, reply); browser->send_process_message(browser, source_process, reply);
handled = true; handled = true;
@ -519,8 +557,22 @@ static void CEF_CALLBACK browser_on_paint(cef_render_handler_t *self, cef_browse
if (type != PET_VIEW) if (type != PET_VIEW)
return; return;
if (br->videowidth != width || br->videoheight != height) /*
if (pbocontext)
{ {
if (!PBO_Lock(pbocontext, width, height, &lost, rgba))
return;
if (lost)
PBO_Update(pbocontext, buffer, width, height, stride);
else while (dirtyRectsCount --> 0)
PBO_Update(pbocontext, (char*)buffer+(width*dirtyRects->y + dirtyRects->x)*4), dirtyRects->width, dirtyRects->height, width*4);
PBO_Unlock(pbocontext);
}
else
*/
if (br->videowidth != width || br->videoheight != height)
{ //copy the entire thing.
if (br->videodata) if (br->videodata)
free(br->videodata); free(br->videodata);
br->videowidth = width; br->videowidth = width;
@ -529,7 +581,7 @@ static void CEF_CALLBACK browser_on_paint(cef_render_handler_t *self, cef_browse
memcpy(br->videodata, buffer, width * height * 4); memcpy(br->videodata, buffer, width * height * 4);
} }
else else
{ { //try to save cpu time by copying only the dirty parts
while (dirtyRectsCount --> 0) while (dirtyRectsCount --> 0)
{ {
if (width == dirtyRects->width && height == dirtyRects->height) if (width == dirtyRects->width && height == dirtyRects->height)
@ -568,6 +620,23 @@ static void CEF_CALLBACK browser_on_before_close(cef_life_span_handler_t* self,
cef_release(browser); cef_release(browser);
} }
//context_menu_handler methods
static void CEF_CALLBACK browser_on_before_context_menu(struct _cef_context_menu_handler_t* self, struct _cef_browser_t* browser, struct _cef_frame_t* frame, struct _cef_context_menu_params_t* params, struct _cef_menu_model_t* model)
{
// browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, context_menu_handler));
//wipe whatever elements libcef thinks it should add
model->clear(model);
//don't bother adding any new ones.
cef_release(browser);
cef_release(frame);
cef_release(params);
cef_release(model);
}
//display_handler methods //display_handler methods
//redirect console.log messages to quake's console, but only display them if we've got developer set. //redirect console.log messages to quake's console, but only display them if we've got developer set.
static int CEF_CALLBACK browser_on_console_message(cef_display_handler_t* self, cef_browser_t* browser, const cef_string_t* message, const cef_string_t* source, int line) static int CEF_CALLBACK browser_on_console_message(cef_display_handler_t* self, cef_browser_t* browser, const cef_string_t* message, const cef_string_t* source, int line)
@ -599,12 +668,48 @@ static void CEF_CALLBACK browser_on_title_change(cef_display_handler_t* self, ce
cef_release(browser); cef_release(browser);
} }
static void CEF_CALLBACK browser_on_favicon_urlchange(cef_display_handler_t* self, cef_browser_t* browser, cef_string_list_t favicon)
{
browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, display_handler));
cef_string_t str = {NULL};
if (favicon)
{
//size_t opts = cef_string_list_size(favicon);
cef_string_list_value(favicon, 0, &str);
}
cef_string_to_utf8(str.str, str.length, &br->currenticon);
cef_string_clear(&str);
if (br->consolename)
pCon_SetConsoleString(br->consolename, "icon", br->currenticon.str?br->currenticon.str:"");
cef_release(browser);
}
static void CEF_CALLBACK browser_on_fullscreenmode_change(cef_display_handler_t* self, cef_browser_t* browser, int fullscreen)
{
browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, display_handler));
br->fullscreen = fullscreen;
if (br->consolename)
pCon_SetConsoleFloat(br->consolename, "fullscreen", br->fullscreen);
cef_release(browser);
}
static void CEF_CALLBACK browser_on_address_change(cef_display_handler_t* self, cef_browser_t* browser, cef_frame_t* frame, const cef_string_t* url) static void CEF_CALLBACK browser_on_address_change(cef_display_handler_t* self, cef_browser_t* browser, cef_frame_t* frame, const cef_string_t* url)
{ {
browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, display_handler)); browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, display_handler));
cef_string_to_utf8(url->str, url->length, &br->currenturl); cef_string_to_utf8(url->str, url->length, &br->currenturl);
if (br->currenticon.length)
{
cef_string_utf8_clear(&br->currenticon);
if (br->consolename)
pCon_SetConsoleString(br->consolename, "icon", br->currenticon.str?br->currenticon.str:"");
}
//FIXME: should probably make sure its the root frame //FIXME: should probably make sure its the root frame
// Con_Printf("new url: %s\n", url.ToString().c_str()); // Con_Printf("new url: %s\n", url.ToString().c_str());
cef_release(browser); cef_release(browser);
@ -680,24 +785,74 @@ browser_subs(render_handler);
browser_subs(display_handler); browser_subs(display_handler);
browser_subs(request_handler); browser_subs(request_handler);
browser_subs(life_span_handler); browser_subs(life_span_handler);
browser_subs(context_menu_handler);
#undef browser_subs #undef browser_subs
nb->client.get_life_span_handler = browser_get_life_span_handler; nb->client.get_context_menu_handler = browser_get_context_menu_handler;
nb->client.get_render_handler = browser_get_render_handler; nb->client.get_dialog_handler = NULL;
nb->client.get_display_handler = browser_get_display_handler; nb->client.get_display_handler = browser_get_display_handler;
nb->client.get_download_handler = NULL;
nb->client.get_drag_handler = NULL;
nb->client.get_find_handler = NULL;
nb->client.get_focus_handler = NULL;
nb->client.get_geolocation_handler = NULL;
nb->client.get_jsdialog_handler = NULL;
nb->client.get_keyboard_handler = NULL;
nb->client.get_life_span_handler = browser_get_life_span_handler;
nb->client.get_load_handler = NULL;
nb->client.get_render_handler = browser_get_render_handler;
nb->client.get_request_handler = browser_get_request_handler; nb->client.get_request_handler = browser_get_request_handler;
nb->client.on_process_message_received = browser_on_process_message_received; nb->client.on_process_message_received = browser_on_process_message_received;
// nb->render_handler.get_accessibility_handler = NULL;
nb->render_handler.get_root_screen_rect = NULL;
nb->render_handler.get_view_rect = browser_get_view_rect; nb->render_handler.get_view_rect = browser_get_view_rect;
nb->render_handler.get_screen_point = NULL;
nb->render_handler.get_screen_info = NULL;
nb->render_handler.on_popup_show = NULL;
nb->render_handler.on_popup_size = NULL;
nb->render_handler.on_paint = browser_on_paint; nb->render_handler.on_paint = browser_on_paint;
nb->display_handler.on_console_message = browser_on_console_message; nb->render_handler.on_cursor_change = NULL;
nb->display_handler.on_title_change = browser_on_title_change; nb->render_handler.start_dragging = NULL;
nb->render_handler.update_drag_cursor = NULL;
nb->render_handler.on_scroll_offset_changed = NULL;
// nb->render_handler.on_ime_composition_range_changed = NULL;
nb->display_handler.on_address_change = browser_on_address_change; nb->display_handler.on_address_change = browser_on_address_change;
nb->display_handler.on_title_change = browser_on_title_change;
nb->display_handler.on_favicon_urlchange = browser_on_favicon_urlchange;
nb->display_handler.on_fullscreen_mode_change = browser_on_fullscreenmode_change;
nb->display_handler.on_tooltip = browser_on_tooltip; nb->display_handler.on_tooltip = browser_on_tooltip;
nb->display_handler.on_status_message = browser_on_status_message; nb->display_handler.on_status_message = browser_on_status_message;
nb->display_handler.on_console_message = browser_on_console_message;
nb->request_handler.on_before_browse = browser_on_before_browse; nb->request_handler.on_before_browse = browser_on_before_browse;
nb->request_handler.on_open_urlfrom_tab = NULL;
nb->request_handler.on_before_resource_load = NULL;
nb->request_handler.get_resource_handler = NULL;
nb->request_handler.on_resource_redirect = NULL;
nb->request_handler.on_resource_response = NULL;
nb->request_handler.get_resource_response_filter = NULL;
nb->request_handler.on_resource_load_complete = NULL;
nb->request_handler.get_auth_credentials = NULL;
nb->request_handler.on_quota_request = NULL;
nb->request_handler.on_protocol_execution = NULL; //FIXME: should implement.
nb->request_handler.on_certificate_error = NULL;
// nb->request_handler.on_select_client_certificate = NULL; //we have no such certs
nb->request_handler.on_plugin_crashed = NULL;
nb->request_handler.on_render_view_ready = NULL;
nb->request_handler.on_render_process_terminated = browser_on_render_process_terminated; nb->request_handler.on_render_process_terminated = browser_on_render_process_terminated;
nb->life_span_handler.on_before_popup = NULL;
nb->life_span_handler.on_after_created = NULL;
nb->life_span_handler.do_close = NULL;
nb->life_span_handler.on_before_close = browser_on_before_close; nb->life_span_handler.on_before_close = browser_on_before_close;
nb->context_menu_handler.on_before_context_menu = browser_on_before_context_menu;
nb->context_menu_handler.run_context_menu = NULL; //fixme: implement a working context menu somehow
nb->context_menu_handler.on_context_menu_command = NULL; //for custom context things, like opening in a new window...
nb->context_menu_handler.on_context_menu_dismissed = NULL; //
nb->desiredwidth = 640; nb->desiredwidth = 640;
nb->desiredheight = 480; nb->desiredheight = 480;
@ -766,9 +921,14 @@ static cef_render_process_handler_t* CEF_CALLBACK app_get_render_process_handler
static void CEF_CALLBACK app_on_register_custom_schemes(struct _cef_app_t* self, cef_scheme_registrar_t* registrar) static void CEF_CALLBACK app_on_register_custom_schemes(struct _cef_app_t* self, cef_scheme_registrar_t* registrar)
{ {
cef_string_t fte = makecefstring("fte"); cef_string_t fte = makecefstring("fte");
registrar->add_custom_scheme(registrar, &fte, false, true, true registrar->add_custom_scheme(registrar, &fte
, /*is_standard*/true
, /*is_local*/false
, /*is_display_isolated*/true
#if CEF_COMMIT_NUMBER >= 1658 //not sure what number it is. #if CEF_COMMIT_NUMBER >= 1658 //not sure what number it is.
, true, true, false , /*is_secure*/true
, /*is_cors_enabled*/true //display_isolated means that we shouldn't be getting requests from http anyway, so we don't need CORS protection.
, /*is_csp_bypassing*/false
#endif #endif
); );
cef_string_clear(&fte); cef_string_clear(&fte);
@ -926,7 +1086,7 @@ static int CEF_CALLBACK fsfunc_execute(cef_v8handler_t* self, const cef_string_t
// key.length = wcslen(key.str); // key.length = wcslen(key.str);
// *exception = makecefstring("SOME KIND OF EXCEPTION!"); // *exception = makecefstring("SOME KIND OF EXCEPTION!");
*retval = makev8string("OH LOOK! A STRING!"); *retval = makev8string("");
if (argumentsCount) if (argumentsCount)
{ {
@ -955,6 +1115,7 @@ static int CEF_CALLBACK fsfunc_execute(cef_v8handler_t* self, const cef_string_t
browser->send_process_message(browser, PID_BROWSER, msg); browser->send_process_message(browser, PID_BROWSER, msg);
if (setting)
cef_string_userfree_free(setting); cef_string_userfree_free(setting);
} }
else else
@ -1011,7 +1172,7 @@ typedef struct
size_t offset; size_t offset;
size_t datasize; size_t datasize;
unsigned int resultcode; unsigned int resultcode;
cef_string_t mimetype; char *responseheaders;
} fteresource_t; } fteresource_t;
static void CEF_CALLBACK resource_handler_addref(cef_base_t* self) static void CEF_CALLBACK resource_handler_addref(cef_base_t* self)
{ {
@ -1027,29 +1188,73 @@ static int CEF_CALLBACK resource_handler_release(cef_base_t* self)
VFS_CLOSE(rh->fh); VFS_CLOSE(rh->fh);
if (rh->data) if (rh->data)
free(rh->data); free(rh->data);
cef_string_clear(&rh->mimetype); free(rh->responseheaders);
free(rh); free(rh);
return true; return true;
} }
return false; return false;
} }
static void res_catfield_l(char **const orig, const char *key, int kl, const char *val, int vl)
{
size_t ol = *orig?strlen(*orig):0;
char *n;
if (ol)
{
n = malloc(ol+1+kl+1+vl+1);
memcpy(n, *orig, ol);
n[ol++] = '\n';
}
else
n = malloc(kl+1+vl+1);
memcpy(n+ol, key, kl);
ol+=kl;
n[ol++] = '\n';
memcpy(n+ol, val, vl);
ol+=vl;
n[ol++] = 0;
free(*orig);
*orig = n;
}
static void res_catfield(char **const orig, const char *key, const char *val)
{
res_catfield_l(orig, key, strlen(key), val, strlen(val));
}
static void res_catfield_csuf(char **const orig, const char *key, cef_string_userfree_t cs)
{
cef_string_utf8_t u8 = {NULL};
cef_string_to_utf8(cs->str, cs->length, &u8);
res_catfield_l(orig, key, strlen(key), u8.str, u8.length);
cef_string_utf8_clear(&u8);
}
static void res_catfield_cs(char **const orig, cef_string_t *key, cef_string_t *val)
{
cef_string_utf8_t keyu8 = {NULL};
cef_string_utf8_t valu8 = {NULL};
cef_string_to_utf8(key->str, key->length, &keyu8);
cef_string_to_utf8(val->str, val->length, &valu8);
res_catfield_l(orig, keyu8.str, keyu8.length, valu8.str, valu8.length);
cef_string_utf8_clear(&keyu8);
cef_string_utf8_clear(&valu8);
}
static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t* self, cef_request_t* request, cef_callback_t* callback) static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t* self, cef_request_t* request, cef_callback_t* callback)
{ {
fteresource_t *rh = (fteresource_t*)((char*)self - offsetof(fteresource_t, rh)); fteresource_t *rh = (fteresource_t*)((char*)self - offsetof(fteresource_t, rh));
cef_string_userfree_t url = request->get_url(request); cef_string_userfree_t url = request->get_url(request), method;
cef_string_utf8_t u8_url = {NULL}; cef_post_data_t *postdata;
size_t numelements;
cef_post_data_element_t *elements[1];
size_t postsize;
char *postbytes;
char *q;
char *e;
cef_string_utf8_t u8_url = {NULL}, u8={NULL};
cef_string_t ext = {NULL}; cef_string_t ext = {NULL};
cef_string_to_utf8(url->str, url->length, &u8_url); cef_string_to_utf8(url->str, url->length, &u8_url);
rh->resultcode = 404; rh->resultcode = 404;
// cef_string_userfree_t method = request->get_method(request);
// _cef_post_data_t postdata = request->get_post_data(request);
//hack at the url to hide the //hack at the url to hide the
{ q = strchr(u8_url.str, '?');
char *q = strchr(u8_url.str, '?');
char *e;
if (q) if (q)
*q = 0; *q = 0;
for(e = q?q:u8_url.str+strlen(u8_url.str); e > u8_url.str; ) for(e = q?q:u8_url.str+strlen(u8_url.str); e > u8_url.str; )
@ -1064,7 +1269,11 @@ static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t*
break; break;
} }
} }
}
res_catfield(&rh->responseheaders, "Access-Control-Allow-Origin", "fte://data");
res_catfield(&rh->responseheaders, "Access-Control-Allow-Origin", "fte://csqc");
res_catfield(&rh->responseheaders, "Access-Control-Allow-Origin", "fte://ssqc");
res_catfield(&rh->responseheaders, "Access-Control-Allow-Origin", "fte://menu");
//sandboxed to the same dir that qc can fopen/fwrite. //sandboxed to the same dir that qc can fopen/fwrite.
//(also blocks any fancy http:// parsing that an engine might do) //(also blocks any fancy http:// parsing that an engine might do)
@ -1074,14 +1283,17 @@ static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t*
if (rh->fh) if (rh->fh)
{ {
cef_string_userfree_t mt = cef_get_mime_type(&ext); cef_string_userfree_t mt = cef_get_mime_type(&ext);
cef_string_copy(mt->str, mt->length, &rh->mimetype); if (mt)
{
res_catfield_csuf(&rh->responseheaders, "Content-Type", mt);
cef_string_userfree_free(mt); cef_string_userfree_free(mt);
}
rh->resultcode = 200; rh->resultcode = 200;
} }
else else
{ {
rh->resultcode = 404; rh->resultcode = 404;
setcefstring("text/html", &rh->mimetype); res_catfield(&rh->responseheaders, "Content-Type", "text/html");
rh->data = strdup("<html><style type=\"text/css\">body {background-color: lightblue;}</style><title>not found</title>File not found within game filesystem.</html>"); rh->data = strdup("<html><style type=\"text/css\">body {background-color: lightblue;}</style><title>not found</title>File not found within game filesystem.</html>");
rh->datasize = strlen(rh->data); rh->datasize = strlen(rh->data);
} }
@ -1090,16 +1302,21 @@ static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t*
{ {
struct pubprogfuncs_s *progs; struct pubprogfuncs_s *progs;
const char *page; const char *page;
const char *respheaders = NULL;
const char *reqheaders = NULL;
if (ext.str) if (ext.str)
{ {
cef_string_userfree_t mt = cef_get_mime_type(&ext); cef_string_userfree_t mt = cef_get_mime_type(&ext);
if (mt) if (mt)
{ {
cef_string_copy(mt->str, mt->length, &rh->mimetype); res_catfield_csuf((char**)&respheaders, "Content-Type", mt);
cef_string_userfree_free(mt); cef_string_userfree_free(mt);
} }
} }
if(q)
*q = '?'; //put it back so the qc can get the full url.
rh->resultcode = 404; rh->resultcode = 404;
if (!BUILTINISVALID(PR_GetVMInstance)) if (!BUILTINISVALID(PR_GetVMInstance))
@ -1120,13 +1337,58 @@ static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t*
{ {
void *pr_globals = PR_globals(progs, PR_CURRENT); void *pr_globals = PR_globals(progs, PR_CURRENT);
((string_t *)pr_globals)[OFS_PARM0] = progs->TempString(progs, u8_url.str+11); ((string_t *)pr_globals)[OFS_PARM0] = progs->TempString(progs, u8_url.str+11);
((string_t *)pr_globals)[OFS_PARM1] = 0;//FIXME: method. PR_TempString(csqcprogs, ""));
((string_t *)pr_globals)[OFS_PARM2] = 0;//FIXME: post data method = request->get_method(request);
cef_string_to_utf8(method->str, method->length, &u8);
((string_t *)pr_globals)[OFS_PARM1] = progs->TempString(progs, u8.str);
cef_string_userfree_free(method);
cef_string_utf8_clear(&u8);
postdata = request->get_post_data(request);
if (postdata)
{
numelements = countof(elements);
memset(elements, 0, sizeof(elements));
postdata->get_elements(postdata, &numelements, elements);
postsize = elements[0]->get_bytes_count(elements[0]);
postbytes = malloc(postsize+1);
elements[0]->get_bytes(elements[0], postsize, postbytes);
postbytes[postsize] = 0;
((string_t *)pr_globals)[OFS_PARM2] = progs->TempString(progs, postbytes);
free(postbytes);
cef_release(elements[0]);
}
else
((string_t *)pr_globals)[OFS_PARM2] = 0;
{
size_t i, elems;
cef_string_t key = {NULL}, val = {NULL};
cef_string_multimap_t hmap = cef_string_multimap_alloc();
request->get_header_map(request, hmap);
elems = cef_string_multimap_size(hmap);
for (i = 0; i < elems; i++)
{
cef_string_multimap_key(hmap, i, &key);
cef_string_multimap_key(hmap, i, &val);
res_catfield_cs(&rh->responseheaders, &key, &val);
cef_string_clear(&key);
cef_string_clear(&val);
}
cef_string_multimap_free(hmap);
}
((string_t *)pr_globals)[OFS_PARM3] = reqheaders?progs->TempString(progs, reqheaders):0; //request heders
((string_t *)pr_globals)[OFS_PARM4] = rh->responseheaders?progs->TempString(progs, rh->responseheaders):0; //response headers
((string_t *)pr_globals)[OFS_PARM5] = 0;
((string_t *)pr_globals)[OFS_PARM6] = 0;
((string_t *)pr_globals)[OFS_PARM7] = 0;
progs->ExecuteProgram(progs, func); progs->ExecuteProgram(progs, func);
if (((string_t *)pr_globals)[OFS_RETURN]) if (((string_t *)pr_globals)[OFS_RETURN])
{ {
page = progs->StringToNative(progs, ((string_t *)pr_globals)[OFS_RETURN]); page = progs->StringToNative(progs, ((string_t *)pr_globals)[OFS_RETURN]);
respheaders = progs->StringToNative(progs, ((string_t *)pr_globals)[OFS_PARM4]);
rh->resultcode = 200; rh->resultcode = 200;
} }
else else
@ -1138,6 +1400,9 @@ static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t*
else else
page = "<html><style type=\"text/css\">body {background-color: lightblue;}</style><title>not found</title>That QCVM is not running</html>"; page = "<html><style type=\"text/css\">body {background-color: lightblue;}</style><title>not found</title>That QCVM is not running</html>";
if (*respheaders == '\n')
respheaders++;
rh->responseheaders = strdup(respheaders);
//FIXME: only return any data if we were successful OR the mime is text/html //FIXME: only return any data if we were successful OR the mime is text/html
rh->data = strdup(page); rh->data = strdup(page);
rh->datasize = strlen(rh->data); rh->datasize = strlen(rh->data);
@ -1145,7 +1410,7 @@ static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t*
else else
{ {
rh->resultcode = 403; rh->resultcode = 403;
setcefstring("text/html", &rh->mimetype); res_catfield(&rh->responseheaders, "Content-Type", "text/html");
rh->data = strdup("<html><style type=\"text/css\">body {background-color: lightblue;}</style><title>forbidden</title><a href=\"fte://data/index.html\">Try here</a> <a href=\"fte://csqc/index.html\">Or try here</a></html>"); rh->data = strdup("<html><style type=\"text/css\">body {background-color: lightblue;}</style><title>forbidden</title><a href=\"fte://data/index.html\">Try here</a> <a href=\"fte://csqc/index.html\">Or try here</a></html>");
rh->datasize = strlen(rh->data); rh->datasize = strlen(rh->data);
} }
@ -1158,9 +1423,24 @@ static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t*
cef_release(request); cef_release(request);
return 1; //failure is reported as an http error code rather than an exception return 1; //failure is reported as an http error code rather than an exception
} }
static char *strseps(char *str, char *chars)
{ //find the next separator
char *best = str+strlen(str);
char *c;
if (*str)
while(*chars)
{
c = strchr(str, *chars++);
if (c && c < best)
best = c;
}
return best;
}
static void CEF_CALLBACK resource_handler_get_response_headers(cef_resource_handler_t* self, cef_response_t* response, int64* response_length, cef_string_t* redirectUrl) static void CEF_CALLBACK resource_handler_get_response_headers(cef_resource_handler_t* self, cef_response_t* response, int64* response_length, cef_string_t* redirectUrl)
{ {
fteresource_t *rh = (fteresource_t*)((char*)self - offsetof(fteresource_t, rh)); fteresource_t *rh = (fteresource_t*)((char*)self - offsetof(fteresource_t, rh));
cef_string_multimap_t *hmap;
cef_string_t key = {NULL}, val={NULL};
if (rh->fh) if (rh->fh)
*response_length = VFS_GETLEN(rh->fh); *response_length = VFS_GETLEN(rh->fh);
@ -1169,9 +1449,38 @@ static void CEF_CALLBACK resource_handler_get_response_headers(cef_resource_hand
else else
*response_length = -1; *response_length = -1;
response->set_mime_type(response, &rh->mimetype); hmap = cef_string_multimap_alloc();
if (rh->responseheaders)
{
char *start;
char *sep;
char *nl;
for (start = rh->responseheaders; *start; )
{
sep = strseps(start, ":\n");
nl = strseps(sep+1, "\n");
cef_string_from_utf8(start, sep-start, &key);
if (*sep)
sep++;
cef_string_from_utf8(sep, nl-sep, &val);
cef_string_multimap_append(hmap, &key, &val);
if (*nl)
start = nl+1;
else
break;
}
}
cef_string_multimap_append(hmap, makecefstringptr("Access-Control-Allow-Origin", &key), makecefstringptr("fte://data", &val));
response->set_header_map(response, hmap);
// response->set_mime_type(response, &rh->mimetype);
response->set_status(response, rh->resultcode); response->set_status(response, rh->resultcode);
cef_string_clear(&key);
cef_string_clear(&val);
cef_release(response); cef_release(response);
} }
static int CEF_CALLBACK resource_handler_read_response(cef_resource_handler_t* self, void* data_out, int bytes_to_read, int* bytes_read, cef_callback_t* callback) static int CEF_CALLBACK resource_handler_read_response(cef_resource_handler_t* self, void* data_out, int bytes_to_read, int* bytes_read, cef_callback_t* callback)
@ -1226,7 +1535,10 @@ static cef_resource_handler_t* CEF_CALLBACK scheme_handler_factory_create(cef_sc
rh->rh.process_request = resource_handler_process_request; rh->rh.process_request = resource_handler_process_request;
rh->rh.get_response_headers = resource_handler_get_response_headers; rh->rh.get_response_headers = resource_handler_get_response_headers;
rh->rh.read_response = resource_handler_read_response; rh->rh.read_response = resource_handler_read_response;
rh->rh.can_get_cookie = NULL;
rh->rh.can_set_cookie = NULL;
rh->rh.cancel = resource_handler_cancel; rh->rh.cancel = resource_handler_cancel;
cef_addref(&rh->rh); cef_addref(&rh->rh);
cef_release(browser); cef_release(browser);
@ -1294,7 +1606,10 @@ cef_request_context_t *Cef_GetRequestContext(void)
return ret; return ret;
} }
static void *Cef_Create(const char *name) static qboolean Cef_Init(qboolean engineprocess);
struct mediacallbacks_s; //todo...
static void *Cef_Create(const char *name, struct mediacallbacks_s *callbacks)
{ {
cef_window_info_t window_info = {0}; cef_window_info_t window_info = {0};
cef_browser_settings_t browserSettings = {sizeof(browserSettings)}; cef_browser_settings_t browserSettings = {sizeof(browserSettings)};
@ -1323,6 +1638,9 @@ static void *Cef_Create(const char *name)
cef_main_args_t mainargs = {0}; cef_main_args_t mainargs = {0};
cef_settings_t settings = {sizeof(settings)}; cef_settings_t settings = {sizeof(settings)};
if (!Cef_Init(true))
return NULL;
//const char *ua = "FTEBrowser"; //const char *ua = "FTEBrowser";
//cef_string_from_utf8(ua, strlen(ua), &settings.user_agent); //cef_string_from_utf8(ua, strlen(ua), &settings.user_agent);
@ -1331,8 +1649,11 @@ static void *Cef_Create(const char *name)
if (pFS_NativePath("cef_debug.log", FS_ROOT, utf8, sizeof(utf8))) if (pFS_NativePath("cef_debug.log", FS_ROOT, utf8, sizeof(utf8)))
cef_string_from_utf8(utf8, strlen(utf8), &settings.log_file); cef_string_from_utf8(utf8, strlen(utf8), &settings.log_file);
// CefString(&settings.resources_dir_path).FromASCII(""); if (pFS_NativePath("", FS_BINARYPATH, utf8, sizeof(utf8)))
// CefString(&settings.locales_dir_path).FromASCII(""); cef_string_from_utf8(utf8, strlen(utf8), &settings.resources_dir_path);
if (pFS_NativePath("locales", FS_BINARYPATH, utf8, sizeof(utf8)))
cef_string_from_utf8(utf8, strlen(utf8), &settings.locales_dir_path);
#ifdef _WIN32 #ifdef _WIN32
{ {
@ -1348,7 +1669,7 @@ static void *Cef_Create(const char *name)
#else #else
settings.log_severity = LOGSEVERITY_DISABLE; settings.log_severity = LOGSEVERITY_DISABLE;
#endif #endif
settings.background_color = 0xffffffff; settings.background_color = 0x00ffffff;
// settings.single_process = true; // settings.single_process = true;
#ifdef _WIN32 #ifdef _WIN32
// settings.multi_threaded_message_loop = true; //fixme: use this. // settings.multi_threaded_message_loop = true; //fixme: use this.
@ -1388,7 +1709,7 @@ static void *Cef_Create(const char *name)
// browserSettings.file_access_from_file_urls = STATE_DISABLED; // browserSettings.file_access_from_file_urls = STATE_DISABLED;
browserSettings.remote_fonts = STATE_DISABLED; browserSettings.remote_fonts = STATE_DISABLED;
browserSettings.plugins = STATE_DISABLED; browserSettings.plugins = STATE_DISABLED;
browserSettings.background_color = 0xffffffff; browserSettings.background_color = 0x00ffffff;
window_info.windowless_rendering_enabled = true; window_info.windowless_rendering_enabled = true;
memset(&window_info.parent_window, 0, sizeof(window_info.parent_window)); memset(&window_info.parent_window, 0, sizeof(window_info.parent_window));
@ -1453,6 +1774,12 @@ static void *Cef_Create(const char *name)
return (void*)newbrowser; return (void*)newbrowser;
} }
static void *Cef_CreateOld(const char *name)
{
return Cef_Create(name, NULL);
}
static qboolean VARGS Cef_DisplayFrame(void *ctx, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ectx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ectx) static qboolean VARGS Cef_DisplayFrame(void *ctx, qboolean nosound, qboolean forcevideo, double mediatime, void (QDECL *uploadtexture)(void *ectx, uploadfmt_t fmt, int width, int height, void *data, void *palette), void *ectx)
{ {
browser_t *browser = (browser_t*)ctx; browser_t *browser = (browser_t*)ctx;
@ -1731,6 +2058,8 @@ qboolean VARGS Cef_GetProperty (void *ctx, const char *field, char *out, size_t
ret = browser->currenttitle.str; ret = browser->currenttitle.str;
else if (!strcmp(field, "status")) else if (!strcmp(field, "status"))
ret = browser->currentstatus.str; ret = browser->currentstatus.str;
else if (!strcmp(field, "icon"))
ret = browser->currenticon.str;
if (ret) if (ret)
{ {
@ -1869,6 +2198,14 @@ static qboolean Cef_Init(qboolean engineprocess)
{(void **)&cef_v8context_get_current_context, "cef_v8context_get_current_context"}, {(void **)&cef_v8context_get_current_context, "cef_v8context_get_current_context"},
{(void **)&cef_post_task, "cef_post_task"}, {(void **)&cef_post_task, "cef_post_task"},
{(void **)&cef_request_context_create_context, "cef_request_context_create_context"}, {(void **)&cef_request_context_create_context, "cef_request_context_create_context"},
{(void **)&cef_string_multimap_alloc, "cef_string_multimap_alloc"},
{(void **)&cef_string_multimap_append, "cef_string_multimap_append"},
{(void **)&cef_string_multimap_size, "cef_string_multimap_size"},
{(void **)&cef_string_multimap_key, "cef_string_multimap_key"},
{(void **)&cef_string_multimap_value, "cef_string_multimap_value"},
{(void **)&cef_string_multimap_free, "cef_string_multimap_free"},
{(void **)&cef_string_list_size, "cef_string_list_size"},
{(void **)&cef_string_list_value, "cef_string_list_value"},
{NULL} {NULL}
}; };
if (!Sys_LoadLibrary("libcef", ceffuncs)) if (!Sys_LoadLibrary("libcef", ceffuncs))
@ -1965,7 +2302,8 @@ qintptr_t Plug_Init(qintptr_t *args)
decoderfuncs.structsize = sizeof(media_decoder_funcs_t); decoderfuncs.structsize = sizeof(media_decoder_funcs_t);
decoderfuncs.drivername = "cef"; decoderfuncs.drivername = "cef";
decoderfuncs.createdecoder = Cef_Create; decoderfuncs.createdecoder = Cef_CreateOld;
// decoderfuncs.createdecoderCB = Cef_Create;
decoderfuncs.decodeframe = Cef_DisplayFrame; decoderfuncs.decodeframe = Cef_DisplayFrame;
decoderfuncs.shutdown = Cef_Destroy; decoderfuncs.shutdown = Cef_Destroy;
decoderfuncs.cursormove = Cef_CursorMove; decoderfuncs.cursormove = Cef_CursorMove;
@ -1981,9 +2319,6 @@ qintptr_t Plug_Init(qintptr_t *args)
return false; return false;
} }
if (!Cef_Init(true))
return false;
if (Plug_Export("ExecuteCommand", Cef_ExecuteCommand)) if (Plug_Export("ExecuteCommand", Cef_ExecuteCommand))
pCmd_AddCommand("cef"); pCmd_AddCommand("cef");

File diff suppressed because it is too large Load diff

View file

@ -357,7 +357,7 @@ static qboolean JCL_JingleSend(jclient_t *jcl, struct c2c_s *c2c, char *action)
if (!strcmp(action, "session-initiate")) if (!strcmp(action, "session-initiate"))
{ //these attributes are meant to only be present in initiate. for call forwarding etc. which we don't properly support. { //these attributes are meant to only be present in initiate. for call forwarding etc. which we don't properly support.
XML_AddParameter(jingle, "initiator", jcl->jid); XML_AddParameter(jingle, "initiator", jcl->fulljid);
} }
if (!strcmp(action, "session-terminate")) if (!strcmp(action, "session-terminate"))
@ -392,7 +392,7 @@ static qboolean JCL_JingleSend(jclient_t *jcl, struct c2c_s *c2c, char *action)
if (!strcmp(action, "session-accept")) if (!strcmp(action, "session-accept"))
{ {
if (c2c->content[c].method == transportmode) if (c2c->content[c].method == transportmode)
XML_AddParameter(jingle, "responder", jcl->jid); XML_AddParameter(jingle, "responder", jcl->fulljid);
else else
action = "transport-replace"; action = "transport-replace";
} }

View file

@ -62,7 +62,7 @@ void XMPP_FT_Frame(jclient_t *jcl)
{ {
//server has accepted us, woo. //server has accepted us, woo.
//sid+requester(them)+target(us) //sid+requester(them)+target(us)
req = va("%s%s%s", ft->sid, ft->with, jcl->jid); req = va("%s%s%s", ft->sid, ft->with, jcl->fulljid);
SHA1(digest, sizeof(digest), req, strlen(req)); SHA1(digest, sizeof(digest), req, strlen(req));
//in hex //in hex
for (req = domain, j=0; j < 20; j++) for (req = domain, j=0; j < 20; j++)

View file

@ -631,6 +631,8 @@ void XML_ConPrintTree(xmltree_t *t, char *subconsole, int indent)
{ {
int start, c, chunk; int start, c, chunk;
struct buf_ctx buf = {NULL, 0, 0}; struct buf_ctx buf = {NULL, 0, 0};
if (!t)
return;
XML_DumpToBuf(&buf, t, indent); XML_DumpToBuf(&buf, t, indent);
buf_cat(&buf, "", 1); buf_cat(&buf, "", 1);

View file

@ -129,18 +129,13 @@ typedef struct jclient_s
int serverport; int serverport;
char domain[256]; char domain[256];
char username[256]; char username[256];
char password[256];
char resource[256]; char resource[256];
char certificatedomain[256]; char certificatedomain[256];
int forcetls; //-1=off, 0=ifpossible, 1=fail if can't upgrade, 2=old-style tls int forcetls; //-1=off, 0=ifpossible, 1=fail if can't upgrade, 2=old-style tls
qboolean savepassword; qboolean savepassword;
qboolean allowauth_plainnontls; //allow plain plain
qboolean allowauth_plaintls; //allow tls plain
qboolean allowauth_digestmd5; //allow digest-md5 auth
qboolean allowauth_scramsha1; //allow scram-sha-1 auth
qboolean allowauth_oauth2; //use oauth2 where possible
char jid[256]; //this is our full username@domain/resource string char fulljid[256]; //this is our full username@domain/resource string
char barejid[256]; //this is our bare username@domain string
char localalias[256];//this is what's shown infront of outgoing messages. >> by default until we can get our name. char localalias[256];//this is what's shown infront of outgoing messages. >> by default until we can get our name.
char vcardphotohash[20]; //20-byte sha1 hash. char vcardphotohash[20]; //20-byte sha1 hash.
enum enum
@ -151,9 +146,6 @@ typedef struct jclient_s
} vcardphotohashstatus; } vcardphotohashstatus;
qboolean vcardphotohashchanged; //forces a presence send. qboolean vcardphotohashchanged; //forces a presence send.
char authnonce[256];
int authmode;
int instreampos; int instreampos;
qboolean connecting; //still waiting for intial stream tag qboolean connecting; //still waiting for intial stream tag
@ -166,6 +158,42 @@ typedef struct jclient_s
char curquakeserver[2048]; char curquakeserver[2048];
char defaultnamespace[2048]; //should be 'jabber:client' or blank (and spammy with all the extra xmlns attribs) char defaultnamespace[2048]; //should be 'jabber:client' or blank (and spammy with all the extra xmlns attribs)
struct sasl_ctx_s
{
char *username; //might be different from the account name, but probably isn't.
char *domain; //might be different from the account domain, but probably isn't.
qboolean issecure; //tls enabled (either upgraded or initially)
int socket;
//this stuff should be saved
char password_plain[256]; //plain password. scrubbed if we auth using a hashed auth.
qbyte password_hash[256]; //safer password, not encrypted, but stored hashed.
size_t password_hash_size;
char password_validity[256]; //internal string used to check that the salt was unchanged
struct saslmethod_s *authmethod; //null name = oauth2->saslmethod
qboolean allowauth_plainnontls; //allow plain plain
qboolean allowauth_plaintls; //allow tls plain
qboolean allowauth_digestmd5; //allow digest-md5 auth
qboolean allowauth_scramsha1; //allow scram-sha-1 auth
qboolean allowauth_oauth2; //use oauth2 where possible
struct
{
char authnonce[256];
} digest;
struct
{
qboolean plus;
char authnonce[256];
char authvhash[20];
char authcbindtype[20];
char authcbinding[256];
hashfunc_t *hashfunc;
size_t hashsize;
} scram;
struct struct
{ {
char saslmethod[64]; char saslmethod[64];
@ -179,6 +207,7 @@ typedef struct jclient_s
char *refreshtoken; //long-term token that we can use to get new access tokens char *refreshtoken; //long-term token that we can use to get new access tokens
char *authtoken; //short-term authorisation token, usable to get an access token (and a refresh token if we're lucky) char *authtoken; //short-term authorisation token, usable to get an access token (and a refresh token if we're lucky)
} oauth2; } oauth2;
} sasl;
struct iq_s struct iq_s
{ {

View file

@ -315,6 +315,9 @@ BUILTIN(void, Net_Close, (qhandle_t socket));
#define ARGNAMES ,sock,certhostname #define ARGNAMES ,sock,certhostname
BUILTINR(int, Net_SetTLSClient, (qhandle_t sock, const char *certhostname)); BUILTINR(int, Net_SetTLSClient, (qhandle_t sock, const char *certhostname));
#undef ARGNAMES #undef ARGNAMES
#define ARGNAMES ,sock,outdata,datalen
BUILTINR(int, Net_GetTLSBinding, (qhandle_t sock, char *outdata, int *datalen));
#undef ARGNAMES
#define ARGNAMES ,inputbuffer,buffersize #define ARGNAMES ,inputbuffer,buffersize
BUILTINR(int, ReadInputBuffer, (void *inputbuffer, int buffersize)); BUILTINR(int, ReadInputBuffer, (void *inputbuffer, int buffersize));
@ -468,6 +471,7 @@ void Plug_InitStandardBuiltins(void)
CHECKBUILTIN(Net_Send); CHECKBUILTIN(Net_Send);
CHECKBUILTIN(Net_Close); CHECKBUILTIN(Net_Close);
CHECKBUILTIN(Net_SetTLSClient); CHECKBUILTIN(Net_SetTLSClient);
CHECKBUILTIN(Net_GetTLSBinding);
//random things //random things
CHECKBUILTIN(CL_GetStats); CHECKBUILTIN(CL_GetStats);

View file

@ -357,6 +357,7 @@ EBUILTIN(int, Net_Recv, (qhandle_t socket, void *buffer, int len));
EBUILTIN(int, Net_Send, (qhandle_t socket, void *buffer, int len)); EBUILTIN(int, Net_Send, (qhandle_t socket, void *buffer, int len));
EBUILTIN(void, Net_Close, (qhandle_t socket)); EBUILTIN(void, Net_Close, (qhandle_t socket));
EBUILTIN(int, Net_SetTLSClient, (qhandle_t sock, const char *certhostname)); EBUILTIN(int, Net_SetTLSClient, (qhandle_t sock, const char *certhostname));
EBUILTIN(int, Net_GetTLSBinding, (qhandle_t sock, char *outdata, int *datalen));
#define N_WOULDBLOCK 0 #define N_WOULDBLOCK 0
#define NET_CLIENTPORT -1 #define NET_CLIENTPORT -1
#define NET_SERVERPORT -2 #define NET_SERVERPORT -2

View file

@ -37,16 +37,36 @@ scalefactor typically needs to be explicitly set to 1. this value affects how th
texture <TEXTURENAME> texture <TEXTURENAME>
specifies to use an image named TEXTURENAME for this effect. specifies to use an image named TEXTURENAME for this effect.
shader [shadername] [\n{\nshaderbody\n}\n]
Specifies to use a named shader.
If shaderbody is included (as a nested block on the following line) then that shader text will be used to create the shader if it doesn't otherwise exist.
This overrides blendmodes, as no default shader will need to be created.
tcoords <s1> <t1> <s2> <t2> [tscale] [rsmax] [rsstep] tcoords <s1> <t1> <s2> <t2> [tscale] [rsmax] [rsstep]
specifies to use a subsection of the image. specifies to use a subsection of the image.
if tscale is set, all units are divided by this. it is the virtual size of your texture. So a value of 1 means that your texture coords must be between 0 and 1. But if it properly matches your texture's size, the coords are in pixels. if tscale is set, all units are divided by this. it is the virtual size of your texture. So a value of 1 means that your texture coords must be between 0 and 1. But if it properly matches your texture's size, the coords are in pixels.
if rsmax is present, each particle will use a random image. These images must be on a single row in your particle font. if rsmax is present, each particle will use a random image. These images must be on a single row in your particle font.
rsstep specifies the stride (gap from one to the next) in your particle font, and is only needed if rsmax is present and greater than 1. rsstep specifies the stride (gap from one to the next) in your particle font, and is only needed if rsmax is present and greater than 1.
atlas count_in_each_axis firstidx [last]
An alternative to tcoords.
The specified texture (or shader) is to be considered as a grid of sprites (x*x, where x is specified in that first arg).
firstidx specifies the first image to use (horizontal THEN vertical).
last specifies the last image to use (inclusive). The engine will pick one at random. They should not span multiple rows.
rotation <startmin> <startmax> <speedmin> <speedmax>
the particle will start with a rotation rotated between startmin and startmax.
It will then rotate with some per-particle value randomly picked between the speedmin+speedmax values.
Should NOT be used on beam particles.
rotationstart <min> [max] rotationstart <min> [max]
obsolete, see rotation.
the particle will start with a rotation rotated between min and max. the particle will start with a rotation rotated between min and max.
if max is missing, the particle will always start with the min value. if max is missing, the particle will always start with the min value.
rotationspeed <min> [max]
obsolete, see rotation.
beamtexstep <value> beamtexstep <value>
only valid if the effect is a beam. only valid if the effect is a beam.
specifies the number of quake units per beam texture repitition. specifies the number of quake units per beam texture repitition.
@ -69,6 +89,10 @@ scalefactor <frac>
1 makes the particle scale the same as anything else 1 makes the particle scale the same as anything else
0 makes the particle not change size no matter how far it is 0 makes the particle not change size no matter how far it is
stretchfactor <factor>
controls how spark particles stretch according to their velocity.
negative values give fixed length sparks.
scaledelta <value> scaledelta <value>
controls how the particle scales over time controls how the particle scales over time
specifies the change in the particle scale per second. specifies the change in the particle scale per second.
@ -84,6 +108,9 @@ count <min> <max>
alpha <value> alpha <value>
specifies the initial alpha value of the effect specifies the initial alpha value of the effect
alpharand <value>
specifies a randomized additonal value added to each particle's initial alpha.
alphadelta <value> alphadelta <value>
specifies how much the alpha value of the effect changes per second (subtracted) specifies how much the alpha value of the effect changes per second (subtracted)
@ -96,7 +123,7 @@ diesubrand <value>
basically the particle will live up to this much less time. the alpha value will also be aged by the amount chosen by this value basically the particle will live up to this much less time. the alpha value will also be aged by the amount chosen by this value
randomvel <horiz> [vert] randomvel <horiz> [vert]
controls how fast the particle moves when it spawns. This works regardless of any requested velocities. controls how fast the particle moves when it spawns (according to its spawn pattern). This works regardless of any requested velocities.
if vert is not specified, horiz is used instead. if vert is not specified, horiz is used instead.
veladd <value> veladd <value>
@ -105,6 +132,20 @@ veladd <value>
orgadd <value> orgadd <value>
biases how much to add to the starting origin relative to the requested velocity. biases how much to add to the starting origin relative to the requested velocity.
orgbias <x> <y> <z>
biases the particle's origin by this absolute worldspace vector, regardless of spawn mode.
velbias
biases the particle's velocity by this absolute worldspace vector, regardless of spawn mode.
orgwrand
randomised offset for the particle's origin, in worldspace.
velwrand
randomised offset for the particle's origin, in worldspace.
friction <<xyz>|<xy> <z> | <x> <y> <z>> friction <<xyz>|<xy> <z> | <x> <y> <z>>
Proportion of the particle's speed that should be lost from friction. Negative values are accepted. Proportion of the particle's speed that should be lost from friction. Negative values are accepted.
@ -135,7 +176,7 @@ inwater <effectname>
Specifies a replacement effect to use when this one is spawned underwater. Specifies a replacement effect to use when this one is spawned underwater.
assoc used is the replacement effect. the assoc value from the replaced effect is ignored (this includes +foo chains). assoc used is the replacement effect. the assoc value from the replaced effect is ignored (this includes +foo chains).
overwater [content names] notunderwater [content names]
Specifies that this particle should ONLY be spawned when out of water. Specifies that this particle should ONLY be spawned when out of water.
The particle will not spawn under water (this does not affect assoc chains). The particle will not spawn under water (this does not affect assoc chains).
Content names are a space-separated list of: water slime lava sky solid fluid. Default is fluid if not specified. Content names are a space-separated list of: water slime lava sky solid fluid. Default is fluid if not specified.
@ -163,19 +204,24 @@ green <value>
blue <value> blue <value>
rgb <red> <green> <blue> rgb <red> <green> <blue>
rgb <value> rgb <value>
rgbf <red> <green> <blue>
Specifies the initial red, green, and/or blue values for each particle. Specifies the initial red, green, and/or blue values for each particle.
Fully opaque is 255 or above. Fully opaque is 255 or above.
Values above 255 are valid, but will remain opaque until the value drops below 255 from the colour deltas. Values above 255 are valid, but will remain opaque until the value drops below 255 from the
colour deltas.
The rgbf form takes values scaled by 1 instead of 255.
redrand <value> redrand <value>
greenrand <value> greenrand <value>
bluerand <value> bluerand <value>
rgbrand <red> <green> <blue> rgbrand <red> <green> <blue>
rgbrand <value> rgbrand <value>
rgbrandf <red> <green> <blue>
Specifies how much extra red, green, and/or blue there may be for particles. Specifies how much extra red, green, and/or blue there may be for particles.
The initial colour will be multiplied by this amount before addition. The initial colour will be multiplied by this amount before addition.
Each componant is separately randomized. EG, red might add nothing, while the full green is added, and only half the blue. Each componant is separately randomized. EG, red might add nothing, while the full green is added, and only half the blue.
Fully opaque is 255 or above. Fully opaque is 255 or above.
The rgbrandf form takes values scaled by 1 instead of 255.
redrandsync <value> redrandsync <value>
greenrandsync <value> greenrandsync <value>
@ -192,6 +238,7 @@ greendelta <value>
bluedelta <value> bluedelta <value>
rgbdelta <red> <green> <blue> rgbdelta <red> <green> <blue>
rgbdelta <value> rgbdelta <value>
rgbdeltaf <red> <green> <blue>
Specifies how much the red, green, and/or blue values of each particle change over time. Specifies how much the red, green, and/or blue values of each particle change over time.
The value 255 is the value required to go from opaque to invisible in 1 second. The value 255 is the value required to go from opaque to invisible in 1 second.
@ -201,7 +248,8 @@ rgbdeltatime <value>
rampmode <mode> rampmode <mode>
mode may be one of: mode may be one of:
none: uses rgb+rand+sync+delta+scale+scaledelta values. none: uses rgb+rand+sync+delta+scale+scaledelta values.
absolute: the ramp overrides all colour+scale values. The effect moves from one absolute ramp index to the next. nearest(or absolute): the ramp overrides all colour+scale values. The effect moves from one absolute ramp index to the next.
lerp: smoothly interpolates from one value to the next.
delta: uses rgb+rand+sync+scale, but not delta values. All delta values come from the colour ramp instead. delta: uses rgb+rand+sync+scale, but not delta values. All delta values come from the colour ramp instead.
if not none, the ramp index used is based upon the particle's age, its lifetime, and how many ramp elements there are. if not none, the ramp index used is based upon the particle's age, its lifetime, and how many ramp elements there are.
@ -221,8 +269,20 @@ stains <value>
The stained colour is based upon the colour of the particle upon impact. The stained colour is based upon the colour of the particle upon impact.
blend <mode> blend <mode>
mode may be one of: add, subtract, blendcolour/blendcolor, blend If the texture used is actually a shader then that shader's blend mode will take precidence.
if the texture used is actually a shader, this is ignored. As a general rule you should try to use only the premul blend modes (as well as atlasing).
mode may be one of:
adda:
addc:
subtract:
invmoda:
invmodc:
blendcolour:
blendalpha:
premul_subtract:
premul_add:
premul_blend:
rtsmoke:
spawnmode <mode> [arg1] [arg2] spawnmode <mode> [arg1] [arg2]
This affects how particles are positioned when they first spawn, and their initial velocities. This affects how particles are positioned when they first spawn, and their initial velocities.
@ -254,6 +314,7 @@ spawnparam2 <value>
obsolete. see spawnmode. obsolete. see spawnmode.
up <value> up <value>
obsoleted by orgbias.
the particle's starting origin is moved upwards by this amount (worldspace). the particle's starting origin is moved upwards by this amount (worldspace).
type <mode> type <mode>
@ -265,12 +326,17 @@ type <mode>
texturedspark: textured particles are aligned along their direction of movement, their length depending upon their speed, width equal to their scale. texturedspark: textured particles are aligned along their direction of movement, their length depending upon their speed, width equal to their scale.
cdecal/decal: particles are spawned only upon bsp geometry. They are clipped by it. cdecal/decal: particles are spawned only upon bsp geometry. They are clipped by it.
udecal: unclipped decal. exact semantics are subject to change. udecal: unclipped decal. exact semantics are subject to change.
*default*: Particles are regular, rotating, 2d images. normal/*default*: Particles are regular, rotating, 2d images.
isbeam isbeam
obsolete. obsolete.
please use 'type beam' instead. please use 'type beam' instead.
clippeddecal <mask> [match]
implies 'type decal'.
The two extra args allow you to spawn these decals ONLY on surfaces with matching surfaceflags.
Separation of mask+match allows you to create many descrete surface types instead of being limited to 32 bits/types.
spawntime <value> spawntime <value>
spawnchance <value> spawnchance <value>
@ -316,20 +382,69 @@ sound <soundname> <vol> <attenuation> [pitch] [delay]
When the effect is first spawned, the named sound will play with the given volume and attenuation at the center point. This may not work with all spawn methods, as it will ignore any count scales. When the effect is first spawned, the named sound will play with the given volume and attenuation at the center point. This may not work with all spawn methods, as it will ignore any count scales.
lightradius <radius> lightradius <radius>
lightradiusfade <radiuspersecond>
lightrgb <r> <g> <b>
lightrgbfade <r/s> <g/s> <b/s>
lighttime <maxage>
Spawns a dlight when the effect is spawned. Spawns a dlight when the effect is spawned.
dlight is removed when radius drops to 0 or the age is exceeded. dlight is removed when radius drops to 0 or the age is exceeded.
at this time it is not possible to override the corona/specular levels. at this time it is not possible to override the corona/specular levels.
lightradiusfade <radiuspersecond>
how fast the light radius shrinks per second
lightrgb <r> <g> <b>
dlight rgb colours. 1=white. higher values can over-saturate.
lightrgbfade <r/s> <g/s> <b/s>
how fast lightrgb changes over time.
lighttime <maxage>
Specifies the maximum lifetime of your dlight.
lightcubemap <cubemapnum> lightcubemap <cubemapnum>
value 0 means no cubemap. value 0 means no cubemap.
otherwise with eg cubemap 5, uses image files cubemaps/5ft.tga, cubemaps/5bk.tga, etc. otherwise with eg cubemap 5, uses image files cubemaps/5ft.tga, cubemaps/5bk.tga, etc.
fixme: at the current time, the cubemap is world-aligned and cannot rotate. fixme: at the current time, the cubemap is world-aligned and cannot rotate.
model <name> <firstframe> <endframe> <framerate> <alpha> lightscales <ambient> <diffuse> <specular>
multipliers for the rtlight's various types of lighting
lightshadows <castshadows>
0 or 1, specifies whether the rtlight will cast shadows or not. Its faster if it doesn't.
lightcorona <intensity> <scale>
model <name> [options]
options are:
frame=
framestart=
framecount=
frameend=
frames=
framerate=
skin=
alpha=
scalemin=
scalemax=
trail=
orient
additive
transparent
fullbright
shadow
noshadow
spawns sprites or models that fly away with random angles and run through some frame sequence. handy for simple gib effects. spawns sprites or models that fly away with random angles and run through some frame sequence. handy for simple gib effects.
sound <name> [options]
options are:
vol=
attn=
pitch=
delay=
weight=
Plays a sound when the effect is spawned. Only ONE sound will be used, picked randomly from the included sounds according to their weights.
spawnstain <radius> <r> <g> <b> spawnstain <radius> <r> <g> <b>
rainfrequency <multiplier>
Specifies the interval between spawning new particle puffs on surfaces.
flurry <magnitude>
These particles will periodically all change their direction, in a vauge attempt to approximate snow flurries.