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 .)
endif
SVNREVISION:=-DSVNREVISION=$(shell test -d $(BASE_DIR)/../.svn && svnversion $(BASE_DIR) || echo -)
ifeq ($(SVNREVISION),)
SVNREVISION:=-DSVNREVISION=$(shell test -d $(BASE_DIR)/../.svn && svnversion $(BASE_DIR) || echo -)
endif
MAKE:=$(MAKE) SVNREVISION=$(SVNREVISION)
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)
ifdef windir
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`
SV_LDFLAGS=`$(SDLCONFIG) --static-libs`
else
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`
endif
GL_CFLAGS=-DFTE_SDL $(GLCFLAGS) `$(SDLCONFIG) --cflags`
@ -887,6 +892,9 @@ GLB_DIR=gl_$(FTE_FULLTARGET)
GLCL_DIR=glcl_$(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_EXE_NAME=../$(EXE_NAME)-sv$(FTE_FULLTARGET)
SV_CFLAGS=-DFTE_SDL `$(SDLCONFIG) --cflags` $(SERVER_ONLY_CFLAGS)
@ -904,6 +912,7 @@ QCC_DIR=qcc$(BITS)
ifeq (,$(findstring NO_ZLIB,$(CFLAGS)))
SV_LDFLAGS+=-lz
GL_LDFLAGS+=-lz
VK_LDFLAGS+=-lz
M_LDFLAGS+=-lz
QCC_LDFLAGS+=-L$(ARCHLIBS) -lz
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)
ifdef windir
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`
SV_LDFLAGS=$(MINGW_LIBS_DIR)/libz.a -lm -lmingw32 -lws2_32 -lwinmm `$(SDLCONFIG) --static-libs`
QCC_LDFLAGS=$(MINGW_LIBS_DIR)/libz.a
else
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`
QCC_LDFLAGS=$(MINGW_LIBS_DIR)/libz.a
endif
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)
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)
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)
M_CFLAGS=$(D3DCFLAGS) -DFTE_SDL -I$(LIBS_DIR) -I$(MINGW_LIBS_DIR)/ -I$(MINGW_LIBS_DIR) $(GLCFLAGS) $(LIBVORBISFILE_STATIC) -D_MERGED_SDL $(DX7SDK) $(SPEEXCFLAGS)
ifeq ($(shell echo $(FTE_TARGET)|grep -E -i -v "win32.*sdl"),)
M_CFLAGS+= -D_MINGW_VFPRINTF
endif
M_LDFLAGS=$(GLLDFLAGS) $(IMAGELDFLAGS) $(OGGVORBISLDFLAGS)
#with d3d...
#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)
#without d3d...
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)
M_CFLAGS=$(VKCFLAGS) $(GLCFLAGS) -DFTE_SDL -I$(LIBS_DIR) -I$(LIBS_DIR) -I$(MINGW_LIBS_DIR)/ -I$(MINGW_LIBS_DIR) $(LIBVORBISFILE_STATIC) $(DX7SDK) $(SPEEXCFLAGS)
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)
D3DCL_EXE_NAME=../$(EXE_NAME)-sdl-d3dcl$(BITS)$(EXEPOSTFIX)
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)
ifeq ($(shell echo $(FTE_TARGET)|grep -E -i -v "win32.*sdl"),)
D3D_CFLAGS+= -D_MINGW_VFPRINTF
endif
D3DB_DIR=sdl_d3d_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)
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)
ifeq ($(shell echo $(FTE_TARGET)|grep -E -i -v "win32.*sdl"),)
VK_CFLAGS+= -D_MINGW_VFPRINTF
endif
VKB_DIR=sdl_vk_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
#FTE_TARGET=vc (Visual C)

View File

@ -867,7 +867,9 @@ void CLFTE_ParseEntities(void)
qboolean isvalid = false;
qboolean removeflag;
int inputframe = cls.netchan.incoming_sequence;
#if defined(QUAKESTATS) || defined(NQPROT)
int i;
#endif
// int 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
if (!CLHL_BuildUserInput(msecstouse, &independantphysics[0]))
#endif
for (plnum = 0; plnum < cl.splitclients; plnum++)
for (plnum = 0; plnum < (cl.splitclients?cl.splitclients:1); plnum++)
{
vec3_t mousemovements;
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)
cl_pendingcmd[plnum].msec = 13;
}
msecstouse = cl_pendingcmd[0].msec;
//the main loop isn't allowed to send
if (runningindepphys && mainloop)
@ -1945,9 +1946,6 @@ void CL_SendCmd (double frametime, qboolean mainloop)
if (!fullsend)
return; // when we're actually playing we try to match netfps exactly to avoid gameplay problems
if (msecstouse == 12)
msecstouse = 13;
// if (msecstouse > 127)
// 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_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_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.");
@ -452,7 +452,7 @@ void CL_SupportedFTEExtensions(int *pext1, int *pext2)
}
#endif
char *CL_GUIDString(netadr_t *adr)
char *CL_GUIDString(netadr_t *adr, const char *guidstring)
{
static qbyte buf[2048];
static int buflen;
@ -461,7 +461,16 @@ char *CL_GUIDString(netadr_t *adr)
void *blocks[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;
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[1] = serveraddr;lens[1] = strlen(serveraddr);
Com_BlocksChecksum(2, blocks, lens, (void*)digest);
@ -522,8 +526,9 @@ void CL_SendConnectPacket (netadr_t *to, int mtu,
#ifdef PROTOCOL_VERSION_FTE
int ftepext, int ftepext2,
#endif
int compressioncrc
/*, ...*/) //dmw new parms
int compressioncrc,
const char *guidhash
/*, ...*/)
{
extern cvar_t qport;
netadr_t addr;
@ -683,7 +688,7 @@ void CL_SendConnectPacket (netadr_t *to, int mtu,
#endif
connectinfo.compresscrc = 0;
info = CL_GUIDString(to);
info = CL_GUIDString(to, guidhash);
if (info)
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;
char *host;
extern int r_blockvidrestart;
const char *lbp;
#ifndef CLIENTONLY
if (!cls.state && (!connectinfo.trying || sv.state != ss_clustermode) && sv.state)
{
const char *lbp;
#ifdef NQPROT
qboolean proquakeangles = false;
#endif
#ifdef NETPREPARSE
extern cvar_t dpcompat_nopreparse;
#endif
extern cvar_t sv_guidhash;
memset(&connectinfo, 0, sizeof(connectinfo));
Q_strncpyz (cls.servername, "internalserver", sizeof(cls.servername));
Cvar_ForceSet(&cl_servername, cls.servername);
@ -945,7 +951,7 @@ void CL_CheckForResend (void)
{
if (!connectinfo.challenge)
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;
@ -976,7 +982,11 @@ void CL_CheckForResend (void)
if (connectinfo.time && realtime - connectinfo.time < 5.0)
return;
NET_InitClient(false);
#ifdef HAVE_DTLS
if (connectinfo.dtlsupgrade != DTLS_ACTIVE)
#endif
//FIXME: this is switching ports far too much
NET_InitClient(false);
t1 = Sys_DoubleTime ();
if (!connectinfo.istransfer)
@ -2833,6 +2843,7 @@ void CL_ConnectionlessPacket (void)
#ifdef HAVE_DTLS
qboolean candtls = false;
#endif
char guidhash[256];
s = MSG_ReadString ();
COM_Parse(s);
@ -2867,7 +2878,7 @@ void CL_ConnectionlessPacket (void)
connectinfo.protocol = CP_QUAKE3;
connectinfo.challenge = atoi(s+17);
CL_SendConnectPacket (&net_from, 0, 0, 0, 0/*, ...*/);
CL_SendConnectPacket (&net_from, 0, 0, 0, 0/*, ...*/, NULL);
}
else
{
@ -2993,6 +3004,17 @@ void CL_ConnectionlessPacket (void)
c = MSG_ReadLong();/*ident*/
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:
MSG_ReadSkip(len); /*payload*/
break;
@ -3007,11 +3029,12 @@ void CL_ConnectionlessPacket (void)
case PROTOCOL_VERSION_FTE2: pext2 = l; break;
case PROTOCOL_VERSION_FRAGMENT: mtu = l; break;
#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
#ifdef HUFFNETWORK
case PROTOCOL_VERSION_HUFFMAN: huffcrc = l; break;
#endif
case PROTOCOL_INFO_GUID: Q_snprintfz(guidhash, sizeof(guidhash), "0x%x", l); break;
default:
break;
}
@ -3041,7 +3064,7 @@ void CL_ConnectionlessPacket (void)
}
#endif
CL_SendConnectPacket (&net_from, mtu, pext, pext2, huffcrc/*, ...*/);
CL_SendConnectPacket (&net_from, mtu, pext, pext2, huffcrc/*, ...*/, guidhash);
return;
}
#ifdef Q2CLIENT

View File

@ -35,10 +35,12 @@ static char *CLNQ_ParseProQuakeMessage (char *s);
static void DLC_Poll(qdownload_t *dl);
static void CL_ProcessUserInfo (int slot, player_info_t *player);
#ifdef NQPROT
static char cl_dp_csqc_progsname[128];
static int cl_dp_csqc_progssize;
static int cl_dp_csqc_progscrc;
static int cl_dp_serverextension_download;
#endif
#ifdef AVAIL_ZLIB
#ifndef ZEXPORT
@ -178,6 +180,7 @@ static char *svc_qwstrings[] =
"???",
};
#ifdef NQPROT
static char *svc_nqstrings[] =
{
"nqsvc_bad",
@ -278,6 +281,7 @@ static char *svc_nqstrings[] =
"NEW PROTOCOL(87)", //87
"NEW PROTOCOL(88)" //88
};
#endif
extern cvar_t requiredownloads, cl_standardchat, msg_filter, msg_filter_frags, msg_filter_pickups, cl_countpendingpl, cl_download_mapsrc;
int oldparsecountmod;
@ -2650,6 +2654,7 @@ qboolean CL_ParseOOBDownload(void)
return true;
}
#ifdef NQPROT
static void CLDP_ParseDownloadData(void)
{
qdownload_t *dl = cls.download;
@ -2781,6 +2786,7 @@ static void CLDP_ParseDownloadFinished(char *s)
CL_RequestNextDownload ();
}
#endif
static vfsfile_t *upload_file;
static qbyte *upload_data;
@ -2870,6 +2876,7 @@ void CL_StopUpload(void)
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)
{
if (!COM_CheckParm("-fileul"))
@ -2897,6 +2904,7 @@ static qboolean CL_StartUploadFile(char *filename)
}
return false;
}
#endif
/*
=====================================================================
@ -2905,9 +2913,6 @@ static qboolean CL_StartUploadFile(char *filename)
=====================================================================
*/
#ifdef CLIENTONLY
static float nextdemotime;
#endif
void CL_ClearParseState(void)
{
@ -4340,6 +4345,7 @@ static void CL_ParseBaselineDelta (void)
memcpy(cl_baselines + es.number, &es, sizeof(es));
}
#ifdef Q2CLIENT
static void CLQ2_Precache_f (void)
{
Model_CheckDownloads();
@ -4353,6 +4359,7 @@ static void CLQ2_Precache_f (void)
CG_Start();
#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);
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))
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))
{
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);
}
@ -1118,6 +1118,10 @@ static qintptr_t VARGS Plug_Con_GetConsoleString(void *offset, quintptr_t mask,
{
Q_strncpyz(value, con->title, size);
}
else if (!strcmp(attrib, "icon"))
{
Q_strncpyz(value, con->icon, size);
}
else if (!strcmp(attrib, "prompt"))
{
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);
else if (!strcmp(attrib, "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"))
Q_strncpyz(con->prompt, value, sizeof(con->prompt));
else if (!strcmp(attrib, "backimage"))

View File

@ -2275,7 +2275,7 @@ int Image_WritePNG (char *filename, enum fs_relative fsroot, int compression, vo
#endif
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;
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.
rgb_buffer += bytestride*(height-1);
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.
header[17] = 0x20;
@ -2330,45 +2330,44 @@ qboolean WriteTGA(char *filename, enum fs_relative fsroot, qbyte *fte_restrict r
{ //can just directly write it
//bgr24, bgra24
c = width*height*opx;
VFS_WRITE(vfs, header, sizeof(header));
VFS_WRITE(vfs, rgb_buffer, c);
}
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.
if (rgb)
{ //rgb24, rgbx32, rgba32
qbyte tmp[3];
// compact in place, and swap
// compact, and swap
c = width*height;
for (i=0 ; i<c ; i++)
{
tmp[2] = rgb_buffer[i*ipx+0];
tmp[1] = rgb_buffer[i*ipx+1];
tmp[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];
rgb_out[i*opx+2] = rgb_buffer[i*ipx+0];
rgb_out[i*opx+1] = rgb_buffer[i*ipx+1];
rgb_out[i*opx+0] = rgb_buffer[i*ipx+2];
}
}
else
{ //(bgr24), bgrx32, (bgra32)
qbyte tmp[3];
// compact in place
// compact
c = width*height;
for (i=0 ; i<c ; i++)
{
tmp[0] = rgb_buffer[i*ipx+0];
tmp[1] = rgb_buffer[i*ipx+1];
tmp[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];
rgb_out[i*opx+0] = rgb_buffer[i*ipx+0];
rgb_out[i*opx+1] = rgb_buffer[i*ipx+1];
rgb_out[i*opx+2] = rgb_buffer[i*ipx+2];
}
}
c *= opx;
VFS_WRITE(vfs, header, sizeof(header));
VFS_WRITE(vfs, rgb_out, c);
free(rgb_out);
}
VFS_WRITE(vfs, header, sizeof(header));
VFS_WRITE(vfs, rgb_buffer, c);
VFS_CLOSE(vfs);
}
return true;

View File

@ -1409,6 +1409,7 @@ void CSQC_CvarChanged(cvar_t *var);
#define CSQC_UnconnectedOkay(inprinciple) false
#define CSQC_UnconnectedInit() false
#define CSQC_UseGamecodeLoadingScreen() false
#define CSQC_Parse_SetAngles(seat,newangles,wasdelta) false
#endif
//
@ -1659,6 +1660,19 @@ void Stats_Clear(void);
void Stats_Init(void);
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
{
size_t structsize;
@ -1676,6 +1690,8 @@ typedef struct
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
// void *(VARGS *createdecoderCB)(const char *name, struct mediacallbacks_s *callbacks);
} media_decoder_funcs_t;
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;
}
}
//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;
}
@ -2348,6 +2356,7 @@ void Con_DrawConsole (int lines, qboolean noback)
if (Key_Dest_Has(kdm_cwindows))
{
int top = 8; //padding at the top
if (con_curwindow==w)
R2D_ImageColours(0.0, 0.05, 0.1, 0.8);
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_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)
{
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);
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
cin_t *cin = R_ShaderGetCinematic(shader);
if (cin)
@ -2372,8 +2388,8 @@ void Con_DrawConsole (int lines, qboolean noback)
{
float x = 0;
// 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 *buttoncmds[] = {"cmd:back", "cmd:forward", "cmd:refresh", ENGINEWEBSITE, NULL};
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, NULL};
float tw;
int i, fl;
@ -2382,6 +2398,14 @@ void Con_DrawConsole (int lines, qboolean noback)
if (i == countof(buttons)-1)
tw = ~0u;
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;
else
tw = 32;
@ -2407,22 +2431,38 @@ void Con_DrawConsole (int lines, qboolean noback)
x += tw;
}
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);
Media_Send_MouseMove(cin, (w->mousecursor[0]) / (w->wnd_w-16.0), (w->mousecursor[1]-top) / (w->wnd_h-8.0-top));
//convert these to pixels.
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)
Media_Send_Command(cin, "cmd:focus");
else
Media_Send_Command(cin, "cmd:unfocus");
}
#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;
}
else

View File

@ -35,7 +35,7 @@ char *r_defaultimageextensions =
;
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_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_picmip2d;
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);
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_size_t (PNGAPI *qpng_get_rowbytes) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_rowbytes);
#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);
#endif
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_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_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);
#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);
#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);
@ -2050,12 +2054,6 @@ qbyte *ReadPCXPalette(qbyte *buf, int len, qbyte *out)
typedef struct bmpheader_s
{
/* unsigned short Type;*/
unsigned int Size;
unsigned short Reserved1;
unsigned short Reserved2;
unsigned int OffsetofBMPBits;
unsigned int SizeofBITMAPINFOHEADER;
signed int Width;
signed int Height;
@ -2079,20 +2077,13 @@ typedef struct bmpheaderv4_s
unsigned int Gamma[3];
} 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;
bmpheader_t h;
qbyte *data;
if (buf[0] != 'B' || buf[1] != 'M')
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);
memcpy(&h, (bmpheader_t *)buf, sizeof(h));
h.SizeofBITMAPINFOHEADER = LittleLong(h.SizeofBITMAPINFOHEADER);
h.Width = LittleLong(h.Width);
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)?
return NULL;
if (length < h.Size)
return NULL; //truncated...
if (!OffsetofBMPBits)
h.Height /= 2; //icons are weird.
*width = h.Width;
*height = h.Height;
@ -2125,15 +2117,18 @@ qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height)
if (h.Width&1)
return NULL;
data = buf+2;
data = buf;
data += sizeof(h);
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);
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]&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;
@ -2158,7 +2168,7 @@ qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height)
if (h.NumofColorIndices>256)
return NULL;
data = buf+2;
data = buf;
data += sizeof(h);
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);
}
buf += h.OffsetofBMPBits;
if (OffsetofBMPBits)
buf += OffsetofBMPBits;
else
buf += h.SizeofBITMAPINFOHEADER + h.NumofColorIndices*4;
data32 = BZ_Malloc(h.Width * h.Height*4);
for (y = 0; y < h.Height; y++)
{
@ -2179,12 +2192,30 @@ qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height)
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;
}
else if (h.BitCount == 24) //24 bit... no 16?
{
int x, y;
buf += h.OffsetofBMPBits;
if (OffsetofBMPBits)
buf += OffsetofBMPBits;
else
buf += h.SizeofBITMAPINFOHEADER;
data = BZ_Malloc(h.Width * h.Height*4);
for (y = 0; y < h.Height; y++)
{
@ -2202,12 +2233,50 @@ qbyte *ReadBMPFile(qbyte *buf, int length, int *width, int *height)
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
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)
{
int y;
@ -2218,6 +2287,7 @@ qboolean WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, int in
int outstride;
int bits = 32;
int extraheadersize = sizeof(h4);
size_t fsize;
memset(&h4, 0, sizeof(h4));
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 = (outstride+3)&~3; //bmp pads rows to a multiple of 4 bytes.
h.Size = 2+sizeof(h)+extraheadersize + outstride*height;
h.Reserved1 = 0;
h.Reserved2 = 0;
h.OffsetofBMPBits = 2+sizeof(h)+extraheadersize; //yes, this is misaligned.
// h.Size = 14+sizeof(h)+extraheadersize + outstride*height;
// h.Reserved1 = 0;
// h.Reserved2 = 0;
// h.OffsetofBMPBits = 2+sizeof(h)+extraheadersize; //yes, this is misaligned.
h.SizeofBITMAPINFOHEADER = (sizeof(h)-12)+extraheadersize;
h.Width = width;
h.Height = height;
@ -2294,14 +2364,38 @@ qboolean WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, int in
in += instride*(height-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++ = '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));
out += sizeof(h);
//v4 header
memcpy(out, &h4, extraheadersize);
out += extraheadersize;
//data
for (y = 0; y < height; y++)
{
memcpy(out, in, width * (bits/8));
@ -2310,12 +2404,69 @@ qboolean WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, int in
in += instride;
}
COM_WriteFile(filename, fsroot, data, h.Size);
COM_WriteFile(filename, fsroot, data, fsize);
BZ_Free(data);
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
@ -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)))
{
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;
}
@ -4220,12 +4377,29 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag
mips->mip[0].data = rgbadata;
else
{
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.
Image_ResampleTexture(rgbadata, imgwidth, imgheight, mips->mip[0].data, mips->mip[0].width, mips->mip[0].height);
if (freedata)
BZ_Free(rgbadata);
freedata = true;
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);
//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);
if (freedata)
BZ_Free(rgbadata);
freedata = true;
break;
default: //scaling not supported...
mips->mip[0].data = rgbadata;
mips->mip[0].width = imgwidth;
mips->mip[0].height = imgheight;
break;
}
}
}
else

View File

@ -3,14 +3,14 @@
#include "quakedef.h"
extern qboolean mouse_active;
static cvar_t m_filter = CVARF("m_filter", "0", CVAR_ARCHIVE);
extern qboolean mouse_active;
static cvar_t m_filter = CVARF("m_filter", "0", CVAR_ARCHIVE);
static cvar_t m_forcewheel = CVARD("m_forcewheel", "1", "0: ignore mousewheels in apis where it is abiguous.\n1: Use mousewheel when it is treated as a third axis. Motion above a threshold is ignored, to avoid issues with an unknown threshold.\n2: Like 1, but excess motion is retained. The threshold specifies exact z-axis distance per notice.");
static cvar_t m_forcewheel_threshold = CVARD("m_forcewheel_threshold", "32", "Mousewheel graduations smaller than this will not trigger mousewheel deltas.");
static cvar_t m_strafeonright = CVARFD("m_strafeonright", "1", CVAR_ARCHIVE, "If 1, touching the right half of the touchscreen will strafe/move, while the left side will turn.");
static cvar_t m_fatpressthreshold = CVARFD("m_fatpressthreshold", "0.2", CVAR_ARCHIVE, "How fat your thumb has to be to register a fat press (touchscreens).");
static cvar_t m_touchmajoraxis = CVARFD("m_touchmajoraxis", "1", CVAR_ARCHIVE, "When using a touchscreen, use only the major axis for strafing.");
static cvar_t m_forcewheel_threshold = CVARD("m_forcewheel_threshold", "32", "Mousewheel graduations smaller than this will not trigger mousewheel deltas.");
static cvar_t m_strafeonright = CVARFD("m_strafeonright", "1", CVAR_ARCHIVE, "If 1, touching the right half of the touchscreen will strafe/move, while the left side will turn.");
static cvar_t m_fatpressthreshold = CVARFD("m_fatpressthreshold", "0.2", CVAR_ARCHIVE, "How fat your thumb has to be to register a fat press (touchscreens).");
static cvar_t m_touchmajoraxis = CVARFD("m_touchmajoraxis", "1", CVAR_ARCHIVE, "When using a touchscreen, use only the major axis for strafing.");
static cvar_t m_slidethreshold = CVARFD("m_slidethreshold", "10", CVAR_ARCHIVE, "How far your finger needs to move to be considered a slide event (touchscreens).");
static cvar_t m_accel = CVARAFD("m_accel", "0", "cl_mouseAccel", CVAR_ARCHIVE, "Values >0 will amplify mouse movement proportional to velocity. Small values have great effect. A lot of good Quake Live players use around the 0.1-0.2 mark, but this depends on your mouse CPI and polling rate.");
@ -44,25 +44,25 @@ void QDECL joyaxiscallback(cvar_t *var, char *oldvalue)
var->ival = 1*sign;
else if (!Q_strcasecmp(end, "back") || !Q_strcasecmp(end, "moveback"))
var->ival = 1*sign*-1;
else if (!Q_strcasecmp(end, "lookup") || !Q_strcasecmp(end, "pitchup"))
var->ival = 2*sign;
else if (!Q_strcasecmp(end, "lookdown") || !Q_strcasecmp(end, "pitchdown"))
var->ival = 2*sign*-1;
else if (!Q_strcasecmp(end, "moveright"))
var->ival = 3*sign;
else if (!Q_strcasecmp(end, "moveleft"))
var->ival = 3*sign*-1;
else if (!Q_strcasecmp(end, "right") || !Q_strcasecmp(end, "turnright"))
var->ival = 4*sign;
else if (!Q_strcasecmp(end, "left") || !Q_strcasecmp(end, "turnleft"))
var->ival = 4*sign*1;
else if (!Q_strcasecmp(end, "up") || !Q_strcasecmp(end, "moveup"))
var->ival = 5*sign;
else if (!Q_strcasecmp(end, "down") || !Q_strcasecmp(end, "movedown"))
var->ival = 5*sign*-1;
else if (!Q_strcasecmp(end, "rollright"))
else if (!Q_strcasecmp(end, "lookup") || !Q_strcasecmp(end, "pitchup"))
var->ival = 2*sign;
else if (!Q_strcasecmp(end, "lookdown") || !Q_strcasecmp(end, "pitchdown"))
var->ival = 2*sign*-1;
else if (!Q_strcasecmp(end, "moveright"))
var->ival = 3*sign;
else if (!Q_strcasecmp(end, "moveleft"))
var->ival = 3*sign*-1;
else if (!Q_strcasecmp(end, "right") || !Q_strcasecmp(end, "turnright"))
var->ival = 4*sign;
else if (!Q_strcasecmp(end, "left") || !Q_strcasecmp(end, "turnleft"))
var->ival = 4*sign*1;
else if (!Q_strcasecmp(end, "up") || !Q_strcasecmp(end, "moveup"))
var->ival = 5*sign;
else if (!Q_strcasecmp(end, "down") || !Q_strcasecmp(end, "movedown"))
var->ival = 5*sign*-1;
else if (!Q_strcasecmp(end, "rollright"))
var->ival = 6*sign;
else if (!Q_strcasecmp(end, "rollleft"))
else if (!Q_strcasecmp(end, "rollleft"))
var->ival = 6*sign*-1;
}
@ -180,175 +180,175 @@ struct mouse_s
float wheeldelta;
int down;
unsigned int updates; //tracks updates per second
} ptr[MAXPOINTERS];
int touchcursor; //the cursor follows whichever finger was most recently pressed in preference to any mouse also on the same system
#define MAXJOYAXIS 6
#define MAXJOYSTICKS 8
struct joy_s
{
unsigned int qdeviceid;
float axis[MAXJOYAXIS];
} joy[MAXJOYSTICKS];
void IN_Shutdown(void)
{
INS_Shutdown();
}
void IN_ReInit(void)
{
int i;
for (i = 0; i < MAXPOINTERS; i++)
{
memset(&ptr[i], 0, sizeof(ptr[i]));
ptr[i].type = M_INVALID;
ptr[i].qdeviceid = i;
}
for (i = 0; i < MAXJOYSTICKS; i++)
{
memset(&joy[i], 0, sizeof(joy[i]));
joy[i].qdeviceid = i;
}
INS_ReInit();
}
struct remapctx
{
char *type;
char *devicename;
unsigned int newdevid;
unsigned int found;
unsigned int failed;
};
static void IN_DeviceIDs_DoRemap(void *vctx, const char *type, const char *devicename, unsigned int *qdevid)
{
struct remapctx *ctx = vctx;
if (!strcmp(ctx->type, type))
if (!strcmp(ctx->devicename, devicename))
{
if (qdevid)
*qdevid = ctx->newdevid;
else
ctx->failed = true;
ctx->found++;
}
}
void IN_DeviceIDs_Enumerate(void *ctx, const char *type, const char *devicename, unsigned int *qdevid)
{
char buf[8192];
devicename = COM_QuotedString(devicename, buf, sizeof(buf), false);
if (!qdevid)
Con_Printf("%s\t%s\t%s\n", type, "N/A", devicename);
else if (*qdevid == DEVID_UNSET)
Con_Printf("%s\t%s\t%s\n", type, "Unset", devicename);
else
Con_Printf("%s\t%u\t%s\n", type, *qdevid, devicename);
}
void IN_DeviceIDs_f(void)
{
struct remapctx ctx;
if (Cmd_Argc() > 3)
{
ctx.failed = false;
ctx.found = 0;
ctx.type = Cmd_Argv(1);
ctx.newdevid = strtoul(Cmd_Argv(2), NULL, 0);
ctx.devicename = Cmd_Argv(3);
INS_EnumerateDevices(&ctx, IN_DeviceIDs_DoRemap);
if (ctx.failed)
Con_Printf("device cannot be remapped\n");
else if (!ctx.found)
Con_Printf("%s \"%s\" not known\n", ctx.type, ctx.devicename);
else if (!cl_warncmd.ival)
Con_Printf("device remapped\n");
}
else
{
Con_Printf("Type\tMapping\tName\n");
INS_EnumerateDevices(NULL, IN_DeviceIDs_Enumerate);
}
}
float IN_DetermineMouseRate(void)
{
float time = Sys_DoubleTime();
static float timer;
static float last;
if (fabs(time - timer) > 1)
{
timer = time;
last = ptr[0].updates;
ptr[0].updates = 0;
}
return last;
}
void IN_Init(void)
{
int i;
} ptr[MAXPOINTERS];
int touchcursor; //the cursor follows whichever finger was most recently pressed in preference to any mouse also on the same system
#define MAXJOYAXIS 6
#define MAXJOYSTICKS 8
struct joy_s
{
unsigned int qdeviceid;
float axis[MAXJOYAXIS];
} joy[MAXJOYSTICKS];
void IN_Shutdown(void)
{
INS_Shutdown();
}
void IN_ReInit(void)
{
int i;
for (i = 0; i < MAXPOINTERS; i++)
{
memset(&ptr[i], 0, sizeof(ptr[i]));
ptr[i].type = M_INVALID;
ptr[i].qdeviceid = i;
}
for (i = 0; i < MAXJOYSTICKS; i++)
{
memset(&joy[i], 0, sizeof(joy[i]));
joy[i].qdeviceid = i;
}
INS_ReInit();
}
struct remapctx
{
char *type;
char *devicename;
unsigned int newdevid;
unsigned int found;
unsigned int failed;
};
static void IN_DeviceIDs_DoRemap(void *vctx, const char *type, const char *devicename, unsigned int *qdevid)
{
struct remapctx *ctx = vctx;
if (!strcmp(ctx->type, type))
if (!strcmp(ctx->devicename, devicename))
{
if (qdevid)
*qdevid = ctx->newdevid;
else
ctx->failed = true;
ctx->found++;
}
}
void IN_DeviceIDs_Enumerate(void *ctx, const char *type, const char *devicename, unsigned int *qdevid)
{
char buf[8192];
devicename = COM_QuotedString(devicename, buf, sizeof(buf), false);
if (!qdevid)
Con_Printf("%s\t%s\t%s\n", type, "N/A", devicename);
else if (*qdevid == DEVID_UNSET)
Con_Printf("%s\t%s\t%s\n", type, "Unset", devicename);
else
Con_Printf("%s\t%u\t%s\n", type, *qdevid, devicename);
}
void IN_DeviceIDs_f(void)
{
struct remapctx ctx;
if (Cmd_Argc() > 3)
{
ctx.failed = false;
ctx.found = 0;
ctx.type = Cmd_Argv(1);
ctx.newdevid = strtoul(Cmd_Argv(2), NULL, 0);
ctx.devicename = Cmd_Argv(3);
INS_EnumerateDevices(&ctx, IN_DeviceIDs_DoRemap);
if (ctx.failed)
Con_Printf("device cannot be remapped\n");
else if (!ctx.found)
Con_Printf("%s \"%s\" not known\n", ctx.type, ctx.devicename);
else if (!cl_warncmd.ival)
Con_Printf("device remapped\n");
}
else
{
Con_Printf("Type\tMapping\tName\n");
INS_EnumerateDevices(NULL, IN_DeviceIDs_Enumerate);
}
}
float IN_DetermineMouseRate(void)
{
float time = Sys_DoubleTime();
static float timer;
static float last;
if (fabs(time - timer) > 1)
{
timer = time;
last = ptr[0].updates;
ptr[0].updates = 0;
}
return last;
}
void IN_Init(void)
{
int i;
events_avail = 0;
events_used = 0;
Cvar_Register (&m_filter, "input controls");
events_used = 0;
Cvar_Register (&m_filter, "input controls");
Cvar_Register (&m_forcewheel, "Input Controls");
Cvar_Register (&m_forcewheel_threshold, "Input Controls");
Cvar_Register (&m_strafeonright, "input controls");
Cvar_Register (&m_fatpressthreshold, "input controls");
Cvar_Register (&m_slidethreshold, "input controls");
Cvar_Register (&m_touchmajoraxis, "input controls");
Cvar_Register (&m_accel, "input controls");
Cvar_Register (&m_accel_style, "input controls");
Cvar_Register (&m_accel_power, "input controls");
Cvar_Register (&m_accel_offset, "input controls");
Cvar_Register (&m_accel_senscap, "input controls");
for (i = 0; i < 6; i++)
{
Cvar_Register (&joy_advaxis[i], "input controls");
Cvar_Register (&joy_advaxisscale[i], "input controls");
Cvar_ForceCallback(&joy_advaxis[i]);
}
for (i = 0; i < 3; i++)
{
Cvar_Register (&joy_anglesens[i], "input controls");
Cvar_Register (&joy_movesens[i], "input controls");
Cvar_Register (&joy_anglethreshold[i], "input controls");
Cvar_Register (&joy_movethreshold[i], "input controls");
}
Cvar_Register (&joy_exponent, "input controls");
Cvar_Register (&joy_radialdeadzone, "input controls");
Cmd_AddCommand ("in_deviceids", IN_DeviceIDs_f);
INS_Init();
}
//tells the keys.c code whether the cursor is currently active, causing mouse clicks instead of binds.
qboolean IN_MouseDevIsTouch(unsigned int devid)
{
if (devid < MAXPOINTERS)
return ptr[devid].type == M_TOUCH;
return false;
}
//there was no ui to click on at least...
//translates MOUSE1 press events into begin-look-or-strafe events.
//translates to MOUSE2 accordingly
//returns 0 if it ate it completely.
int IN_TranslateMButtonPress(unsigned int devid)
{
int ret;
if (!ptr[devid].down)
{
//set the cursor-pressed state, so we begin to look/strafe around
Cvar_Register (&m_forcewheel_threshold, "Input Controls");
Cvar_Register (&m_strafeonright, "input controls");
Cvar_Register (&m_fatpressthreshold, "input controls");
Cvar_Register (&m_slidethreshold, "input controls");
Cvar_Register (&m_touchmajoraxis, "input controls");
Cvar_Register (&m_accel, "input controls");
Cvar_Register (&m_accel_style, "input controls");
Cvar_Register (&m_accel_power, "input controls");
Cvar_Register (&m_accel_offset, "input controls");
Cvar_Register (&m_accel_senscap, "input controls");
for (i = 0; i < 6; i++)
{
Cvar_Register (&joy_advaxis[i], "input controls");
Cvar_Register (&joy_advaxisscale[i], "input controls");
Cvar_ForceCallback(&joy_advaxis[i]);
}
for (i = 0; i < 3; i++)
{
Cvar_Register (&joy_anglesens[i], "input controls");
Cvar_Register (&joy_movesens[i], "input controls");
Cvar_Register (&joy_anglethreshold[i], "input controls");
Cvar_Register (&joy_movethreshold[i], "input controls");
}
Cvar_Register (&joy_exponent, "input controls");
Cvar_Register (&joy_radialdeadzone, "input controls");
Cmd_AddCommand ("in_deviceids", IN_DeviceIDs_f);
INS_Init();
}
//tells the keys.c code whether the cursor is currently active, causing mouse clicks instead of binds.
qboolean IN_MouseDevIsTouch(unsigned int devid)
{
if (devid < MAXPOINTERS)
return ptr[devid].type == M_TOUCH;
return false;
}
//there was no ui to click on at least...
//translates MOUSE1 press events into begin-look-or-strafe events.
//translates to MOUSE2 accordingly
//returns 0 if it ate it completely.
int IN_TranslateMButtonPress(unsigned int devid)
{
int ret;
if (!ptr[devid].down)
{
//set the cursor-pressed state, so we begin to look/strafe around
ptr[devid].down = 1;
ptr[devid].moveddist = 0;
ptr[devid].downpos[0] = ptr[devid].oldpos[0];
@ -362,14 +362,14 @@ int IN_TranslateMButtonPress(unsigned int devid)
//this is the key binding that the press should use
ret = (m_strafeonright.ival && ptr[devid].downpos[0] > vid.pixelwidth/2)?K_MOUSE2:K_MOUSE1;
}
return ret;
}
/*a 'pointer' is either a multitouch pointer, or a separate device
note that mice use the keyboard button api, but separate devices*/
void IN_Commands(void)
{
return ret;
}
/*a 'pointer' is either a multitouch pointer, or a separate device
note that mice use the keyboard button api, but separate devices*/
void IN_Commands(void)
{
struct eventlist_s *ev;
INS_Commands();
@ -513,10 +513,10 @@ void IN_Commands(void)
break;
}
events_used++;
}
}
void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum, float frametime)
}
}
void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum, float frametime)
{
int mx, my;
double mouse_x, mouse_y, mouse_deltadist;
@ -619,38 +619,38 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum, float frame
if (mouse->type == M_TOUCH)
{
if (m_strafeonright.ival && mouse->downpos[0] > vid.pixelwidth/2 && movements != NULL && !Key_Dest_Has(~kdm_game))
{
//if they're strafing, calculate the speed to move at based upon their displacement
if (mouse->down)
{
mx = mouse->oldpos[0] - (vid.pixelwidth*3)/4;
my = mouse->oldpos[1] - (vid.pixelheight*3)/4;
//mx = (mouse->oldpos[0] - mouse->downpos[0])*0.1;
//my = (mouse->oldpos[1] - mouse->downpos[1])*0.1;
}
else
{
mx = 0;
my = 0;
}
if (m_touchmajoraxis.ival)
{
//major axis only
if (abs(mx) > abs(my))
my = 0;
else
mx = 0;
}
strafe_x = true;
strafe_y = true;
}
else
{
strafe_x = false;
if (m_strafeonright.ival && mouse->downpos[0] > vid.pixelwidth/2 && movements != NULL && !Key_Dest_Has(~kdm_game))
{
//if they're strafing, calculate the speed to move at based upon their displacement
if (mouse->down)
{
mx = mouse->oldpos[0] - (vid.pixelwidth*3)/4;
my = mouse->oldpos[1] - (vid.pixelheight*3)/4;
//mx = (mouse->oldpos[0] - mouse->downpos[0])*0.1;
//my = (mouse->oldpos[1] - mouse->downpos[1])*0.1;
}
else
{
mx = 0;
my = 0;
}
if (m_touchmajoraxis.ival)
{
//major axis only
if (abs(mx) > abs(my))
my = 0;
else
mx = 0;
}
strafe_x = true;
strafe_y = true;
}
else
{
strafe_x = false;
strafe_y = false;
//boost sensitivity so that the default works okay.
@ -797,35 +797,35 @@ void IN_MoveMouse(struct mouse_s *mouse, float *movements, int pnum, float frame
else
movements[0] -= m_forward.value * mouse_y;
}
}
//rescales threshold-1 down 0-1
static float joydeadzone(float mag, float deadzone)
{
if (mag > 1) //erg?
mag = 1;
if (mag > deadzone)
{
mag -= deadzone;
mag = mag / (1.f-deadzone);
}
else
mag = 0;
return mag;
}
void IN_MoveJoystick(struct joy_s *joy, float *movements, int pnum, float frametime)
{
float mag;
vec3_t jlook, jstrafe;
}
//rescales threshold-1 down 0-1
static float joydeadzone(float mag, float deadzone)
{
if (mag > 1) //erg?
mag = 1;
if (mag > deadzone)
{
mag -= deadzone;
mag = mag / (1.f-deadzone);
}
else
mag = 0;
return mag;
}
void IN_MoveJoystick(struct joy_s *joy, float *movements, int pnum, float frametime)
{
float mag;
vec3_t jlook, jstrafe;
int wpnum, i;
for (i = 0; i < MAXJOYAXIS; i++)
if (joy->axis[i])
break;
if (i == MAXJOYAXIS)
return;
int wpnum, i;
for (i = 0; i < MAXJOYAXIS; i++)
if (joy->axis[i])
break;
if (i == MAXJOYAXIS)
return;
/*each device will be processed when its player comes to be processed*/
wpnum = cl.splitclients;
if (wpnum < 1)
@ -835,91 +835,91 @@ void IN_MoveJoystick(struct joy_s *joy, float *movements, int pnum, float framet
else
wpnum = joy->qdeviceid % wpnum;
if (wpnum != pnum)
return;
memset(jstrafe, 0, sizeof(jstrafe));
memset(jlook, 0, sizeof(jlook));
for (i = 0; i < 6; i++)
{
int ax = joy_advaxis[i].ival;
switch(ax)
{
default:
case 0: //dead axis
break;
case 1:
case 3:
case 5:
jstrafe[(ax-1)/2] += joy->axis[i] * joy_advaxisscale[i].value;
break;
case -1:
case -3:
case -5:
jstrafe[(-ax-1)/2] -= joy->axis[i] * joy_advaxisscale[i].value;
case 2:
case 4:
case 6:
jlook[(ax-2)/2] += joy->axis[i] * joy_advaxisscale[i].value;
break;
case -2:
case -4:
case -6:
jlook[(-ax-2)/2] -= joy->axis[i] * joy_advaxisscale[i].value;
break;
}
}
//uses a radial deadzone for x+y axis, and separate out the z axis, just because most controllers are 2d affairs with any 3rd axis being a separate knob.
//deadzone values are stolen from microsoft's xinput documentation. they seem quite large to me - I guess that means that xbox controllers are just dodgy imprecise crap with excessive amounts of friction and finger grease.
if (joy_radialdeadzone.ival)
{
mag = joydeadzone(sqrt(jlook[0]*jlook[0] + jlook[1]*jlook[1]), sqrt(joy_anglethreshold[0].value*joy_anglethreshold[0].value + joy_anglethreshold[1].value*joy_anglethreshold[1].value));
mag = pow(mag, joy_exponent.value);
jlook[0] *= mag;
jlook[1] *= mag;
mag = joydeadzone(fabs(jlook[2]), joy_anglethreshold[2].value);
jlook[2] *= mag;
mag = joydeadzone(sqrt(jstrafe[0]*jstrafe[0] + jstrafe[1]*jstrafe[1]), sqrt(joy_movethreshold[0].value*joy_movethreshold[0].value + joy_movethreshold[1].value*joy_movethreshold[1].value));
mag = pow(mag, joy_exponent.value);
jstrafe[0] *= mag;
jstrafe[1] *= mag;
mag = joydeadzone(fabs(jstrafe[2]), joy_movethreshold[2].value);
jstrafe[2] *= mag;
}
else
{
for (i = 0; i < 3; i++)
{
mag = joydeadzone(fabs(jlook[i]), joy_anglethreshold[i].value);
mag = pow(mag, joy_exponent.value);
jlook[i] *= mag;
mag = joydeadzone(fabs(jstrafe[i]), joy_movethreshold[i].value);
mag = pow(mag, joy_exponent.value);
jstrafe[i] *= mag;
}
}
if (Key_Dest_Has(~kdm_game))
{
VectorClear(jlook);
VectorClear(jstrafe);
}
return;
memset(jstrafe, 0, sizeof(jstrafe));
memset(jlook, 0, sizeof(jlook));
for (i = 0; i < 6; i++)
{
int ax = joy_advaxis[i].ival;
switch(ax)
{
default:
case 0: //dead axis
break;
case 1:
case 3:
case 5:
jstrafe[(ax-1)/2] += joy->axis[i] * joy_advaxisscale[i].value;
break;
case -1:
case -3:
case -5:
jstrafe[(-ax-1)/2] -= joy->axis[i] * joy_advaxisscale[i].value;
case 2:
case 4:
case 6:
jlook[(ax-2)/2] += joy->axis[i] * joy_advaxisscale[i].value;
break;
case -2:
case -4:
case -6:
jlook[(-ax-2)/2] -= joy->axis[i] * joy_advaxisscale[i].value;
break;
}
}
//uses a radial deadzone for x+y axis, and separate out the z axis, just because most controllers are 2d affairs with any 3rd axis being a separate knob.
//deadzone values are stolen from microsoft's xinput documentation. they seem quite large to me - I guess that means that xbox controllers are just dodgy imprecise crap with excessive amounts of friction and finger grease.
if (joy_radialdeadzone.ival)
{
mag = joydeadzone(sqrt(jlook[0]*jlook[0] + jlook[1]*jlook[1]), sqrt(joy_anglethreshold[0].value*joy_anglethreshold[0].value + joy_anglethreshold[1].value*joy_anglethreshold[1].value));
mag = pow(mag, joy_exponent.value);
jlook[0] *= mag;
jlook[1] *= mag;
mag = joydeadzone(fabs(jlook[2]), joy_anglethreshold[2].value);
jlook[2] *= mag;
mag = joydeadzone(sqrt(jstrafe[0]*jstrafe[0] + jstrafe[1]*jstrafe[1]), sqrt(joy_movethreshold[0].value*joy_movethreshold[0].value + joy_movethreshold[1].value*joy_movethreshold[1].value));
mag = pow(mag, joy_exponent.value);
jstrafe[0] *= mag;
jstrafe[1] *= mag;
mag = joydeadzone(fabs(jstrafe[2]), joy_movethreshold[2].value);
jstrafe[2] *= mag;
}
else
{
for (i = 0; i < 3; i++)
{
mag = joydeadzone(fabs(jlook[i]), joy_anglethreshold[i].value);
mag = pow(mag, joy_exponent.value);
jlook[i] *= mag;
mag = joydeadzone(fabs(jstrafe[i]), joy_movethreshold[i].value);
mag = pow(mag, joy_exponent.value);
jstrafe[i] *= mag;
}
}
if (Key_Dest_Has(~kdm_game))
{
VectorClear(jlook);
VectorClear(jstrafe);
}
if (in_speed.state[pnum] & 1)
{
VectorScale(jlook, 360*cl_movespeedkey.value, jlook);
VectorScale(jstrafe, 360*cl_movespeedkey.value, jstrafe);
}
VectorScale(jlook, 360*frametime, jlook);
}
VectorScale(jlook, 360*frametime, jlook);
if (!movements) //if this is null, gamecode should still get inputs, just no camera looking or anything.
return;
return;
//angle changes
cl.playerview[pnum].viewanglechange[PITCH] += joy_anglesens[0].value * jlook[0];
cl.playerview[pnum].viewanglechange[YAW] += joy_anglesens[1].value * jlook[1];
@ -928,25 +928,25 @@ void IN_MoveJoystick(struct joy_s *joy, float *movements, int pnum, float framet
if (in_mlook.state[pnum] & 1)
V_StopPitchDrift (&cl.playerview[pnum]);
//movement
//movement
movements[0] += joy_movesens[0].value * cl_forwardspeed.value * jstrafe[0];
movements[1] += joy_movesens[1].value * cl_sidespeed.value * jstrafe[1];
movements[2] += joy_movesens[2].value * cl_upspeed.value * jstrafe[2];
}
void IN_Move (float *movements, int pnum, float frametime)
{
int i;
INS_Move(movements, pnum);
for (i = 0; i < MAXPOINTERS; i++)
IN_MoveMouse(&ptr[i], movements, pnum, frametime);
for (i = 0; i < MAXJOYSTICKS; i++)
IN_MoveJoystick(&joy[i], movements, pnum, frametime);
}
void IN_JoystickAxisEvent(unsigned int devid, int axis, float value)
{
movements[1] += joy_movesens[1].value * cl_sidespeed.value * jstrafe[1];
movements[2] += joy_movesens[2].value * cl_upspeed.value * jstrafe[2];
}
void IN_Move (float *movements, int pnum, float frametime)
{
int i;
INS_Move(movements, pnum);
for (i = 0; i < MAXPOINTERS; i++)
IN_MoveMouse(&ptr[i], movements, pnum, frametime);
for (i = 0; i < MAXJOYSTICKS; i++)
IN_MoveJoystick(&joy[i], movements, pnum, frametime);
}
void IN_JoystickAxisEvent(unsigned int devid, int axis, float value)
{
struct eventlist_s *ev = in_newevent();
if (!ev)
return;
@ -954,8 +954,8 @@ void IN_JoystickAxisEvent(unsigned int devid, int axis, float value)
ev->devid = devid;
ev->joy.axis = axis;
ev->joy.value = value;
in_finishevent();
}
in_finishevent();
}
void IN_KeyEvent(unsigned int devid, int down, int keycode, int unicode)
{

View File

@ -2,6 +2,11 @@
#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
SDL_Window *sdlwindow;
#else
@ -776,15 +781,27 @@ void Sys_SendKeyEvents(void)
default:
break;
case SDL_WINDOWEVENT_SIZE_CHANGED:
#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
#if SDL_VERSION_ATLEAST(2,0,6) && defined(VKQUAKE)
if (qrenderer == QR_VULKAN)
{
extern cvar_t vid_conautoscale, vid_conwidth; //make sure the screen is updated properly.
Cvar_ForceCallback(&vid_conautoscale);
Cvar_ForceCallback(&vid_conwidth);
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
SDL_GL_GetDrawableSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight); //get the proper physical size.
#else
SDL_GetWindowSize(sdlwindow, &vid.pixelwidth, &vid.pixelheight);
#endif
{
extern cvar_t vid_conautoscale, vid_conwidth; //make sure the screen is updated properly.
Cvar_ForceCallback(&vid_conautoscale);
Cvar_ForceCallback(&vid_conwidth);
}
}
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:

View File

@ -187,7 +187,9 @@ static char *manifestpackages; //metapackage named by the manicfest.
static char *declinedpackages; //metapackage named by the manicfest.
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.
#endif
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.
@ -2550,6 +2552,7 @@ void PM_ManifestPackage(const char *metaname, int security)
void PM_Command_f(void)
{
size_t i;
package_t *p;
const char *act = Cmd_Argv(1);
const char *key;
@ -2706,8 +2709,13 @@ void PM_Command_f(void)
{
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"))
{
{ //auto-mark any updated packages.
unsigned int changes = PM_MarkUpdates();
if (changes)
{
@ -2718,7 +2726,7 @@ void PM_Command_f(void)
Con_Printf("Already using latest versions of all packages\n");
}
else if (!strcmp(act, "add") || !strcmp(act, "get") || !strcmp(act, "install") || !strcmp(act, "enable"))
{
{ //FIXME: make sure this updates.
int arg = 2;
for (arg = 2; arg < Cmd_Argc(); arg++)
{
@ -2732,7 +2740,7 @@ void PM_Command_f(void)
PM_PrintChanges();
}
else if (!strcmp(act, "reinstall"))
{
{ //fixme: favour the current verson.
int arg = 2;
for (arg = 2; arg < Cmd_Argc(); arg++)
{
@ -2784,7 +2792,7 @@ void PM_Command_f(void)
PM_PrintChanges();
}
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)

View File

@ -2434,9 +2434,7 @@ cin_t *Media_StartCin(char *name)
if (!cin)
cin = Media_Plugin_TryLoad(name);
#endif
if (!cin)
Con_Printf("Unable to decode \"%s\"\n", name);
else
if (cin)
cin->filmstarttime = realtime;
return cin;
}
@ -2465,7 +2463,6 @@ qboolean Media_BeginNextFilm(void)
}
videoshader = R_RegisterCustom(sname, SUF_NONE, Shader_DefaultCinematic, p->name);
Z_Free(p);
cin = R_ShaderGetCinematic(videoshader);
if (cin)
@ -2474,16 +2471,15 @@ qboolean Media_BeginNextFilm(void)
Media_Send_Reset(cin);
if (cin->changestream)
cin->changestream(cin, "cmd:focus");
return true;
}
else
{
Con_Printf("Unable to play cinematic %s\n", p->name);
R_UnloadShader(videoshader);
videoshader = NULL;
return false;
}
Z_Free(p);
return !!videoshader;
}
qboolean Media_StopFilm(qboolean all)
{
@ -2515,6 +2511,8 @@ qboolean Media_StopFilm(qboolean all)
}
//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)
{
CL_SendClientCommand(true, "nextserver %i", cl.servercount);
@ -2693,7 +2691,7 @@ void Media_Send_KeyEvent(cin_t *cin, int button, int unicode, int event)
cin = R_ShaderGetCinematic(videoshader);
if (!cin)
return;
if (cin->key)
if (cin->key)
cin->key(cin, button, unicode, event);
else if (button == K_SPACE && !event)
{
@ -2819,8 +2817,19 @@ void Media_PlayFilm_f (void)
void Media_PlayVideoWindowed_f (void)
{
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)
return;
con->parseflags = PFS_FORCEUTF8;
@ -2834,7 +2843,7 @@ void Media_PlayVideoWindowed_f (void)
Q_strncpyz(con->backimage, "", sizeof(con->backimage));
if (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);
}
@ -3526,9 +3535,8 @@ void Media_RecordFrame (void)
buffer = qglMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);
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)
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);
}
offscreen_captureframe++;
@ -3536,12 +3544,14 @@ void Media_RecordFrame (void)
frame = captureframe%countof(offscreen_queue);
//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;
if (offscreen_queue[frame].pbo_handle)
qglDeleteBuffersARB(1, &offscreen_queue[frame].pbo_handle);
offscreen_queue[frame].format = offscreen_format;
offscreen_queue[frame].width = vid.pixelwidth;
offscreen_queue[frame].height = vid.pixelheight;
offscreen_queue[frame].width = vid.fbpwidth;
offscreen_queue[frame].height = vid.fbpheight;
switch(offscreen_queue[frame].format)
{
case TF_BGR24:
@ -3556,13 +3566,13 @@ void Media_RecordFrame (void)
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;
qglGenBuffersARB(1, &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
@ -3671,7 +3681,7 @@ static unsigned int MSD_GetDMAPos(soundcardinfo_t *sc)
s = captureframe*(sc->sn.speed*captureframeinterval);
// s >>= (sc->sn.samplebits/8) - 1;
// s >>= sc->sn.samplebytes - 1;
s *= sc->sn.numchannels;
return s;
}
@ -3701,7 +3711,7 @@ static void MSD_Submit(soundcardinfo_t *sc, int start, int end)
if (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));
@ -3759,7 +3769,22 @@ void Media_InitFakeSoundDevice (int speed, int channels, int samplebits)
sc->sn.samples = speed*0.5;
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.numchannels = channels;
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->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));
@ -3801,8 +3826,7 @@ void Media_StopRecordFilm_f (void)
buffer = qglMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);
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, 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);
}
offscreen_captureframe++;
@ -3931,8 +3955,8 @@ static void Media_RecordFilm (char *recordingname, qboolean demo)
capturingfbo = true;
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);
vid.fbpwidth = capturewidth.ival;
vid.fbpheight = captureheight.ival;
vid.fbpwidth = capturetexture->width;
vid.fbpheight = capturetexture->height;
vid.framebuffer = capturetexture;
}
#endif

View File

@ -1376,7 +1376,16 @@ void P_ParticleEffect_f(void)
}
else if (!strcmp(var, "alpha"))
{
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"))
ptype->alpharand = atof(value);
#ifndef NOLEGACY

View File

@ -496,7 +496,7 @@ void QCBUILTIN PF_cs_media_create (pubprogfuncs_t *prinst, struct globalvars_s *
"videomap %s\n"
"rgbgen vertex\n"
"alphagen vertex\n"
"blendfunc blend\n"
"blendfunc gl_one gl_one_minus_src_alpha\n"
"nodepth\n"
"}\n"
"}\n",
@ -537,17 +537,20 @@ void QCBUILTIN PF_cs_media_command (pubprogfuncs_t *prinst, struct globalvars_s
return;
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)
{
const char *shader = PR_GetStringOfs(prinst, OFS_PARM0);
int key = G_FLOAT(OFS_PARM1);
int eventtype = G_FLOAT(OFS_PARM2);
int charcode = (prinst->callargc>3)?G_FLOAT(OFS_PARM3):((key>127)?0:key);
cin_t *cin;
cin = R_ShaderFindCinematic(shader);
G_FLOAT(OFS_RETURN) = 0;
if (!cin)
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
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;
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)
csqcg.trace_endcontentsi = PR_FindGlobal(csqcprogs, "trace_endcontents", 0, &etype);
csqcg.trace_endcontentsi = (int*)PR_FindGlobal(csqcprogs, "trace_endcontents", 0, &etype);
}
#else
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:
*r = r_refdef.grect.width;
if (csqc_isdarkplaces)
*r *= (float)vid.pixelwidth / vid.width;
break;
case VF_SIZE_Y:
*r = r_refdef.grect.height;
if (csqc_isdarkplaces)
*r *= (float)vid.pixelheight / vid.height;
break;
case VF_SIZE:
r[0] = r_refdef.grect.width;
r[1] = r_refdef.grect.height;
r[2] = 0;
if (csqc_isdarkplaces)
{
r[0] *= (float)vid.pixelwidth / vid.width;
r[1] *= (float)vid.pixelheight / vid.height;
}
break;
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.height = p[1];
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;
case VF_MIN_X:
@ -7671,8 +7687,16 @@ qboolean CSQC_DrawView(void)
{
void *pr_globals = PR_globals(csqcprogs, PR_CURRENT);
G_FLOAT(OFS_PARM0) = vid.width;
G_FLOAT(OFS_PARM1) = vid.height;
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_PARM1) = vid.height;
}
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)

View File

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

View File

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

View File

@ -484,7 +484,7 @@ qboolean OpenAL_LoadCache(unsigned int *bufptr, sfxcache_t *sc, float volume)
return true;
}
void OpenAL_CvarInit(void)
static void QDECL OpenAL_CvarInit(void)
{
Cvar_Register(&s_al_debug, 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->selfpainting = true;
sc->sn.sampleformat = QSF_EXTERNALMIXER;
OnChangeALSettings(NULL, NULL);
@ -1397,7 +1398,8 @@ sounddriver_t OPENAL_Output =
{
SDRVNAME,
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 chunk;
int result;
int stride = sc->sn.numchannels * (sc->sn.samplebits/8);
int stride = sc->sn.numchannels * sc->sn.samplebytes;
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);
#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)
{
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.samplepos = 0;
// sc->sn.samplebits = bps;
// sc->sn.samplebytes = bps/8;
sc->samplequeue = buffer_size = 2048;
#else
@ -351,7 +364,7 @@ static qboolean QDECL ALSA_InitCard (soundcardinfo_t *sc, const char *pcmname)
}
// get sample bit size
bps = sc->sn.samplebits;
bps = sc->sn.samplebytes*8;
{
snd_pcm_format_t spft;
if (bps == 16)
@ -456,7 +469,7 @@ static qboolean QDECL ALSA_InitCard (soundcardinfo_t *sc, const char *pcmname)
sc->sn.numchannels = stereo;
sc->sn.samplepos = 0;
sc->sn.samplebits = bps;
sc->sn.samplebytes = bps/8;
buffer_size = sc->sn.samples / stereo;
if (buffer_size)
@ -509,7 +522,7 @@ static qboolean QDECL ALSA_InitCard (soundcardinfo_t *sc, const char *pcmname)
sc->Submit = ALSA_RW_Submit;
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);
if (0 > err)

View File

@ -531,11 +531,8 @@ static unsigned int DSOUND_GetDMAPos(soundcardinfo_t *sc)
IDirectSoundBuffer_GetCurrentPosition(dh->pDSBuf, &mmtime, &dwWrite);
s = mmtime - dh->mmstarttime;
s /= (sc->sn.samplebits/8);
s /= sc->sn.samplebytes;
s %= (sc->sn.samples);
return s;
}
@ -657,15 +654,25 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname)
sc->sn.numchannels = 1;
}
if (sc->sn.samplebits == 32)
{ //FTE does not support 32bit int audio, rather we interpret samplebits 32 as floats.
switch(sc->sn.samplebytes)
{
case 4:
//FTE does not support 32bit int audio, rather we interpret samplebits 32 as floats.
format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
format.Format.cbSize = 22;
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.wBitsPerSample = sc->sn.samplebits;
format.Format.wBitsPerSample = sc->sn.samplebytes*8;
format.Format.nSamplesPerSec = sc->sn.speed;
format.Format.nBlockAlign = format.Format.nChannels * format.Format.wBitsPerSample / 8;
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.samplebits = format.Format.wBitsPerSample;
sc->sn.samplebytes = format.Format.wBitsPerSample/8;
sc->sn.speed = format.Format.nSamplesPerSec;
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"
" %d bits/sample\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
@ -938,7 +945,7 @@ static int DSOUND_InitCard_Internal (soundcardinfo_t *sc, char *cardname)
IDirectSoundBuffer_GetCurrentPosition(dh->pDSBuf, &dh->mmstarttime, &dwWrite);
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.buffer = NULL;

View File

@ -220,7 +220,7 @@ void S_SoundInfo_f(void)
for (sc = sndcardinfo; sc; sc = sc->next)
{
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);
for (i = 0, active = 0, known = 0; i < sc->total_chans; i++)
{
@ -1697,18 +1697,24 @@ extern sounddriver_t OPENAL_Output;
#ifdef __DJGPP__
extern sounddriver_t SBLASTER_Output;
#endif
sounddriver pSNDIO_InitCard;
sounddriver pOSS_InitCard;
sounddriver pMacOS_InitCard;
sounddriver pSDL_InitCard;
sounddriver pWAV_InitCard;
sounddriver pDroid_InitCard;
sounddriver pAHI_InitCard;
#ifdef NACL
extern sounddriver pPPAPI_InitCard;
#if defined(_WIN32) && !defined(WINRT) && !defined(FTE_SDL)
extern sounddriver_t WaveOut_Output;
#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
static sounddriver_t *outputdrivers[] =
{
@ -1731,37 +1737,35 @@ static sounddriver_t *outputdrivers[] =
#ifdef __linux__
&ALSA_Output, //pure shite
#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__
&SBLASTER_Output, //zomgwtfdos?
#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
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)
{
soundcardinfo_t *sc = Z_Malloc(sizeof(soundcardinfo_t));
sdriver_t *od;
sounddriver_t *sd;
int i;
int st;
@ -1803,10 +1807,12 @@ static soundcardinfo_t *SNDDMA_Init(char *driver, char *device, int seat)
sc->sn.numchannels = 1;
// set requested sample bits
if (snd_samplebits.ival >= 16)
sc->sn.samplebits = 16;
if (snd_samplebits.ival >= 32)
sc->sn.samplebytes = 4;
else if (snd_samplebits.ival >= 16)
sc->sn.samplebytes = 2;
else
sc->sn.samplebits = 8;
sc->sn.samplebytes = 1;
// set requested buffer size
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);
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);
if (snd_speed)
{ //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);
S_ShutdownCard(sc);
continue;
return NULL;
}
}
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);
if (!driver)
@ -2152,7 +2148,7 @@ S_Init
*/
void S_Init (void)
{
int p;
int p, i;
Con_DPrintf("\nSound Initialization\n");
@ -2208,9 +2204,12 @@ void S_Init (void)
mixermutex = Sys_CreateMutex();
#endif
#ifdef AVAIL_OPENAL
OpenAL_CvarInit();
#endif
for (i = 0; outputdrivers[i]; i++)
{
sounddriver_t *sd = outputdrivers[i];
if (sd && sd->name && sd->RegisterCvars)
sd->RegisterCvars();
}
if (COM_CheckParm("-nosound"))
{
@ -3072,7 +3071,7 @@ static void S_ClearBuffer (soundcardinfo_t *sc)
if (!sound_started || !sc->sn.buffer)
return;
if (sc->sn.samplebits == 8)
if (sc->sn.sampleformat == QSF_U8)
clear = 0x80;
else
clear = 0;
@ -3081,7 +3080,7 @@ static void S_ClearBuffer (soundcardinfo_t *sc)
buffer = sc->Lock(sc, &dummy);
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);
}
}

View File

@ -1,139 +1,154 @@
/*
this file is basically a copy of the SDL one
java code has a function or two which just periodically calls us to ask us to dump out audio for it
*/
#include "quakedef.h"
#include <jni.h>
#include <pthread.h>
static soundcardinfo_t *sys_sc = NULL;
extern int sys_soundflags;
//called by the java code when it wants to know what sort of AudioTrack format to use.
JNIEXPORT jint JNICALL Java_com_fteqw_FTEDroidEngine_audioinfo(JNIEnv *env, jclass this, jint arg)
{
soundcardinfo_t *sc = sys_sc;
if (!sc)
return 0;
switch(arg)
{
case 1:
return sc->sn.numchannels;
case 2:
return sc->sn.samplebits;
default:
return sc->sn.speed;
}
}
extern int S_GetMixerTime(soundcardinfo_t *sc);
//transfer the 'dma' buffer into the buffer it requests, called from a dedicated sound thread created by the java code.
JNIEXPORT jint JNICALL Java_com_fteqw_FTEDroidEngine_paintaudio(JNIEnv *env, jclass this, jbyteArray stream, jint len)
{
int offset = 0;
soundcardinfo_t *sc = sys_sc;
int framesz;
if (sc)
{
int buffersize = sc->sn.samples*sc->sn.samplebits/8;
int curtime = S_GetMixerTime(sc);
framesz = sc->sn.numchannels * sc->sn.samplebits/8;
S_PaintChannels (sc, curtime + (len / framesz));
if (len > buffersize)
{
len = buffersize; //whoa nellie!
}
if (len + sc->snd_sent%buffersize > buffersize)
{ //buffer will wrap, fill in the rest
(*env)->SetByteArrayRegion(env, stream, offset, buffersize - (sc->snd_sent%buffersize), (char*)sc->sn.buffer + (sc->snd_sent%buffersize));
offset += buffersize - (sc->snd_sent%buffersize);
sc->snd_sent += buffersize - (sc->snd_sent%buffersize);
len -= buffersize - (sc->snd_sent%buffersize);
if (len < 0) /*this must be impossible, surely?*/
len = 0;
}
//and finish from the start
(*env)->SetByteArrayRegion(env, stream, offset, len, (char*)sc->sn.buffer + (sc->snd_sent%buffersize));
offset += len;
sc->snd_sent += len;
}
else
offset = len; /*so the playback thread ends up blocked properly*/
return offset;
}
static void Droid_Shutdown(soundcardinfo_t *sc)
{
//fixme: what if we're currently inside Java_com_fteqw_FTEDroidEngine_paintaudio?
sys_sc = NULL;
free(sc->sn.buffer);
sys_soundflags = 0;
}
//return the number of samples that have already been submitted to the device.
static unsigned int Droid_GetDMAPos(soundcardinfo_t *sc)
{
sc->sn.samplepos = sc->snd_sent / (sc->sn.samplebits/8);
return sc->sn.samplepos;
}
static void Droid_UnlockBuffer(soundcardinfo_t *sc, void *buffer)
{
}
static void *Droid_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx)
{
return sc->sn.buffer;
}
/*
static void Droid_SetEnvironmentReverb(soundcardinfo_t *sc, qboolean uw)
{
}
*/
static void Droid_Submit(soundcardinfo_t *sc, int start, int end)
{
}
//on android, 16bit audio is '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
//nor any guarentee about channels supported. I assume mono will always work.
static int Droid_InitCard (soundcardinfo_t *sc, int cardnum)
{
if (sys_sc)
return 2;
sc->selfpainting = true;
// sc->sn.speed = 11025;
// sc->sn.samplebits = 16;
// sc->sn.numchannels = 1;
/*internal buffer should have 1 sec audio*/
sc->sn.samples = sc->sn.speed*sc->sn.numchannels;
sc->Lock = Droid_LockBuffer;
sc->Unlock = Droid_UnlockBuffer;
// sc->SetEnvironmentReverb = Droid_SetEnvironmentReverb;
sc->Submit = Droid_Submit;
sc->Shutdown = Droid_Shutdown;
sc->GetDMAPos = Droid_GetDMAPos;
sc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebits/8);
sys_sc = sc;
sys_soundflags = 3;
return 1;
}
int (*pDroid_InitCard) (soundcardinfo_t *sc, int cardnum) = &Droid_InitCard;
/*
this file is basically a copy of the SDL one
java code has a function or two which just periodically calls us to ask us to dump out audio for it
*/
#include "quakedef.h"
#include <jni.h>
#include <pthread.h>
static soundcardinfo_t *sys_sc = NULL;
extern int sys_soundflags;
//called by the java code when it wants to know what sort of AudioTrack format to use.
JNIEXPORT jint JNICALL Java_com_fteqw_FTEDroidEngine_audioinfo(JNIEnv *env, jclass this, jint arg)
{
soundcardinfo_t *sc = sys_sc;
if (!sc)
return 0;
switch(arg)
{
case 1:
return sc->sn.numchannels;
case 2:
return sc->sn.samplebytes*8;
default:
return sc->sn.speed;
}
}
extern int S_GetMixerTime(soundcardinfo_t *sc);
//transfer the 'dma' buffer into the buffer it requests, called from a dedicated sound thread created by the java code.
JNIEXPORT jint JNICALL Java_com_fteqw_FTEDroidEngine_paintaudio(JNIEnv *env, jclass this, jbyteArray stream, jint len)
{
int offset = 0;
soundcardinfo_t *sc = sys_sc;
int framesz;
if (sc)
{
int buffersize = sc->sn.samples*sc->sn.samplebytes;
int curtime = S_GetMixerTime(sc);
framesz = sc->sn.numchannels * sc->sn.samplebytes;
S_PaintChannels (sc, curtime + (len / framesz));
if (len > buffersize)
{
len = buffersize; //whoa nellie!
}
if (len + sc->snd_sent%buffersize > buffersize)
{ //buffer will wrap, fill in the rest
(*env)->SetByteArrayRegion(env, stream, offset, buffersize - (sc->snd_sent%buffersize), (char*)sc->sn.buffer + (sc->snd_sent%buffersize));
offset += buffersize - (sc->snd_sent%buffersize);
sc->snd_sent += buffersize - (sc->snd_sent%buffersize);
len -= buffersize - (sc->snd_sent%buffersize);
if (len < 0) /*this must be impossible, surely?*/
len = 0;
}
//and finish from the start
(*env)->SetByteArrayRegion(env, stream, offset, len, (char*)sc->sn.buffer + (sc->snd_sent%buffersize));
offset += len;
sc->snd_sent += len;
}
else
offset = len; /*so the playback thread ends up blocked properly*/
return offset;
}
static void Droid_Shutdown(soundcardinfo_t *sc)
{
//fixme: what if we're currently inside Java_com_fteqw_FTEDroidEngine_paintaudio?
sys_sc = NULL;
free(sc->sn.buffer);
sys_soundflags = 0;
}
//return the number of samples that have already been submitted to the device.
static unsigned int Droid_GetDMAPos(soundcardinfo_t *sc)
{
sc->sn.samplepos = sc->snd_sent / sc->sn.samplebytes;
return sc->sn.samplepos;
}
static void Droid_UnlockBuffer(soundcardinfo_t *sc, void *buffer)
{
}
static void *Droid_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx)
{
return sc->sn.buffer;
}
/*
static void Droid_SetEnvironmentReverb(soundcardinfo_t *sc, qboolean uw)
{
}
*/
static void Droid_Submit(soundcardinfo_t *sc, int start, int end)
{
}
//on android, 16bit audio is '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
//nor any guarentee about channels supported. I assume mono will always work.
static qboolean Droid_InitCard (soundcardinfo_t *sc, const char *cardname)
{
if (sys_sc)
return false; //can only cope with one device.
if (cardname && *cardname)
return false; //only the default device
sc->selfpainting = true;
// sc->sn.speed = 11025;
// sc->sn.samplebytes = 2;
// 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*/
sc->sn.samples = sc->sn.speed*sc->sn.numchannels;
sc->Lock = Droid_LockBuffer;
sc->Unlock = Droid_UnlockBuffer;
// sc->SetEnvironmentReverb = Droid_SetEnvironmentReverb;
sc->Submit = Droid_Submit;
sc->Shutdown = Droid_Shutdown;
sc->GetDMAPos = Droid_GetDMAPos;
sc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebytes);
sys_sc = sc;
sys_soundflags = 3;
return 1;
}
sounddriver_t Droid_AudioOutput =
{
"Android",
Droid_InitCard,
NULL
};

View File

@ -14,6 +14,11 @@
#include <sys/stat.h>
#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 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;
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);
sc->sn.samplepos = count.ptr / (sc->sn.samplebits / 8);
sc->sn.samplepos = count.ptr / sc->sn.samplebytes;
}
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)
{
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;
}
@ -61,12 +66,12 @@ static void OSS_Alsa_Submit(soundcardinfo_t *sc, int start, int end)
int result;
/*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;
if (!bytes)
return;
ringsize = sc->sn.samples * (sc->sn.samplebits/8);
ringsize = sc->sn.samples * sc->sn.samplebytes;
chunk = bytes;
offset = sc->snd_sent % ringsize;
@ -99,7 +104,7 @@ static void OSS_Shutdown(soundcardinfo_t *sc)
if (sc->Submit == OSS_Alsa_Submit)
free(sc->sn.buffer); /*if using alsa-compat, just free the buffer*/
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)
close(sc->audio_fd);
@ -214,20 +219,15 @@ static qboolean OSS_InitCard(soundcardinfo_t *sc, const char *snddev)
sc->sn.numchannels = 1;
#endif
//choose bits
// ask the device what it supports
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
if (!(fmt & AFMT_U8) && sc->sn.samplebits == 8)
{ //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)
//choose a format
if (sc->sn.samplebytes >= 4 && (fmt & AFMT_FLOAT))
{
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);
if (rc < 0)
{
@ -237,8 +237,24 @@ static qboolean OSS_InitCard(soundcardinfo_t *sc, const char *snddev)
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 = ioctl(sc->audio_fd, SNDCTL_DSP_SETFMT, &rc);
if (rc < 0)
@ -249,10 +265,24 @@ static qboolean OSS_InitCard(soundcardinfo_t *sc, const char *snddev)
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
{
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);
return false;
}
@ -286,7 +316,7 @@ static qboolean OSS_InitCard(soundcardinfo_t *sc, const char *snddev)
return false;
}
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 */
// 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))
{
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)
{
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->samplequeue = info.bytes / (sc->sn.samplebits/8);
sc->samplequeue = info.bytes / sc->sn.samplebytes;
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->GetDMAPos = OSS_Alsa_GetDMAPos;
}

View File

@ -1,253 +1,258 @@
/*
Copyright (C) 2001-2002 A Nourai
Copyright (C) 2006 Jacek Piszczek (Mac OSX port)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the included (GNU.txt) GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
#include "sound.h"
#include <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h>
// Jacek:
// coreaudio is poorly documented so I'm not 100% sure the code below
// is correct :(
struct MacOSSound_Private
{
AudioUnit gOutputUnit;
unsigned int readpos;
};
static OSStatus AudioRender(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
soundcardinfo_t *sc = inRefCon;
struct MacOSSound_Private *pdata = sc->handle;
int start = pdata->readpos;
int buffersize = sc->sn.samples * (sc->sn.samplebits/8);
int bytes = ioData->mBuffers[0].mDataByteSize;
int remaining;
start %= buffersize;
if (start + bytes > buffersize)
{
remaining = bytes;
bytes = buffersize - start;
remaining -= bytes;
}
else
{
remaining = 0;
}
memcpy(ioData->mBuffers[0].mData, sc->sn.buffer + start, bytes);
memcpy((char*)ioData->mBuffers[0].mData+bytes, sc->sn.buffer, remaining);
pdata->readpos += inNumberFrames*sc->sn.numchannels * (sc->sn.samplebits/8);
return noErr;
}
static void MacOS_Shutdown(soundcardinfo_t *sc)
{
struct MacOSSound_Private *pdata = sc->handle;
sc->handle = NULL;
if (!pdata)
return;
// stop playback
AudioOutputUnitStop (pdata->gOutputUnit);
// release the unit
AudioUnitUninitialize (pdata->gOutputUnit);
// free the unit
CloseComponent (pdata->gOutputUnit);
// free the buffer memory
Z_Free(sc->sn.buffer);
Z_Free(pdata);
}
static unsigned int MacOS_GetDMAPos(soundcardinfo_t *sc)
{
struct MacOSSound_Private *pdata = sc->handle;
sc->sn.samplepos = pdata->readpos/(sc->sn.samplebits/8);
return sc->sn.samplepos;
}
static void MacOS_Submit(soundcardinfo_t *sc)
{
}
static void *MacOS_Lock(soundcardinfo_t *sc, unsigned int *sampidx)
{
return sc->sn.buffer;
}
static void MacOS_Unlock(soundcardinfo_t *sc, void *buffer)
{
}
static int MacOS_InitCard(soundcardinfo_t *sc, int cardnum)
{
ComponentResult err = noErr;
if (cardnum)
return 2; /* no more */
struct MacOSSound_Private *pdata = Z_Malloc(sizeof(*pdata));
if (!pdata)
return FALSE;
// Open the default output unit
ComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
Component comp = FindNextComponent(NULL, &desc);
if (comp == NULL)
{
Con_Printf("FindNextComponent failed\n");
Z_Free(pdata);
return FALSE;
}
err = OpenAComponent(comp, &pdata->gOutputUnit);
if (comp == NULL)
{
Con_Printf("OpenAComponent failed\n");
Z_Free(pdata);
return FALSE;
}
// Set up a callback function to generate output to the output unit
AURenderCallbackStruct input;
input.inputProc = AudioRender;
input.inputProcRefCon = sc;
err = AudioUnitSetProperty ( pdata->gOutputUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&input,
sizeof(input));
if (err)
{
Con_Printf("AudioUnitSetProperty failed\n");
CloseComponent(pdata->gOutputUnit);
Z_Free(pdata);
return FALSE;
}
// describe our audio data
AudioStreamBasicDescription streamFormat;
streamFormat.mSampleRate = sc->sn.speed;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags = kAudioFormatFlagsNativeEndian
| kLinearPCMFormatFlagIsPacked;
//| kAudioFormatFlagIsNonInterleaved;
streamFormat.mFramesPerPacket = 1;
streamFormat.mChannelsPerFrame = 2;
streamFormat.mBitsPerChannel = 16;
if (streamFormat.mBitsPerChannel >= 16)
streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
else
streamFormat.mFormatFlags |= 0;
streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame * (streamFormat.mBitsPerChannel/8);
streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame * streamFormat.mFramesPerPacket;
err = AudioUnitSetProperty (pdata->gOutputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&streamFormat,
sizeof(AudioStreamBasicDescription));
if (err)
{
Con_Printf("AudioUnitSetProperty failed\n");
CloseComponent(pdata->gOutputUnit);
Z_Free(pdata);
return FALSE;
}
// set the shm structure
sc->sn.speed = streamFormat.mSampleRate;
sc->sn.samplebits = streamFormat.mBitsPerChannel;
sc->sn.numchannels = streamFormat.mChannelsPerFrame;
sc->sn.samples = 256 * 1024;
sc->sn.buffer = Z_Malloc(sc->sn.samples*sc->sn.samplebits/8);
int i;
for (i = 0; i < sc->sn.samples*sc->sn.samplebits/8; i++)
sc->sn.buffer[i] = rand();
if (sc->sn.buffer == 0)
{
Con_Printf("Malloc failed - cannot allocate sound buffer\n");
CloseComponent(pdata->gOutputUnit);
Z_Free(pdata);
return FALSE;
}
// Initialize unit
err = AudioUnitInitialize(pdata->gOutputUnit);
if (err)
{
Con_Printf("AudioOutputInitialize failed\n");
CloseComponent(pdata->gOutputUnit);
Z_Free(sc->sn.buffer);
Z_Free(pdata);
return FALSE;
}
// start playing :)
err = AudioOutputUnitStart (pdata->gOutputUnit);
if (err)
{
Con_Printf("AudioOutputUnitStart failed\n");
AudioUnitUninitialize (pdata->gOutputUnit);
CloseComponent(pdata->gOutputUnit);
Z_Free(sc->sn.buffer);
Z_Free(pdata);
return FALSE;
}
sc->handle = pdata;
sc->Lock = MacOS_Lock;
sc->Unlock = MacOS_Unlock;
sc->Submit = MacOS_Submit;
sc->GetDMAPos = MacOS_GetDMAPos;
sc->Shutdown = MacOS_Shutdown;
Con_Printf("Sound initialised\n");
return TRUE;
}
sounddriver pMacOS_InitCard = &MacOS_InitCard;
/*
Copyright (C) 2001-2002 A Nourai
Copyright (C) 2006 Jacek Piszczek (Mac OSX port)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the included (GNU.txt) GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
#include "sound.h"
#include <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h>
// Jacek:
// coreaudio is poorly documented so I'm not 100% sure the code below
// is correct :(
struct MacOSSound_Private
{
AudioUnit gOutputUnit;
unsigned int readpos;
};
static OSStatus AudioRender(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
soundcardinfo_t *sc = inRefCon;
struct MacOSSound_Private *pdata = sc->handle;
int start = pdata->readpos;
int buffersize = sc->sn.samples * sc->sn.samplebytes;
int bytes = ioData->mBuffers[0].mDataByteSize;
int remaining;
start %= buffersize;
if (start + bytes > buffersize)
{
remaining = bytes;
bytes = buffersize - start;
remaining -= bytes;
}
else
{
remaining = 0;
}
memcpy(ioData->mBuffers[0].mData, sc->sn.buffer + start, bytes);
memcpy((char*)ioData->mBuffers[0].mData+bytes, sc->sn.buffer, remaining);
pdata->readpos += inNumberFrames*sc->sn.numchannels * sc->sn.samplebytes;
return noErr;
}
static void MacOS_Shutdown(soundcardinfo_t *sc)
{
struct MacOSSound_Private *pdata = sc->handle;
sc->handle = NULL;
if (!pdata)
return;
// stop playback
AudioOutputUnitStop (pdata->gOutputUnit);
// release the unit
AudioUnitUninitialize (pdata->gOutputUnit);
// free the unit
CloseComponent (pdata->gOutputUnit);
// free the buffer memory
Z_Free(sc->sn.buffer);
Z_Free(pdata);
}
static unsigned int MacOS_GetDMAPos(soundcardinfo_t *sc)
{
struct MacOSSound_Private *pdata = sc->handle;
sc->sn.samplepos = pdata->readpos/sc->sn.samplebytes;
return sc->sn.samplepos;
}
static void MacOS_Submit(soundcardinfo_t *sc)
{
}
static void *MacOS_Lock(soundcardinfo_t *sc, unsigned int *sampidx)
{
return sc->sn.buffer;
}
static void MacOS_Unlock(soundcardinfo_t *sc, void *buffer)
{
}
static qboolean MacOS_InitCard(soundcardinfo_t *sc, const char *cardname)
{
ComponentResult err = noErr;
if (cardname && *cardname)
return false; //only the default device will be used for now.
struct MacOSSound_Private *pdata = Z_Malloc(sizeof(*pdata));
if (!pdata)
return FALSE;
// Open the default output unit
ComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
Component comp = FindNextComponent(NULL, &desc);
if (comp == NULL)
{
Con_Printf("FindNextComponent failed\n");
Z_Free(pdata);
return FALSE;
}
err = OpenAComponent(comp, &pdata->gOutputUnit);
if (comp == NULL)
{
Con_Printf("OpenAComponent failed\n");
Z_Free(pdata);
return FALSE;
}
// Set up a callback function to generate output to the output unit
AURenderCallbackStruct input;
input.inputProc = AudioRender;
input.inputProcRefCon = sc;
err = AudioUnitSetProperty ( pdata->gOutputUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&input,
sizeof(input));
if (err)
{
Con_Printf("AudioUnitSetProperty failed\n");
CloseComponent(pdata->gOutputUnit);
Z_Free(pdata);
return FALSE;
}
// describe our audio data
AudioStreamBasicDescription streamFormat;
streamFormat.mSampleRate = sc->sn.speed;
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags = kAudioFormatFlagsNativeEndian
| kLinearPCMFormatFlagIsPacked;
//| kAudioFormatFlagIsNonInterleaved;
streamFormat.mFramesPerPacket = 1;
streamFormat.mChannelsPerFrame = 2;
streamFormat.mBitsPerChannel = 16;
if (streamFormat.mBitsPerChannel >= 16)
streamFormat.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
else
streamFormat.mFormatFlags |= 0;
streamFormat.mBytesPerFrame = streamFormat.mChannelsPerFrame * (streamFormat.mBitsPerChannel/8);
streamFormat.mBytesPerPacket = streamFormat.mBytesPerFrame * streamFormat.mFramesPerPacket;
err = AudioUnitSetProperty (pdata->gOutputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&streamFormat,
sizeof(AudioStreamBasicDescription));
if (err)
{
Con_Printf("AudioUnitSetProperty failed\n");
CloseComponent(pdata->gOutputUnit);
Z_Free(pdata);
return FALSE;
}
// set the shm structure
sc->sn.speed = streamFormat.mSampleRate;
sc->sn.samplebytes = streamFormat.mBitsPerChannel/8;
sc->sn.sampleformat = QCF_S16;
sc->sn.numchannels = streamFormat.mChannelsPerFrame;
sc->sn.samples = 256 * 1024;
sc->sn.buffer = Z_Malloc(sc->sn.samples*sc->sn.samplebytes);
int i;
for (i = 0; i < sc->sn.samples*sc->sn.samplebytes; i++)
sc->sn.buffer[i] = rand();
if (sc->sn.buffer == 0)
{
Con_Printf("Malloc failed - cannot allocate sound buffer\n");
CloseComponent(pdata->gOutputUnit);
Z_Free(pdata);
return FALSE;
}
// Initialize unit
err = AudioUnitInitialize(pdata->gOutputUnit);
if (err)
{
Con_Printf("AudioOutputInitialize failed\n");
CloseComponent(pdata->gOutputUnit);
Z_Free(sc->sn.buffer);
Z_Free(pdata);
return FALSE;
}
// start playing :)
err = AudioOutputUnitStart (pdata->gOutputUnit);
if (err)
{
Con_Printf("AudioOutputUnitStart failed\n");
AudioUnitUninitialize (pdata->gOutputUnit);
CloseComponent(pdata->gOutputUnit);
Z_Free(sc->sn.buffer);
Z_Free(pdata);
return FALSE;
}
sc->handle = pdata;
sc->Lock = MacOS_Lock;
sc->Unlock = MacOS_Unlock;
sc->Submit = MacOS_Submit;
sc->GetDMAPos = MacOS_GetDMAPos;
sc->Shutdown = MacOS_Shutdown;
Con_Printf("Sound initialised\n");
return TRUE;
}
sounddriver_t MacOS_AudioOutput =
{
"CoreAudio",
MacOS_InitCard,
NULL
};

View File

@ -52,57 +52,86 @@ void S_TransferPaintBuffer(soundcardinfo_t *sc, int endtime)
if (!pbuf)
return;
if (sc->sn.samplebits == 16)
switch(sc->sn.sampleformat)
{
short *out = (short *) pbuf;
while (count)
case QSF_INVALID: //erk...
case QSF_EXTERNALMIXER: //shouldn't reach this.
break;
case QSF_U8:
{
for (i = 0; i < numc; i++)
unsigned char *out = (unsigned char *) pbuf;
while (count)
{
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;
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) + 128;
out_idx = (out_idx + 1) % outlimit;
}
p += MAXSOUNDCHANNELS - numc;
count -= numc;
}
p += MAXSOUNDCHANNELS - numc;
count -= numc;
}
}
else if (sc->sn.samplebits == 8)
{
unsigned char *out = (unsigned char *) pbuf;
while (count)
break;
case QSF_S8:
{
for (i = 0; i < numc; i++)
char *out = (char *) pbuf;
while (count)
{
val = *p++;// * snd_vol) >> 8;
if (val > 0x7fff)
val = 0x7fff;
else if (val < (short)0x8000)
val = (short)0x8000;
out[out_idx] = (val>>8) + 128;
out_idx = (out_idx + 1) % outlimit;
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;
}
p += MAXSOUNDCHANNELS - numc;
count -= numc;
}
}
else if (sc->sn.samplebits == 32)
{
float *out = (float *) pbuf;
while (count)
break;
case QSF_S16:
{
for (i = 0; i < numc; i++)
short *out = (short *) pbuf;
while (count)
{
out[out_idx] = *p++ * (1.0 / 32768);
out_idx = (out_idx + 1) % outlimit;
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;
}
p += MAXSOUNDCHANNELS - numc;
count -= numc;
}
break;
case QSF_F32:
{
float *out = (float *) pbuf;
while (count)
{
for (i = 0; i < numc; i++)
{
out[out_idx] = *p++ * (1.0 / 32768);
out_idx = (out_idx + 1) % outlimit;
}
p += MAXSOUNDCHANNELS - numc;
count -= numc;
}
}
break;
}
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;
@ -129,8 +129,8 @@ static int AHI_InitCard(soundcardinfo_t *sc, int cardnum)
struct AHISampleInfo sample;
if (cardnum)
return 2; /* Which means "no more cards" */
if (cardname && *cardname)
return false; /* only allow the default audio device */
ad = AllocVec(sizeof(*ad), MEMF_ANY);
if (ad)
@ -171,7 +171,7 @@ static int AHI_InitCard(soundcardinfo_t *sc, int cardnum)
channels = 2;
sc->sn.speed = speed;
sc->sn.samplebits = bits;
sc->sn.samplebytes = bits/8;
sc->sn.numchannels = channels;
sc->sn.samples = speed*channels;
@ -194,6 +194,7 @@ static int AHI_InitCard(soundcardinfo_t *sc, int cardnum)
else
sample.ahisi_Type = AHIST_S16S;
}
sc->sn.sampleformat = (bits==8)?QSF_S8:QSF_S16;
sample.ahisi_Address = ad->samplebuffer;
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("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);
}
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);
count = inportb(dma*2+1);
count += inportb(dma*2+1) << 8;
if (sc->sn.samplebits == 16)
if (sc->sn.samplebytes == 2)
count /= 2;
count = sc->sn.samples - (count+1);
}
@ -368,7 +368,7 @@ static unsigned int SBLASTER_GetDMAPos(soundcardinfo_t *sc)
outportb(0xd8, 0);
count = inportb(0xc0+(dma-4)*4+2);
count += inportb(0xc0+(dma-4)*4+2) << 8;
if (sc->sn.samplebits == 8)
if (sc->sn.samplebytes == 1)
count *= 2;
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)
sc->sn.numchannels = 2;
if (sc->sn.samplebits != 8)
sc->sn.samplebits = 16;
if (sc->sn.samplebytes != 1)
sc->sn.samplebytes = 2;
}
// version 3 cards (sb pro) do 8 bit stereo
else if (dsp_version == 3)
{
if (sc->sn.numchannels != 1)
sc->sn.numchannels = 2;
sc->sn.samplebits = 8;
sc->sn.samplebytes = 1;
}
// v2 cards do 8 bit mono
else
{
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->Unlock = SBLASTER_UnlockBuffer;
sc->Shutdown = SBLASTER_Shutdown;
@ -560,10 +565,10 @@ static qboolean SBLASTER_InitCard(soundcardinfo_t *sc, const char *pcmname)
dma_size = 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.buffer = (unsigned char *) dma_buffer;
sc->sn.samples = size/(sc->sn.samplebits/8);
sc->sn.samples = size/sc->sn.samplebytes;
StartDMA();
StartSB(sc);

View File

@ -18,12 +18,18 @@
#define SDL_AUDIO_ALLOW_FREQUENCY_CHANGE 0x00000001
#define SDL_AUDIO_ALLOW_FORMAT_CHANGE 0x00000002
#define SDL_AUDIO_ALLOW_CHANNELS_CHANGE 0x00000004
#define AUDIO_U8 0x0008
#define AUDIO_S8 0x8008
#define AUDIO_S16LSB 0x8010
#define AUDIO_S16MSB 0x9010
#define AUDIO_F32LSB 0x8120
#define AUDIO_F32MSB 0x9120
#if __BYTE_ORDER == __BIG_ENDIAN
#define AUDIO_S16SYS AUDIO_S16MSB
#define AUDIO_F32SYS AUDIO_F32MSB
#else
#define AUDIO_S16SYS AUDIO_S16LSB
#define AUDIO_F32SYS AUDIO_F32LSB
#endif
#define SDLCALL QDECL
@ -129,8 +135,11 @@ static void SSDL_Shutdown(soundcardinfo_t *sc)
Con_DPrintf("Shutdown SDL sound\n");
#if SDL_MAJOR_VERSION >= 2
SDL_PauseAudioDevice(sc->audio_fd, 1);
SDL_CloseAudioDevice(sc->audio_fd);
if (sc->audio_fd)
{
SDL_PauseAudioDevice(sc->audio_fd, 1);
SDL_CloseAudioDevice(sc->audio_fd);
}
#else
SDL_CloseAudio();
#endif
@ -142,7 +151,7 @@ static void SSDL_Shutdown(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;
}
@ -154,12 +163,12 @@ static void VARGS SSDL_Paint(void *userdata, qbyte *stream, int len)
#ifdef SELFPAINT
sc->sn.buffer = stream;
sc->sn.samples = len / (sc->sn.samplebits/8);
sc->sn.samples = len / sc->sn.samplebytes;
sc->samplequeue = sc->sn.samples;
S_MixerThread(sc);
sc->snd_sent += len;
#else
int buffersize = sc->sn.samples*(sc->sn.samplebits/8);
int buffersize = sc->sn.samples*sc->sn.samplebytes;
if (len > buffersize)
{
@ -220,26 +229,32 @@ static qboolean QDECL SDL_InitCard(soundcardinfo_t *sc, const char *devicename)
desired.freq = sc->sn.speed;
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.format = AUDIO_S16SYS;
desired.callback = (void*)SSDL_Paint;
desired.userdata = sc;
memcpy(&obtained, &desired, sizeof(obtained));
#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)
{
Con_Printf("SDL_OpenAudioDevice(%s) failed: couldn't open sound device (%s).\n", devicename?devicename:"default", SDL_GetError());
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)
Con_Printf("Initing SDL audio device '%s'.\n", devicename);
else
Con_Printf("Initing default SDL audio device.\n");
#else
desired.format = AUDIO_S16SYS;
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.
if ( SDL_OpenAudio(&desired, &obtained) < 0 )
if (SDL_OpenAudio(&desired, &obtained) < 0)
{
Con_Printf("SDL_OpenAudio failed: couldn't open sound device (%s).\n", SDL_GetError());
return false;
@ -248,21 +263,49 @@ static qboolean QDECL SDL_InitCard(soundcardinfo_t *sc, const char *devicename)
#endif
sc->sn.numchannels = obtained.channels;
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
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
sc->selfpainting = true;
#endif
Con_DPrintf("channels: %i\n", sc->sn.numchannels);
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("FakeSamples: %i\n", sc->sn.samples);
#ifndef SELFPAINT
sc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebits/8);
sc->sn.buffer = malloc(sc->sn.samples*sc->sn.samplebytes);
#endif
Con_DPrintf("Got sound %i-%i\n", obtained.freq, obtained.format);

View File

@ -37,24 +37,19 @@ struct sndio_private
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_unlock(soundcardinfo_t *, void *);
static void sndio_shutdown(soundcardinfo_t *);
static unsigned int sndio_getdmapos(soundcardinfo_t *);
static void sndio_submit(soundcardinfo_t *, int, int);
static void sndio_setunderwater(soundcardinfo_t *sc, qboolean underwater); //simply a stub. Any ideas how to actually implement this properly?
static void sndio_setunderwater(soundcardinfo_t *sc, qboolean underwater) //simply a stub. Any ideas how to actually implement this properly?
{
}
#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)
static qboolean sndio_init(soundcardinfo_t *sc, const char *cardname)
{
struct sndio_private *sp;
struct sio_par par;
@ -63,22 +58,22 @@ sndio_init(soundcardinfo_t *sc, int cardnum)
int i;
Con_DPrintf("sndio_init called\n");
if (cardnum)
return SND_NOMORE;
if (cardname && *cardname)
return false; //only support the default device for now.
sp = calloc(sizeof(struct sndio_private), 1);
if (sp == NULL)
{
Con_Printf("Could not get mem");
return SND_ERROR;
return false;
}
Con_DPrintf("trying to open sp->hdl\n");
sp->hdl = sio_open(SIO_DEVANY, SIO_PLAY, 1);
if (sp->hdl == NULL)
{
Con_Printf("Could not open sndio device\n");
return SND_NOMORE;
return false;
}
Con_DPrintf("Opened sndio\n");
sc->GetDMAPos = sndio_getdmapos;
@ -88,33 +83,44 @@ sndio_init(soundcardinfo_t *sc, int cardnum)
sc->Unlock = sndio_unlock;
sc->SetWaterDistortion = sndio_setunderwater;
sc->handle = sp;
sio_initpar(&par);
par.rate = sc->sn.speed;
par.bits = sc->sn.samplebits;
par.bits = (sc->sn.samplebytes==1)?8:16;
par.sig = 1;
par.le = SIO_LE_NATIVE;
par.pchan = sc->sn.numchannels;
par.appbufsz = par.rate / 20; /* 1/20 second latency */
if (!sio_setpar(sp->hdl, &par) || !sio_getpar(sp->hdl, &par))
{
Con_Printf("Error setting audio parameters\n");
sio_close(sp->hdl);
return SND_ERROR;
return false;
}
if ((par.pchan != 1 && par.pchan != 2) ||
(par.bits != 16 || par.sig != 1))
{
Con_Printf("Could not set appropriate audio parameters\n");
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.numchannels = par.pchan;
sc->sn.samplebits = par.bits;
*/
/*
* find the smallest power of two larger than the buffer size
* and use it as the internal buffer's size
@ -122,36 +128,36 @@ sndio_init(soundcardinfo_t *sc, int cardnum)
for (i = 1; i < par.appbufsz; i <<= 1)
; /* nothing */
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);
if (sc->sn.buffer == NULL)
{
Con_Printf("Could not allocate audio ring buffer\n");
return SND_ERROR;
return false;
}
dma_ptr = 0;
if (!sio_start(sp->hdl))
{
Con_Printf("Could not start audio\n");
sio_close(sp->hdl);
return SND_ERROR;
return false;
}
sc->sn.samplepos = 0;
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.samples = %d, par.pchan = %d\n", sc->sn.samples, par.pchan);
Con_DPrintf("dma_buffer_size = %d\n", sp->dma_buffer_size);
return SND_LOADED;
return true;
}
static void *
sndio_lock(soundcardinfo_t *sc, unsigned int *sampidx)
{
return sc->sn.buffer;
return sc->sn.buffer;
}
static void
@ -163,7 +169,7 @@ static void
sndio_shutdown(soundcardinfo_t *sc)
{
struct sndio_private *sp = sc->handle;
sio_close(sp->hdl);
free(sc->sn.buffer);
sc->sn.buffer = NULL;
@ -174,7 +180,7 @@ static unsigned int
sndio_getdmapos(soundcardinfo_t *sc)
{
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;
}
@ -185,7 +191,7 @@ sndio_submit(soundcardinfo_t *sc, int startcount, int endcount)
struct sndio_private *sp = sc->handle;
size_t count, todo, avail;
int n;
n = sio_pollfd(sp->hdl, &pfd, POLLOUT);
while (poll(&pfd, n, 0) < 0 && errno == EINTR)
;
@ -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_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)
{
return sc->sn.buffer;
@ -55,19 +67,28 @@ static void WASAPI_Shutdown(soundcardinfo_t *sc)
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)
{
sc->sn.samplebits = 32;//oo, floating point audio. I guess this means we can have fun with clamping, right?
{ //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)))
{
Con_Printf("WASAPI: unsupported sample type\n");
return false; //we only support pcm / floats
}
else if (pwfx->wBitsPerSample == 8 || pwfx->wBitsPerSample == 16)
sc->sn.samplebits = pwfx->wBitsPerSample; //these sample sizes work
else if (pwfx->wBitsPerSample == 8)
{
sc->sn.samplebytes = 1;
sc->sn.sampleformat = QSF_U8;
}
else if (pwfx->wBitsPerSample == 16)
{
sc->sn.samplebytes = 2;
sc->sn.sampleformat = QSF_S16;
}
else
{
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.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;
}
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)))
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.
//this may cause failures later in this function.
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;
}
if (Cvar_Get("wasapi_forcechannels", "0", 0, "WASAPI audio output")->ival)
if (wasapi_forcechannels.ival)
{
Con_Printf("WASAPI: overriding channels\n");
@ -150,8 +171,7 @@ static qboolean WASAPI_DetermineFormat(soundcardinfo_t *sc, IAudioClient *dev, q
pwfx = pwfx2;
}
}
if (!WASAPI_AcceptableFormat(sc, dev, pwfx))
if (!WASAPI_AcceptableFormat(sc, dev, pwfx, false))
return false;
}
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)))
{
//fixme: try float audio...
//try to switch over to 24bit pcm (although with no more 16bit audio)
/* pwfx->wBitsPerSample = 24;
pwfx->nBlockAlign = pwfx->wBitsPerSample/8 * pwfx->nChannels;
@ -184,112 +206,128 @@ static qboolean WASAPI_DetermineFormat(soundcardinfo_t *sc, IAudioClient *dev, q
}
}
}
if (!WASAPI_AcceptableFormat(sc, dev, pwfx))
if (!WASAPI_AcceptableFormat(sc, dev, pwfx, true))
return false;
}
*ret = pwfx;
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)
{
soundcardinfo_t *sc = arg;
qboolean inited = false;
// REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioClient *pAudioClient = NULL;
IAudioRenderClient *pRenderClient = NULL;
UINT32 bufferFrameCount = 0;
HANDLE hEvent = NULL;
WAVEFORMATEX *pwfx;
qboolean exclusive = Cvar_Get("wasapi_exclusive", "1", 0, "WASAPI audio output")->ival;
qboolean exclusive = wasapi_exclusive.ival;
void *cond = sc->handle;
//main thread will wait for us to finish initing, so lets do that...
CoInitialize(NULL);
if (SUCCEEDED(CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void**)&pEnumerator)))
if (SUCCEEDED(pEnumerator->lpVtbl->GetDefaultAudioEndpoint(pEnumerator, eRender, eConsole, &pDevice)))
if (SUCCEEDED(pDevice->lpVtbl->Activate(pDevice, &IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient)))
IMMDevice *pDevice = WASAPI_GetDevice(sc);
if (pDevice)
{
if (!WASAPI_DetermineFormat(sc, pAudioClient, exclusive, &pwfx))
if (SUCCEEDED(pDevice->lpVtbl->Activate(pDevice, &IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient)))
{
Con_Printf("WASAPI: unable to determine mutually supported audio format\n");
}
else
{
if (sc->sn.samplebits && (!snd_speed || sc->sn.speed == snd_speed))
if (!WASAPI_DetermineFormat(sc, pAudioClient, exclusive, &pwfx))
{
HRESULT hr;
REFERENCE_TIME buffersize = REFTIMES_PER_SEC * Cvar_Get("wasapi_buffersize", "0.01", 0, "WASAPI audio output")->ival;
if (exclusive)
pAudioClient->lpVtbl->GetDevicePeriod(pAudioClient, NULL, &buffersize);
Con_Printf("WASAPI: unable to determine mutually supported audio format\n");
}
else
{
if (sc->sn.samplebytes && (!snd_speed || sc->sn.speed == snd_speed))
{
HRESULT hr;
REFERENCE_TIME buffersize = REFTIMES_PER_SEC * wasapi_buffersize.value;
if (exclusive)
pAudioClient->lpVtbl->GetDevicePeriod(pAudioClient, NULL, &buffersize);
hr = pAudioClient->lpVtbl->Initialize(pAudioClient, exclusive?AUDCLNT_SHAREMODE_EXCLUSIVE:AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buffersize, (exclusive?buffersize:0), pwfx, NULL);
hr = pAudioClient->lpVtbl->Initialize(pAudioClient, exclusive?AUDCLNT_SHAREMODE_EXCLUSIVE:AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buffersize, (exclusive?buffersize:0), pwfx, NULL);
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
{ //this is stupid, but does what the documentation says should be done.
if (SUCCEEDED(pAudioClient->lpVtbl->GetBufferSize(pAudioClient, &bufferFrameCount)))
{
if (pAudioClient)
pAudioClient->lpVtbl->Release(pAudioClient);
pAudioClient = NULL;
if (SUCCEEDED(pDevice->lpVtbl->Activate(pDevice, &IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient)))
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
{ //this is stupid, but does what the documentation says should be done.
if (SUCCEEDED(pAudioClient->lpVtbl->GetBufferSize(pAudioClient, &bufferFrameCount)))
{
buffersize = (REFERENCE_TIME)((10000.0 * 1000 / pwfx->nSamplesPerSec * bufferFrameCount) + 0.5);
hr = pAudioClient->lpVtbl->Initialize(pAudioClient, exclusive?AUDCLNT_SHAREMODE_EXCLUSIVE:AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buffersize, (exclusive?buffersize:0), pwfx, NULL);
if (pAudioClient)
pAudioClient->lpVtbl->Release(pAudioClient);
pAudioClient = NULL;
if (SUCCEEDED(pDevice->lpVtbl->Activate(pDevice, &IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient)))
{
buffersize = (REFERENCE_TIME)((10000.0 * 1000 / pwfx->nSamplesPerSec * bufferFrameCount) + 0.5);
hr = pAudioClient->lpVtbl->Initialize(pAudioClient, exclusive?AUDCLNT_SHAREMODE_EXCLUSIVE:AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, buffersize, (exclusive?buffersize:0), pwfx, NULL);
}
}
}
if (SUCCEEDED(hr))
{
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hEvent)
{
pAudioClient->lpVtbl->SetEventHandle(pAudioClient, hEvent);
if (SUCCEEDED(pAudioClient->lpVtbl->GetBufferSize(pAudioClient, &bufferFrameCount)))
if (SUCCEEDED(pAudioClient->lpVtbl->GetService(pAudioClient, &IID_IAudioRenderClient, (void**)&pRenderClient)))
inited = true;
}
}
else
{
switch(hr)
{
case AUDCLNT_E_UNSUPPORTED_FORMAT:
Con_Printf("WASAPI Initialize: AUDCLNT_E_UNSUPPORTED_FORMAT\n");
break;
case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED:
Con_Printf("WASAPI Initialize: AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED\n");
break;
case AUDCLNT_E_EXCLUSIVE_MODE_ONLY:
Con_Printf("WASAPI Initialize: AUDCLNT_E_EXCLUSIVE_MODE_ONLY\n");
break;
case AUDCLNT_E_DEVICE_IN_USE:
Con_Printf("WASAPI Initialize: AUDCLNT_E_DEVICE_IN_USE\n");
break;
case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED:
Con_Printf("WASAPI Initialize: AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED\n");
break;
case E_INVALIDARG:
Con_Printf("WASAPI Initialize: E_INVALIDARG\n");
break;
default:
Con_Printf("pAudioClient->lpVtbl->Initialize failed (%x)\n", (unsigned int)hr);
}
}
}
if (SUCCEEDED(hr))
{
hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hEvent)
{
pAudioClient->lpVtbl->SetEventHandle(pAudioClient, hEvent);
if (SUCCEEDED(pAudioClient->lpVtbl->GetBufferSize(pAudioClient, &bufferFrameCount)))
if (SUCCEEDED(pAudioClient->lpVtbl->GetService(pAudioClient, &IID_IAudioRenderClient, (void**)&pRenderClient)))
inited = true;
}
}
else
{
switch(hr)
{
case AUDCLNT_E_UNSUPPORTED_FORMAT:
Con_Printf("WASAPI Initialize: AUDCLNT_E_UNSUPPORTED_FORMAT\n");
break;
case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED:
Con_Printf("WASAPI Initialize: AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED\n");
break;
case AUDCLNT_E_EXCLUSIVE_MODE_ONLY:
Con_Printf("WASAPI Initialize: AUDCLNT_E_EXCLUSIVE_MODE_ONLY\n");
break;
case AUDCLNT_E_DEVICE_IN_USE:
Con_Printf("WASAPI Initialize: AUDCLNT_E_DEVICE_IN_USE\n");
break;
case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED:
Con_Printf("WASAPI Initialize: AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED\n");
break;
case E_INVALIDARG:
Con_Printf("WASAPI Initialize: E_INVALIDARG\n");
break;
default:
Con_Printf("pAudioClient->lpVtbl->Initialize failed (%x)\n", (unsigned int)hr);
}
}
CoTaskMemFree(pwfx);
}
CoTaskMemFree(pwfx);
}
pDevice->lpVtbl->Release(pDevice);
pDevice = NULL;
}
if (inited)
@ -359,19 +397,12 @@ static int WASAPI_Thread(void *arg)
pRenderClient->lpVtbl->Release(pRenderClient);
if (pAudioClient)
pAudioClient->lpVtbl->Release(pAudioClient);
if (pDevice)
pDevice->lpVtbl->Release(pDevice);
if (pEnumerator)
pEnumerator->lpVtbl->Release(pEnumerator);
return 0;
}
static qboolean QDECL WASAPI_InitCard (soundcardinfo_t *sc, const char *cardname)
{
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));
sc->selfpainting = true;
@ -504,7 +535,8 @@ sounddriver_t WASAPI_Output =
{
AUDIODRIVERNAME,
WASAPI_InitCard,
WASAPI_Enumerate
WASAPI_Enumerate,
WASAPI_RegisterCvars
};
#endif

View File

@ -142,14 +142,8 @@ how many sample are required to fill it up.
static unsigned int WAV_GetDMAPos(soundcardinfo_t *sc)
{
int s;
s = sc->snd_sent * WAV_BUFFER_SIZE;
s >>= (sc->sn.samplebits/8) - 1;
// s = (s/shm->numchannels % (shm->samples-1))*shm->numchannels;
s >>= sc->sn.samplebytes - 1;
return s;
}
@ -194,7 +188,7 @@ static void WAV_Submit(soundcardinfo_t *sc, int start, int end)
else
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 );
@ -224,15 +218,15 @@ SNDDM_InitWav
Crappy windows multimedia base
==================
*/
int WAV_InitCard (soundcardinfo_t *sc, int cardnum)
qboolean WAV_InitCard (soundcardinfo_t *sc, const char *cardname)
{
WAVEFORMATEX format;
int i;
HRESULT hr;
wavhandle_t *wh;
if (cardnum != 0)
return 2; //we only support one card, at the moment.
if (*cardname)
return false; //we only support one card, at the moment.
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
sc->sn.speed = 48000;
if (sc->sn.samplebits > 16)
sc->sn.samplebits = 16;
if (sc->sn.samplebytes > 2)
{
sc->sn.samplebytes = 2;
sc->sn.sampleformat = QSF_S16;
}
else
sc->sn.samplebits = 8;
{
sc->sn.samplebytes = 1;
sc->sn.sampleformat = QSF_U8;
}
memset (&format, 0, sizeof(format));
format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = sc->sn.numchannels;
format.wBitsPerSample = sc->sn.samplebits;
format.wBitsPerSample = sc->sn.samplebytes*8;
format.nSamplesPerSec = sc->sn.speed;
format.nBlockAlign = format.nChannels
*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.buffer = (unsigned char *) wh->lpData;
Q_strncpyz(sc->name, "wav out", sizeof(sc->name));
@ -364,5 +364,11 @@ int WAV_InitCard (soundcardinfo_t *sc, int cardnum)
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

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.
//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.
//also no reverb (fixme: XAUDIO2FX_REVERB_PARAMETERS).
//dxsdk = 2.7 = win7+
//w8sdk = 2.8 = win8+
//w10sdk = 2.9 = win10+
//dxsdk = 2.7 = win7+ (redistributable)
//w8sdk = 2.8 = win8+ (system component, not available on vista/win7)
//w10sdk = 2.9 = win10+ (system component, not available on vista/win7/win8/win8.1)
#if defined(AVAIL_XAUDIO2) && !defined(SERVERONLY)
#include "winquake.h"
@ -58,11 +59,11 @@ static void XAUDIO_Submit(soundcardinfo_t *sc, int start, int end)
XAUDIO2_BUFFER buf;
//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
start *= sc->sn.numchannels*sc->sn.samplebits/8;
end *= sc->sn.numchannels*sc->sn.samplebits/8;
start *= sc->sn.numchannels*sc->sn.samplebytes;
end *= sc->sn.numchannels*sc->sn.samplebytes;
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
memset(&buf, 0, sizeof(buf));
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.
Con_Printf("XAudio2 underrun\n");
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);
if ((qbyte*)buf.pAudioData + buf.AudioBytes > xa->bufferstart + buffersize)
@ -114,12 +115,12 @@ void WINAPI XAUDIO_CB_OnVoiceError (IXAudio2VoiceCallback *ths, void* pBufferCon
static IXAudio2VoiceCallbackVtbl cbvtbl =
{
XAUDIO_CB_OnVoiceProcessingPassStart,
XAUDIO_CB_OnVoiceProcessingPassEnd,
XAUDIO_CB_OnStreamEnd,
XAUDIO_CB_OnBufferStart,
XAUDIO_CB_OnBufferEnd,
XAUDIO_CB_OnLoopEnd,
XAUDIO_CB_OnVoiceError
XAUDIO_CB_OnVoiceProcessingPassEnd,
XAUDIO_CB_OnStreamEnd,
XAUDIO_CB_OnBufferStart,
XAUDIO_CB_OnBufferEnd,
XAUDIO_CB_OnLoopEnd,
XAUDIO_CB_OnVoiceError
};
static qboolean QDECL XAUDIO_InitCard(soundcardinfo_t *sc, const char *cardname)
@ -141,7 +142,7 @@ static qboolean QDECL XAUDIO_InitCard(soundcardinfo_t *sc, const char *cardname)
wfmt.Format.wFormatTag = WAVE_FORMAT_PCM;
wfmt.Format.nChannels = sc->sn.numchannels;
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.nAvgBytesPerSec = wfmt.Format.nSamplesPerSec * wfmt.Format.nBlockAlign;
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;
xa->bufferstart = BZ_Malloc(sc->sn.samples * (sc->sn.samplebits/8));
xa->bufferstart = BZ_Malloc(sc->sn.samples * sc->sn.samplebytes);
if (xa->bufferstart)
{

View File

@ -80,14 +80,21 @@ typedef struct sfxcache_s
typedef struct
{
// qboolean gamealive;
// qboolean soundalive;
// qboolean splitbuffer;
int numchannels; // this many samples per frame
int samples; // mono samples in buffer (individual, non grouped)
// int submission_chunk; // don't mix less than this #
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
unsigned char *buffer; // pointer to mixed pcm buffer (not directly used by mixer)
} 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
// ====================================================================
@ -338,8 +340,9 @@ typedef struct
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 *Enumerate) (void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename));
void (QDECL *RegisterCvars) (void);
} sounddriver_t;
typedef int (*sounddriver) (soundcardinfo_t *sc, int cardnum);
/*typedef int (*sounddriver) (soundcardinfo_t *sc, int cardnum);
extern sounddriver pOPENAL_InitCard;
extern sounddriver pDSOUND_InitCard;
extern sounddriver pALSA_InitCard;
@ -348,6 +351,7 @@ extern sounddriver pOSS_InitCard;
extern sounddriver pSDL_InitCard;
extern sounddriver pWAV_InitCard;
extern sounddriver pAHI_InitCard;
*/
struct soundcardinfo_s { //windows has one defined AFTER directsound
char name[256]; //a description of the card.

View File

@ -11,6 +11,9 @@
#ifndef WIN32
#include <fcntl.h>
#include <sys/stat.h>
#ifdef __unix__
#include <unistd.h>
#endif
#else
#include <direct.h>
#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);
break;
default:
Con_Printf("Error %u loading %s\n", err, name);
Con_Printf("Error %u loading %s\n", (unsigned)err, name);
break;
}
@ -1845,6 +1845,7 @@ char *Sys_GetClipboard(void)
{
cp = (cp&0x3ff)<<10;
cp |= *clipWText++ & 0x3ff;
cp += 0x10000;
}
else
cp = 0xFFFDu;
@ -4258,7 +4259,7 @@ void *WIN_CreateCursor(const char *filename, float hotx, float hoty, float scale
COM_FileExtension(filename, ext, sizeof(ext));
Q_strncatz(aname, "_alpha.", sizeof(aname));
Q_strncatz(aname, ext, sizeof(aname));
alphsize = FS_LoadFile(filename, &alph);
alphsize = FS_LoadFile(filename, (void**)&alph);
if (alph)
{
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);
extern vec3_t nametagorg[MAX_CLIENTS];
extern qboolean nametagseen[MAX_CLIENTS];
extern cvar_t r_showshaders, r_showfields, r_projection;
void R_DrawNameTags(void)
{
int i;
@ -1866,8 +1867,6 @@ void R_DrawNameTags(void)
char *ourteam;
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.
return;
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)
{
trace_t trace;
@ -2012,6 +2012,7 @@ void R_DrawNameTags(void)
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);
}
#endif
if (((!r_refdef.playerview->spectator && !cls.demoplayback) || !scr_autoid.ival) && (!cl.teamplay || !scr_autoid_team.ival))
return;

View File

@ -108,13 +108,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define AVAIL_OGGVORBIS
#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_DSOUND
#define AVAIL_WASAPI
#endif
#ifdef WINRT
#define AVAIL_XAUDIO2
//#define AVAIL_XAUDIO2 //gcc doesn't provide any headers
#endif
#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)
//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.
// #define HAVE_DTLS
//FIXME: we don't cache server certs
#define HAVE_DTLS
#endif
#if defined(USE_SQLITE) || defined(USE_MYSQL)

View File

@ -2137,9 +2137,9 @@ struct cmdargcompletionctx_s
match_t *match;
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;
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;
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;
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] == '{')
{
*error = 0;
*out = (char*)in + 2;
charcode = 0;
while (ishexcode(**out))
@ -3344,10 +3346,6 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t
}
continue;
}
else if (str[1] == 'a')
{
ext ^= CON_2NDCHARSETTEXT;
}
else if (str[1] == 'b')
{
ext ^= CON_BLINKTEXT;
@ -3359,7 +3357,7 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t
else
ext = defaultflags;
}
else if (str[1] == 'm')
else if (str[1] == 'm'||str[1] == 'a')
ext ^= CON_2NDCHARSETTEXT;
else if (str[1] == 'h')
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 Q2COM_BlockSequenceCRCByte (qbyte *base, int length, int sequence);
int SHA1(char *digest, int maxdigestsize, const char *string, int stringlen);
int SHA1_HMAC(unsigned char *digest, int maxdigestsize, const unsigned char *data, int datalen, const unsigned char *key, int keylen);
typedef size_t hashfunc_t(unsigned char *digest, size_t maxdigestsize, size_t numstrings, const unsigned char **strings, size_t *stringlens);
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);
char *version_string(void);
@ -769,6 +773,7 @@ void Log_Init(void);
void Log_ShutDown(void);
void IPLog_Add(const char *ip, const char *name); //for associating player ip addresses with names.
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*/

View File

@ -44,6 +44,7 @@
#define MULTITHREAD //misc basic multithreading - dsound, downloads, basic stuff that's unlikely to have race conditions.
#endif
#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 NOLEGACY //just spike trying to kill off crappy crap...

View File

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

View File

@ -856,7 +856,7 @@ uInt dem_uncompress_block(decodectx_t *dc)
if (cfields & 4) { cam2 += getlong(inptr); inptr += 4; }
outlen = 0;
insert_msg(&a1,4);
a1 = 0/*length*/; insert_msg(&a1,4);
a1 = cnvlong(cam0); insert_msg(&a1,4);
a1 = cnvlong(cam1); 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))
{
// DWORD err = GetLastError();
// ERROR_INVALID_USER_BUFFER or ERROR_NOT_ENOUGH_MEMORY
return 0;
}
return written;
}
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))
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)
{
IPLog_Dump("iplog.txt");
@ -660,4 +825,10 @@ void Log_Init(void)
if (COM_CheckParm("-condebug"))
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[3] = ((buf.cursize+4+sizeof(integ)-20)>>0)&0xff;
//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(20)); //sha1 key length
SZ_Write(&buf, integ, sizeof(integ)); //integrity data
@ -1369,7 +1369,7 @@ qboolean ICE_WasStun(netsrc_t netsrc)
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.
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)))
{
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[3] = ((buf.cursize+4+sizeof(integrity)-20)>>0)&0xff;
//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(sizeof(integrity))); //sha1 key length
SZ_Write(&buf, integrity, sizeof(integrity)); //integrity data

View File

@ -11,6 +11,8 @@
#endif
#ifdef HAVE_GNUTLS
#define privname "privkey.pem"
#define pubname "cert.pem"
#if defined(_WIN32) && !defined(MINGW) && 0
@ -94,36 +96,38 @@ typedef enum
typedef int (VARGS gnutls_certificate_verify_function)(gnutls_session_t session);
#else
#include <gnutls/gnutls.h>
#if GNUTLS_VERSION_MAJOR >= 3 && defined(HAVE_DTLS)
#include <gnutls/dtls.h>
#else
#undef HAVE_DTLS
#endif
#define gnutls_connection_end_t unsigned int
#include <gnutls/gnutls.h>
#include <gnutls/abstract.h>
#include <gnutls/x509.h>
#if GNUTLS_VERSION_MAJOR >= 3 && defined(HAVE_DTLS)
#include <gnutls/dtls.h>
#else
#undef HAVE_DTLS
#endif
#define gnutls_connection_end_t unsigned int
#if GNUTLS_VERSION_MAJOR < 3 || (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR < 3)
#define GNUTLS_SONUM 26 //cygwin or something.
#endif
#if GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR == 3
#define GNUTLS_SOPREFIX "-deb0" //not sure what this is about.
#define GNUTLS_SONUM 28 //debian jessie
#endif
#if GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR == 4
#define GNUTLS_SONUM 30 //ubuntu 16.04
#endif
#if GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR == 5
#define GNUTLS_SONUM 30 //debian stretch
#endif
#if GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR > 5
#define GNUTLS_SONUM 30 //no idea what the future holds. maybe we'll be lucky...
#endif
#ifndef GNUTLS_SONUM
#pragma message "GNUTLS version not recognised. Will probably not be loadable."
#endif
#ifndef GNUTLS_SOPREFIX
#define GNUTLS_SOPREFIX
#endif
#if GNUTLS_VERSION_MAJOR < 3 || (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR < 3)
#define GNUTLS_SONUM 26 //cygwin or something.
#endif
#if GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR == 3
#define GNUTLS_SOPREFIX "-deb0" //not sure what this is about.
#define GNUTLS_SONUM 28 //debian jessie
#endif
#if GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR == 4
#define GNUTLS_SONUM 30 //ubuntu 16.04
#endif
#if GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR == 5
#define GNUTLS_SONUM 30 //debian stretch
#endif
#if GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR > 5
#define GNUTLS_SONUM 30 //no idea what the future holds. maybe we'll be lucky...
#endif
#ifndef GNUTLS_SONUM
#pragma message "GNUTLS version not recognised. Will probably not be loadable."
#endif
#ifndef GNUTLS_SOPREFIX
#define GNUTLS_SOPREFIX
#endif
#endif
#if GNUTLS_VERSION_MAJOR >= 3
@ -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_session_get_ptr)(gnutls_session_t session);
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
static int (VARGS *qgnutls_certificate_set_x509_system_trust)(gnutls_certificate_credentials_t cred);
#else
@ -172,6 +177,7 @@ static int (VARGS *qgnutls_x509_crt_import)(gnutls_x509_crt_t cert, const gnutls
#endif
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 void *(VARGS *qgnutls_malloc)(size_t);
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);
@ -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);
#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)
{
#ifdef GNUTLS_HAVE_SYSTEMTRUST
@ -217,6 +244,30 @@ static qboolean Init_GNUTLS(void)
#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 \
GNUTLS_FUNC(gnutls_bye) \
GNUTLS_FUNC(gnutls_perror) \
@ -243,7 +294,8 @@ static qboolean Init_GNUTLS(void)
GNUTLS_FUNC(gnutls_certificate_type_get) \
GNUTLS_FUNC(gnutls_free) \
GNUTLS_FUNC(gnutls_server_name_set) \
GNUTLS_DTLS_STUFF
GNUTLS_DTLS_STUFF \
GNUTLS_X509_STUFF
#ifdef GNUTLS_DYNAMIC
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_session_get_ptr, "gnutls_session_get_ptr"},
{(void**)&qgnutls_session_set_ptr, "gnutls_session_set_ptr"},
{(void**)&qgnutls_session_channel_binding, "gnutls_session_channel_binding"},
#ifdef GNUTLS_HAVE_SYSTEMTRUST
{(void**)&qgnutls_certificate_set_x509_system_trust, "gnutls_certificate_set_x509_system_trust"},
#else
@ -291,6 +344,7 @@ static qboolean Init_GNUTLS(void)
#endif
{(void**)&qgnutls_certificate_get_peers, "gnutls_certificate_get_peers"},
{(void**)&qgnutls_certificate_type_get, "gnutls_certificate_type_get"},
{(void**)&qgnutls_malloc, "gnutls_malloc"},
{(void**)&qgnutls_free, "gnutls_free"},
{(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"},
#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}
};
@ -375,6 +450,36 @@ static struct
{"triptohell.info", sizeof(triptohell_certdata), 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)
{
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))
return 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;
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))
return 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;
if (certstatus & GNUTLS_CERT_SIGNER_NOT_FOUND)
@ -708,6 +817,137 @@ static gnutls_certificate_credentials_t xcred[2];
static gnutls_datum_t cookie_key;
#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)
{
static int initstatus[2];
@ -742,6 +982,10 @@ qboolean SSL_InitGlobal(qboolean isserver)
if (isserver)
{
#if 1
if (!SSL_LoadPrivateCert(xcred[isserver]))
initstatus[isserver] = -1;
#else
int ret = -1;
char keyfile[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);
initstatus[isserver] = -1;
}
#endif
}
else
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;
}
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
@ -1017,13 +1284,13 @@ static const dtlsfuncs_t dtlsfuncs_gnutls =
GNUDTLS_Received,
GNUDTLS_Timeouts,
};
const dtlsfuncs_t *DTLS_InitServer(void)
const dtlsfuncs_t *GNUDTLS_InitServer(void)
{
if (!SSL_InitGlobal(true))
return NULL; //unable to init a server certificate. don't allow dtls to init.
return &dtlsfuncs_gnutls;
}
const dtlsfuncs_t *DTLS_InitClient(void)
const dtlsfuncs_t *GNUDTLS_InitClient(void)
{
return &dtlsfuncs_gnutls;
}

File diff suppressed because it is too large Load Diff

View File

@ -5033,7 +5033,7 @@ ftenet_generic_connection_t *FTENET_TCPConnect_EstablishConnection(qboolean isse
ftenet_tcpconnect_connection_t *newcon;
unsigned long _true = true;
int newsocket;
SOCKET newsocket;
qboolean tls = (adr.prot == NP_TLS || adr.prot == NP_WSS);
#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);
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
vfsfile_t *FS_OpenTCPSocket(SOCKET socket, qboolean conpending, const char *peername); //conpending allows us to reject any writes until the connection has succeeded
#endif

View File

@ -1093,6 +1093,36 @@ qintptr_t VARGS Plug_Net_SetTLSClient(void *offset, quintptr_t mask, const qintp
}
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
@ -1607,6 +1637,7 @@ void Plug_Initialise(qboolean fromgamedir)
Plug_RegisterBuiltin("Net_TCPConnect", Plug_Net_TCPConnect, 0);
#ifdef HAVE_SSL
Plug_RegisterBuiltin("Net_SetTLSClient", Plug_Net_SetTLSClient, 0);
Plug_RegisterBuiltin("Net_GetTLSBinding", Plug_Net_GetTLSBinding, 0);
#endif
Plug_RegisterBuiltin("Net_Recv", Plug_Net_Recv, 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;
ptr = (char*)COM_QuotedString(text, buffer, sizeof(buffer)-10, false);
ptr += strlen(ptr);
*ptr = ' ';
*ptr++ = ' ';
COM_QuotedString(info, ptr, sizeof(buffer)-(ptr-buffer), 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;
#else
world_t *w = prinst->parms->user;
struct dl_download *dl;
struct dl_download *dl = NULL;
const unsigned char *url = PR_GetStringOfs(prinst, OFS_PARM0);
float id = G_FLOAT(OFS_PARM1);
const char *mimetype = (prinst->callargc >= 3)?PR_GetStringOfs(prinst, OFS_PARM2):"";
const char *dataorsep = (prinst->callargc >= 4)?PR_GetStringOfs(prinst, OFS_PARM3):"";
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)
{
@ -4528,20 +4528,57 @@ void QCBUILTIN PF_uri_get (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob
if (*mimetype)
{
const char *data;
Con_DPrintf("PF_uri_post(%s,%g)\n", url, id);
if (strbufid)
{
//convert the string buffer into a simple string using dataorsep as a separator
//not supported at this time
dl = NULL;
Con_DPrintf("PF_uri_post: stringbuffers not supported\n");
size_t bufno = strbufid-BUFSTRBASE;
if (bufno < strbufmax && strbuflist[bufno].prinst == prinst)
{
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
{
//simple data post.
data = dataorsep;
dl = HTTP_CL_Put(url, mimetype, data, strlen(data), PR_uri_get_callback);
dl = HTTP_CL_Put(url, mimetype, dataorsep, strlen(dataorsep), PR_uri_get_callback);
}
}
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;
if (maxdigestsize < DIGEST_SIZE)
@ -192,6 +192,21 @@ int SHA1(char *digest, int maxdigestsize, const char *string, int stringlen)
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
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,
const unsigned char *data, int datalen,
const unsigned char *key, int keylen)
typedef size_t hashfunc_t(unsigned char *digest, size_t maxdigestsize, size_t numstrings, const unsigned char **strings, size_t *stringlens);
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)
{
SHA1_CTX inner;
SHA1_CTX outer;
char optkeybuf[DIGEST_SIZE];
char block[64];
char innerhash[DIGEST_SIZE];
#define HMAC_DIGEST_MAXSIZE 20
char optkeybuf[HMAC_DIGEST_MAXSIZE];
char innerhash[HMAC_DIGEST_MAXSIZE];
if (maxdigestsize < DIGEST_SIZE)
return 0;
char block[64];
int innerhashsize;
/* Reduce the key's size, so that it is never larger than a block. */
if (keylen > 64)
if (keylen > sizeof(block))
{
SHA1_CTX keyhash;
SHA1Init (&keyhash);
SHA1Update (&keyhash, key, keylen);
SHA1Final (optkeybuf, &keyhash);
key = optkeybuf;
keylen = sizeof(optkeybuf);
keylen = hashfunc(optkeybuf, sizeof(optkeybuf), 1, &key, &keylen);
key=optkeybuf;
}
/* Compute INNERHASH from KEY and IN. */
SHA1Init (&inner);
memset (block, IPAD, sizeof (block));
memxor (block, key, keylen);
SHA1Update (&inner, block, 64);
SHA1Update (&inner, data, datalen);
SHA1Final (innerhash, &inner);
{
const unsigned char *strings_i[2] = {block, data};
size_t stringlens_i[2] = {sizeof(block), datalen};
innerhashsize = hashfunc(innerhash, sizeof(innerhash), 2, strings_i, stringlens_i);
}
/* Compute result from KEY and INNERHASH. */
SHA1Init (&outer);
memset (block, OPAD, sizeof (block));
memxor (block, key, keylen);
SHA1Update (&outer, block, 64);
SHA1Update (&outer, innerhash, 20);
SHA1Final (digest, &outer);
return DIGEST_SIZE;
{
const unsigned char *strings_o[2] = {block, innerhash};
size_t stringlens_o[2] = {sizeof(block), innerhashsize};
return hashfunc(digest, maxdigestsize, 2, strings_o, stringlens_o);
}
}

View File

@ -1148,8 +1148,8 @@ static void SelectPassTexture(unsigned int tu, const shaderpass_t *pass)
static void colourgenbyte(const shaderpass_t *pass, int cnt, byte_vec4_t *srcb, vec4_t *srcf, byte_vec4_t *dst, const mesh_t *mesh)
{
#define D3DCOLOR unsigned int
#define D3DCOLOR_ARGB(a,r,g,b) ((D3DCOLOR)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))
#define D3DCOLOR_COLORVALUE(r,g,b,a) D3DCOLOR_RGBA((DWORD)((r)*255.f),(DWORD)((g)*255.f),(DWORD)((b)*255.f),(DWORD)((a)*255.f))
#define D3DCOLOR_ARGB(a,r,g,b) ((D3DCOLOR)((((a)&0xff)<<24)|(((r)&0xff)<<16)|(((g)&0xff)<<8)|((b)&0xff)))
#define D3DCOLOR_COLORVALUE(r,g,b,a) D3DCOLOR_RGBA((DWORD)((r)*255.f),(DWORD)((g)*255.f),(DWORD)((b)*255.f),(DWORD)((a)*255.f))
#define D3DCOLOR_RGBA(r,g,b,a) D3DCOLOR_ARGB(a,r,g,b)
D3DCOLOR block;

View File

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

View File

@ -2,7 +2,7 @@
#include "glquake.h"
#include "gl_draw.h"
#ifdef D3D8QUAKE
//#define FIXME
//#define FIXME //for Eukara to fix, if he ever wants to get the d3d8 renderer working fully.
#include "shader.h"
#if !defined(HMONITOR_DECLARED) && (WINVER < 0x0500)
#define HMONITOR_DECLARED
@ -48,6 +48,7 @@ extern float d3d_trueprojection[16];
static void BE_RotateForEntity (const entity_t *e, const model_t *mod);
/*========================================== tables for deforms =====================================*/
#define R_FastSin(x) sin((x)*(2*M_PI))
#define frand() (rand()*(1.0/RAND_MAX))
#define FTABLE_SIZE 1024
#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_inversesawtoothtable[FTABLE_SIZE];
#if FIXME
static float *FTableForFunc ( unsigned int func )
{
switch (func)
@ -82,6 +84,7 @@ static float *FTableForFunc ( unsigned int func )
//bad values allow us to crash (so I can debug em)
return NULL;
}
#endif
static void FTable_Init(void)
{
@ -110,6 +113,7 @@ static void FTable_Init(void)
}
}
#if FIXME
typedef vec3_t mat3_t[3];
static mat3_t axisDefault={{1, 0, 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));
}
#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)
{
D3DCOLOR block;
@ -1025,9 +1031,11 @@ static unsigned int BE_GenerateColourMods(unsigned int vertcount, const shaderpa
#endif
return ret;
}
#endif
/*********************************************************************************************************/
/*========================================== texture coord generation =====================================*/
#if FIXME
static void tcgen_environment(float *st, unsigned int numverts, float *xyz, float *normal)
{
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 cost, sint;
int j;
#define R_FastSin(x) sin((x)*(2*M_PI))
switch (tcmod->type)
{
case SHADER_TCMOD_ROTATE:
@ -1188,7 +1195,7 @@ static void GenerateTCMods(const shaderpass_t *pass, float *dest)
mesh_t *mesh;
unsigned int mno;
// unsigned int fvertex = 0; //unused variable
int i;
// int i;
float *src;
float *out;
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;
}
}
#endif
/*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*/
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);
@ -1590,6 +1597,7 @@ static void BE_SubmitMeshChain(unsigned int firstvert, unsigned int vertcount, u
RQuantAdd(RQUANT_PRIMITIVEINDICIES, idxcount);
}
#ifdef FIXME
static void R_FetchPlayerColour(unsigned int cv, vec3_t rgb)
{
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)
{
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++)
{
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->lmst_array[0][i], vbovdata->tc[1]);
Vector4Scale(m->colors4f_array[0][i], 255, vbovdata->colorsb);

View File

@ -23,7 +23,7 @@
>
<Tool
Name="VCNMakeTool"
BuildCommandLine="cd $(InputDir)\.. &amp;&amp; vcify make web-dbg"
BuildCommandLine="cd $(InputDir)\.. &amp;&amp; vcify make web-dbg DEPCC="
ReBuildCommandLine=""
CleanCommandLine="cd $(InputDir)\.. &amp;&amp; vcify make clean -j8"
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|x64.ActiveCfg = Release|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}.MinGLDebug|Win32.ActiveCfg = Debug|Win32
{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)
{
int i;
struct vk_rendertarg *oldfbo = vk.rendertarg;
// struct vk_rendertarg *oldfbo = vk.rendertarg;
texid_t intex;
int pixels = 1;
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 FT_FREETYPE_H
#ifndef FT_LOAD_COLOR
#define FT_LOAD_COLOR (1L<<20)
#endif
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_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_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_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);
#else
typedef unsigned int FT_Pixel_Mode; //for consistency even without freetype support.
#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[] =
{
//0xe10X
@ -158,18 +173,25 @@ static const char *imgs[] =
"e16f"
};
#define FONTCHARS (1<<16)
#define FONTPLANES (1<<2) //this is total, not per font.
#define PLANEIDXTYPE unsigned short
#define CHARIDXTYPE unsigned short
#define FONT_MAXCHARS 0x110000 //as defined by UTF-16, and thus applied to all unicode because UTF-16 is the crappy limited one.
#define FONTBLOCKS ((FONT_MAXCHARS+FONTBLOCKSIZE-1)/FONTBLOCKSIZE)
#define FONTBLOCKSIZE 0x100 //must be power-of-two
#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 BITMAPPLANE ((1<<(8*sizeof(PLANEIDXTYPE)))-2)
#define DEFAULTPLANE ((1<<(8*sizeof(PLANEIDXTYPE)))-3)
#define SINGLEPLANE ((1<<(8*sizeof(PLANEIDXTYPE)))-4)
#define TRACKERIMAGE ((1<<(8*sizeof(PLANEIDXTYPE)))-5)
#define PLANEWIDTH (1<<8)
#define PLANEHEIGHT PLANEWIDTH
#define INVALIDPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-1)
#define BITMAPPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-2)
#define DEFAULTPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-3)
#define SINGLEPLANE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-4)
#define TRACKERIMAGE ((1<<(8*sizeof(FIMAGEIDXTYPE)))-5)
#define FIMAGEWIDTH (1<<10)
#define FIMAGEHEIGHT FIMAGEWIDTH
#define FONTPLANES FONTIMAGES
#define PLANEWIDTH FIMAGEWIDTH
#define PLANEHEIGHT FIMAGEHEIGHT
#ifdef AVAIL_FREETYPE
//windows' font linking allows linking multiple extra fonts to a main font.
@ -203,18 +225,20 @@ typedef struct font_s
{
struct charcache_s *nextchar;
PLANEIDXTYPE texplane;
FIMAGEIDXTYPE texplane;
unsigned char advance; //how wide this char is, when drawn
char pad;
unsigned short block; //to quickly find the char again
unsigned char bmx;
unsigned char bmy;
//glyph offset+sizes. I guess these are not strictly needed, but whatever
unsigned short bmx;
unsigned short bmy;
unsigned char bmw;
unsigned char bmh;
//positions inside the atlas
short top;
short left;
} chars[FONTCHARS];
} *chars[FONTBLOCKS];
char name[MAX_OSPATH];
short charheight;
@ -231,15 +255,15 @@ typedef struct font_s
//shared between fonts.
typedef struct {
texid_t texnum[FONTPLANES];
texid_t texnum[FONTIMAGES];
texid_t defaultfont;
texid_t trackerimage;
unsigned char plane[PLANEWIDTH*PLANEHEIGHT][4]; //tracks the current plane
PLANEIDXTYPE activeplane;
unsigned char planerowx;
unsigned char planerowy;
unsigned char planerowh;
FIMAGEIDXTYPE activeplane;
unsigned short planerowx;
unsigned short planerowy;
unsigned short planerowh;
qboolean planechanged;
struct charcache_s *oldestchar;
@ -492,14 +516,54 @@ void Font_FlushPlane(void)
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.
//note: make sure it doesn't already exist or things will get cyclic
//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;
unsigned char *out;
struct charcache_s *c = &f->chars[charidx];
struct charcache_s *c = Font_GetCharStore(f, charidx);
int pad = 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;
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 (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;
}
}
else
{
else if (pixelmode == FT_PIXEL_MODE_RGBA_SA)
{ //rgba font
pitch*=4;
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;
}
}
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;
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.
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;
}
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)
{
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)
{
c->left = 0;
@ -704,7 +890,7 @@ static struct charcache_s *Font_TryLoadGlyph(font_t *f, CHARIDXTYPE charidx)
/*make tab invisible*/
if (charidx == '\t' || charidx == '\n')
{
c = &f->chars[charidx];
c = &f->chars[charidx/FONTBLOCKSIZE][charidx&FONTBLOCKMASK];
c->left = 0;
c->advance = f->charheight;
c->top = 0;
@ -724,41 +910,56 @@ static struct charcache_s *Font_TryLoadGlyph(font_t *f, CHARIDXTYPE charidx)
for (file = 0; file < f->ftfaces; file++)
{
FT_Face face = f->face[file]->face;
if (f->face[file]->activeheight != f->charheight)
{
f->face[file]->activeheight = f->charheight;
pFT_Set_Pixel_Sizes(face, 0, f->charheight);
}
if (charidx == 0xfffe || pFT_Get_Char_Index(face, charidx)) //ignore glyph 0 (undefined)
if (pFT_Load_Char(face, charidx, FT_LOAD_RENDER) == 0)
{
FT_GlyphSlot slot;
FT_Bitmap *bm;
slot = face->glyph;
bm = &slot->bitmap;
c = Font_LoadGlyphData(f, charidx, true, bm->buffer, bm->width, bm->rows, bm->pitch);
if (c)
// if (f->face[file]->activeheight)
if (f->face[file]->activeheight != f->charheight)
{
c->advance = slot->advance.x >> 6;
c->left = slot->bitmap_left;
c->top = f->charheight*3/4 - slot->bitmap_top;
return c;
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);
}
if (charidx == 0xfffe || pFT_Get_Char_Index(face, charidx)) //ignore glyph 0 (undefined)
if (pFT_Load_Char(face, charidx, FT_LOAD_RENDER|FT_LOAD_COLOR) == 0)
{
FT_GlyphSlot slot;
FT_Bitmap *bm;
slot = face->glyph;
bm = &slot->bitmap;
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)
{
c->advance = slot->advance.x >> 6;
c->left = slot->bitmap_left;
c->top = f->charheight*3/4 - slot->bitmap_top;
return c;
}
}
}
}
}
}
#endif
if (charidx == '\r')
{
if (f->chars[charidx|0xe000].texplane != INVALIDPLANE)
{
f->chars[charidx] = f->chars[charidx|0xe000];
return &f->chars[charidx];
}
}
return Font_CopyChar(f, charidx|0xe000, charidx);
return NULL;
}
@ -768,12 +969,12 @@ static struct charcache_s *Font_GetChar(font_t *f, unsigned int codepoint)
{
CHARIDXTYPE charidx;
struct charcache_s *c;
if (codepoint > CON_CHARMASK)
if (codepoint > FONT_MAXCHARS)
charidx = 0xfffd;
else
charidx = codepoint;
c = &f->chars[charidx];
if (c->texplane == INVALIDPLANE)
c = Font_GetCharIfLoaded(f, charidx);
if (!c)
{
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];
if (charidx != '?')
{
c = &f->chars[charidx];
if (c->texplane == INVALIDPLANE)
c = Font_GetCharIfLoaded(f, charidx);
if (!c)
c = Font_TryLoadGlyph(f, charidx);
}
}
if (!c)
c = Font_LoadPlaceholderGlyph(f, charidx);
if (!c)
{
charidx = 0xfffd; //unicode's replacement char
c = &f->chars[charidx];
if (c->texplane == INVALIDPLANE)
c = Font_GetCharIfLoaded(f, charidx);
if (!c)
c = Font_TryLoadGlyph(f, charidx);
}
if (!c)
{
charidx = '?'; //meh
c = &f->chars[charidx];
if (c->texplane == INVALIDPLANE)
c = Font_GetCharIfLoaded(f, charidx);
if (!c)
c = Font_TryLoadGlyph(f, charidx);
}
}
@ -840,6 +1043,9 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, const char *fontfil
if (!*fontfilename)
return false;
if (height < 1)
height = 1;
//ran out of font slots.
if (f->ftfaces == MAX_FTFACES)
return false;
@ -856,12 +1062,24 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, const char *fontfil
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[] =
{
{(void**)&pFT_Init_FreeType, "FT_Init_FreeType"},
{(void**)&pFT_Load_Char, "FT_Load_Char"},
{(void**)&pFT_Get_Char_Index, "FT_Get_Char_Index"},
{(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_Memory_Face, "FT_New_Memory_Face"},
{(void**)&pFT_Init_FreeType, "FT_Init_FreeType"},
@ -873,7 +1091,9 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, const char *fontfil
triedtoloadfreetype = true;
#ifdef _WIN32
fontmodule = Sys_LoadLibrary("freetype6", ft2funcs);
fontmodule = Sys_LoadLibrary("libfreetype-6", ft2funcs);
if (!fontmodule)
fontmodule = Sys_LoadLibrary("freetype6", ft2funcs);
#else
fontmodule = Sys_LoadLibrary("libfreetype.so.6", ft2funcs);
#endif
@ -882,6 +1102,7 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, const char *fontfil
Con_DPrintf("Couldn't load freetype library.\n");
return false;
}
#endif
error = pFT_Init_FreeType(&fontlib);
if (error)
{
@ -952,7 +1173,13 @@ qboolean Font_LoadFreeTypeFont(struct font_s *f, int height, const char *fontfil
#endif
if (!error)
{
error = pFT_Set_Pixel_Sizes(face, 0, height);
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);
if (!error)
{
/*success!*/
@ -1181,11 +1408,13 @@ static texid_t Font_LoadDefaultConchars(void)
COM_WorkerPartialSync(tex, &tex->status, TEX_LOADING);
if (TEXLOADED(tex))
return tex;
#ifdef HEXEN2
tex = Font_LoadHexen2Conchars(true);
if (tex && tex->status == TEX_LOADING)
COM_WorkerPartialSync(tex, &tex->status, TEX_LOADING);
if (TEXLOADED(tex))
return tex;
#endif
tex = Font_LoadFallbackConchars();
if (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;
int height = ((vheight * vid.rotpixelheight)/vid.height) + 0.5;
char facename[MAX_QPATH];
struct charcache_s *c;
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++)
{
f->chars[i].advance = (height*3)/4;
f->chars[i].left = 0;
f->chars[i].top = 0;
f->chars[i].nextchar = 0; //these chars are not linked in
f->chars[i].pad = 0;
f->chars[i].texplane = BITMAPPLANE; /*if its a 'raster' font, don't use the default chars, always use the raster images*/
c = Font_GetCharStore(f, i);
c->advance = (height*3)/4;
c->left = 0;
c->top = 0;
c->nextchar = 0; //these chars are not linked in
c->texplane = BITMAPPLANE; /*if its a 'raster' font, don't use the default chars, always use the raster images*/
if (i >= 'a' && i <= 'z')
{
f->chars[i].bmx = ((i - 64)&15)*8;
f->chars[i].bmy = ((i - 64)/16)*8;
f->chars[i].bmh = 8;
f->chars[i].bmw = 8;
c->bmx = ((i - 64)&15)*8;
c->bmy = ((i - 64)/16)*8;
c->bmh = 8;
c->bmw = 8;
}
else if (i >= 32 && i < 96)
{
f->chars[i].bmx = ((i - 32)&15)*8;
f->chars[i].bmy = ((i - 32)/16)*8;
f->chars[i].bmh = 8;
f->chars[i].bmw = 8;
c->bmx = ((i - 32)&15)*8;
c->bmy = ((i - 32)/16)*8;
c->bmh = 8;
c->bmw = 8;
}
else
{
f->chars[i].bmh = 0;
f->chars[i].bmw = 0;
f->chars[i].bmx = 0;
f->chars[i].bmy = 0;
c->bmh = 0;
c->bmw = 0;
c->bmx = 0;
c->bmy = 0;
}
}
for (i = 0xe000; i <= 0xe0ff; i++)
{
f->chars[i] = f->chars[i&0xff];
Font_CopyChar(f, i, i|0xe0ff);
}
return f;
}
@ -1486,21 +1714,22 @@ struct font_s *Font_LoadFont(float vheight, const char *fontfilename)
for ( ; i < 32; i++)
{
f->chars[i].texplane = INVALIDPLANE;
// f->chars[i].texplane = INVALIDPLANE;
}
/*force it to load, even if there's nothing there*/
for ( ; i < 128; i++)
{
f->chars[i].advance = f->charheight;
f->chars[i].bmh = PLANEWIDTH/16;
f->chars[i].bmw = PLANEWIDTH/16;
f->chars[i].bmx = (i&15)*(PLANEWIDTH/16);
f->chars[i].bmy = (i/16)*(PLANEWIDTH/16);
f->chars[i].left = 0;
f->chars[i].top = 0;
f->chars[i].nextchar = 0; //these chars are not linked in
f->chars[i].pad = 0;
f->chars[i].texplane = BITMAPPLANE;
c = Font_GetCharStore(f, i);
c->advance = f->charheight;
c->bmh = PLANEWIDTH/16;
c->bmw = PLANEWIDTH/16;
c->bmx = (i&15)*(PLANEWIDTH/16);
c->bmy = (i/16)*(PLANEWIDTH/16);
c->left = 0;
c->top = 0;
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;
}
for (; i < FONTCHARS; i++)
{
f->chars[i].texplane = INVALIDPLANE;
}
/*pack the default chars into it*/
for (i = 0xe000; i <= 0xe0ff; i++)
{
f->chars[i].advance = f->charheight;
f->chars[i].bmh = PLANEWIDTH/16;
f->chars[i].bmw = PLANEWIDTH/16;
f->chars[i].bmx = (i&15)*(PLANEWIDTH/16);
f->chars[i].bmy = ((i/16)*(PLANEWIDTH/16)) & 0xff;
f->chars[i].left = 0;
f->chars[i].top = 0;
f->chars[i].nextchar = 0; //these chars are not linked in
f->chars[i].pad = 0;
f->chars[i].texplane = defaultplane;
c = Font_GetCharStore(f, i);
c->advance = f->charheight;
c->bmh = PLANEWIDTH/16;
c->bmw = PLANEWIDTH/16;
c->bmx = ((i&15))*(PLANEWIDTH/16);
c->bmy = ((i&0xf0)/16)*(PLANEWIDTH/16);
c->left = 0;
c->top = 0;
c->nextchar = 0; //these chars are not linked in
c->texplane = defaultplane;
}
return f;
}
@ -1559,7 +1783,7 @@ void Font_Free(struct font_s *f)
for (link = &fontplanes.oldestchar; *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;
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 id;
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 (!mod->numentityinfo)
Mod_ParseEntities(mod);
@ -4902,7 +4907,8 @@ void QCBUILTIN PF_terrain_edit(pubprogfuncs_t *prinst, struct globalvars_s *pr_g
else
{
newvals = NULL;
mod->entityinfo[idx].id = 0;
if (idx < mod->numentityinfo)
mod->entityinfo[idx].id = 0;
}
#ifndef CLIENTONLY

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.
//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.
float lightmaplevel = -1;
COM_Parse(entlump);
if (!strcmp(com_token, "Version"))
@ -929,7 +928,7 @@ qboolean R_ImportRTLights(const char *entlump)
else if (entnum == 0 && !strcmp("lightmapbright", key))
{
//tenebrae compat. this overrides r_shadow_realtime_world_lightmap
lightmaplevel = atof(value);
r_shadow_realtime_world_lightmaps.value = atof(value);
}
}
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);
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);
}
if (pass->cin)
{
@ -4362,6 +4362,7 @@ void Shader_Programify (shader_t *s)
char *prog = NULL;
const char *mask;
char args[1024];
qboolean eightbit = false;
/* enum
{
T_UNKNOWN,
@ -4437,12 +4438,20 @@ void Shader_Programify (shader_t *s)
else if (modellighting)
{
pass = modellighting;
prog = "defaultskin";
eightbit = r_softwarebanding && (qrenderer == QR_OPENGL) && sh_config.progs_supported;
if (eightbit)
prog = "defaultskin#EIGHTBIT";
else
prog = "defaultskin";
}
else if (lightmap)
{
pass = modellighting;
prog = "defaultwall";
eightbit = r_softwarebanding && (qrenderer == QR_OPENGL || qrenderer == QR_VULKAN) && sh_config.progs_supported;
if (eightbit)
prog = "defaultwall#EIGHTBIT";
else
prog = "defaultwall";
}
else if (vertexlighting)
{
@ -4487,7 +4496,13 @@ void Shader_Programify (shader_t *s)
}
else
{
s->passes[s->numpasses++].texgen = T_GEN_DIFFUSE;
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->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 *qglMapBufferARB)(GLenum target, GLenum access);
GLboolean (APIENTRY *qglUnmapBufferARB)(GLenum target);
void *(APIENTRY *qglMapBufferRange)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access);
#endif
void (APIENTRY *qglGenVertexArrays)(GLsizei n, GLuint *arrays);
@ -855,6 +857,9 @@ void GL_CheckExtensions (void *(*getglfunction) (char *name))
qglMapBufferARB = 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
#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.
void GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name))
qboolean GL_Init(rendererstate_t *info, void *(*getglfunction) (char *name))
{
#ifndef GL_STATIC
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.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;
GL_Init(info, GLES_GetSymbol);
return true;
return GL_Init(info, GLES_GetSymbol);
}
#else
@ -174,8 +173,7 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
vid.pixelwidth = w;
vid.pixelheight = h;
GL_Init(info, GLES_GetSymbol);
return true;
return GL_Init(info, GLES_GetSymbol);
}
void GLVID_SwapBuffers(void)

View File

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

View File

@ -66,11 +66,11 @@ qboolean GLVID_Init(rendererstate_t *info, unsigned char *palette)
vid.numpages = 2;
// initialise the NSApplication and the screen
// initialise the NSApplication and the screen
initCocoa(info);
// calculate the conwidth AFTER the screen has been opened
// calculate the conwidth AFTER the screen has been opened
if (vid.pixelwidth <= 640)
{
vid.width = vid.pixelwidth;
@ -81,23 +81,23 @@ qboolean GLVID_Init(rendererstate_t *info, unsigned char *palette)
vid.width = vid.pixelwidth/2;
vid.height = vid.pixelheight/2;
}
if ((i = COM_CheckParm("-conwidth")) && i + 1 < com_argc)
{
vid.width = Q_atoi(com_argv[i + 1]);
// pick a conheight that matches with correct aspect
vid.height = vid.width * 3 / 4;
}
vid.width &= 0xfff8; // make it a multiple of eight
if ((i = COM_CheckParm("-conheight")) && i + 1 < com_argc)
vid.height = Q_atoi(com_argv[i + 1]);
if (vid.width < 320)
vid.width = 320;
if (vid.height < 200)
vid.height = 200;
@ -182,7 +182,7 @@ void GLVID_SwapBuffers(void)
void GLVID_SetDeviceGammaRamp(unsigned short *ramps)
{
cocoaGamma(ramps,ramps+256,ramps+512);
cocoaGamma(ramps,ramps+256,ramps+512);
}
void GLVID_ShiftPalette(unsigned char *p)

View File

@ -128,7 +128,6 @@ static qboolean VID_SetFullDIBMode (rendererstate_t *info); //-1 on bpp or hz fo
#endif
#ifdef WTHREAD
static HANDLE windowthread;
static void *windowmutex;
#endif
static DEVMODE gdevmode;
@ -821,6 +820,7 @@ static void Win32VK_Present(struct vkframe *theframe)
static qboolean Win32VK_AttachVulkan (rendererstate_t *info)
{ //make sure we can get a valid renderer.
const char *extnames[] = {VK_KHR_WIN32_SURFACE_EXTENSION_NAME, NULL};
#ifdef VK_NO_PROTOTYPES
hInstVulkan = NULL;
if (!hInstVulkan)
@ -835,7 +835,7 @@ static qboolean Win32VK_AttachVulkan (rendererstate_t *info)
vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) GetProcAddress(hInstVulkan, "vkGetInstanceProcAddr");
#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
@ -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);
#ifdef VKQUAKE
@ -1501,7 +1502,8 @@ static int GLVID_SetMode (rendererstate_t *info, unsigned char *palette)
stat = EGL_Init (info, palette, mainwindow, maindc);
if (stat)
GL_Init(info, &EGL_Proc);
if (GL_Init(info, &EGL_Proc))
return false;
}
break;
#endif
@ -2109,6 +2111,9 @@ void GLVID_SwapBuffers (void)
#endif
#ifdef VKQUAKE
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)
break;
#endif

View File

@ -1,14 +1,27 @@
#include "quakedef.h"
#include "glquake.h"
#include <SDL.h>
#include <SDL_syswm.h>
#ifdef GLQUAKE
#include "glquake.h"
#define OPENGL_SDL
#endif
#if SDL_MAJOR_VERSION >= 2
SDL_Window *sdlwindow;
static SDL_GLContext *sdlcontext;
#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;
#ifdef OPENGL_SDL
static SDL_GLContext *sdlcontext;
#endif
#else
SDL_Surface *sdlsurf;
SDL_Surface *sdlsurf;
#endif
extern cvar_t vid_vsync;
@ -34,7 +47,7 @@ unsigned short intitialgammaramps[3][256];
qboolean mouseactive;
extern qboolean mouseusedforgui;
#ifdef OPENGL_SDL
static void *GLVID_getsdlglfunction(char *functionname)
{
#ifdef GL_STATIC
@ -44,6 +57,7 @@ static void *GLVID_getsdlglfunction(char *functionname)
return SDL_GL_GetProcAddress(functionname);
#endif
}
#endif
#if SDL_MAJOR_VERSION >= 2
void *GLVID_CreateCursor (const char *filename, float hotx, float hoty, float scale)
@ -101,7 +115,7 @@ void GLVID_DestroyCursor (void *cursor)
#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;
@ -110,72 +124,91 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
SDL_SetVideoMode(0, 0, 0, 0); //to get around some SDL bugs
#endif
#ifdef OPENGL_SDL
if (qrenderer == QR_OPENGL)
{
#if SDL_MAJOR_VERSION >= 2
SDL_GL_LoadLibrary(NULL);
SDL_GL_LoadLibrary(NULL);
#endif
if (info->bpp >= 32)
{
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); //technically we don't always need stencil support.
}
else
{
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
}
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
if (info->stereo)
SDL_GL_SetAttribute(SDL_GL_STEREO, 1);
#if 0//SDL_MAJOR_VERSION >= 2
//FIXME: this stuff isn't part of info.
//this means it shouldn't be exposed to the menu or widely advertised.
if (*vid_gl_context_version.string)
{
int major, minor;
char *ver = vid_gl_context_version.string;
major = strtoul(ver, &ver, 10);
if (*ver == '.')
if (info->bpp >= 32)
{
ver++;
minor = strtoul(ver, &ver, 10);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); //technically we don't always need stencil support.
}
else
minor = 0;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS,
(vid_gl_context_debug.ival?SDL_GL_CONTEXT_DEBUG_FLAG:0) |
(vid_gl_context_forwardcompatible.ival?SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG:0) |
0);
{
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0);
}
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
if (vid_gl_context_es.ival)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
else if (vid_gl_context_compatibility.ival)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
else
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
if (info->stereo)
SDL_GL_SetAttribute(SDL_GL_STEREO, 1);
#if 0//SDL_MAJOR_VERSION >= 2
//FIXME: this stuff isn't part of info.
//this means it shouldn't be exposed to the menu or widely advertised.
if (*vid_gl_context_version.string)
{
int major, minor;
char *ver = vid_gl_context_version.string;
major = strtoul(ver, &ver, 10);
if (*ver == '.')
{
ver++;
minor = strtoul(ver, &ver, 10);
}
else
minor = 0;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor);
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS,
(vid_gl_context_debug.ival?SDL_GL_CONTEXT_DEBUG_FLAG:0) |
(vid_gl_context_forwardcompatible.ival?SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG:0) |
0);
if (vid_gl_context_es.ival)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
else if (vid_gl_context_compatibility.ival)
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
else
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
#endif
if (info->multisample)
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, info->multisample);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
if (info->multisample)
{
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, info->multisample);
SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
}
}
#endif
#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)
flags |= SDL_WINDOW_FULLSCREEN;
flags |= SDL_WINDOW_OPENGL;
flags |= SDL_WINDOW_RESIZABLE;
flags |= SDL_WINDOW_INPUT_GRABBED;
flags |= SDL_WINDOW_SHOWN;
@ -189,19 +222,38 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
return false;
}
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
sdlcontext = SDL_GL_CreateContext(sdlwindow);
if (!sdlcontext)
switch(qrenderer)
{
Con_Printf("Couldn't initialize GL context: %s\n", SDL_GetError());
return false;
#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);
if (!sdlcontext)
{
Con_Printf("Couldn't initialize GL context: %s\n", SDL_GetError());
return false;
}
}
#endif
{
SDL_Surface *iconsurf;
#include "bymorphed.h"
@ -230,10 +282,6 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
#endif
vid.activeapp = true;
GL_Init(info, GLVID_getsdlglfunction);
qglViewport (0, 0, vid.pixelwidth, vid.pixelheight);
mouseactive = false;
if (vid_isfullscreen)
IN_ActivateMouse();
@ -272,6 +320,17 @@ qboolean GLVID_Init (rendererstate_t *info, unsigned char *palette)
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)
{
vid.activeapp = false;
@ -281,7 +340,21 @@ void GLVID_DeInit (void)
#if SDL_MAJOR_VERSION >= 2
SDL_SetWindowGammaRamp(sdlwindow, NULL, NULL, NULL);
SDL_GL_DeleteContext(sdlcontext);
switch(qrenderer)
{
#ifdef OPENGL_SDL
case QR_OPENGL:
SDL_GL_DeleteContext(sdlcontext);
break;
#endif
#ifdef VULKAN_SDL
case QR_VULKAN:
break;
#endif
default:
break;
}
SDL_DestroyWindow(sdlwindow);
sdlwindow = NULL;
#else
@ -294,22 +367,31 @@ void GLVID_DeInit (void)
void GLVID_SwapBuffers (void)
{
#if SDL_MAJOR_VERSION >= 2
if (vid_vsync.modified)
switch(qrenderer)
{
if (*vid_vsync.string)
#ifdef OPENGL_SDL
case QR_OPENGL:
#if SDL_MAJOR_VERSION >= 2
if (vid_vsync.modified)
{
//if swap_tear isn't supported, try without.
if (SDL_GL_SetSwapInterval(vid_vsync.ival) == -1 && vid_vsync.ival < 0)
SDL_GL_SetSwapInterval(-vid_vsync.ival);
if (*vid_vsync.string)
{
//if swap_tear isn't supported, try without.
if (SDL_GL_SetSwapInterval(vid_vsync.ival) == -1 && vid_vsync.ival < 0)
SDL_GL_SetSwapInterval(-vid_vsync.ival);
}
vid_vsync.modified = false;
}
vid_vsync.modified = false;
}
SDL_GL_SwapWindow(sdlwindow);
SDL_GL_SwapWindow(sdlwindow);
#else
SDL_GL_SwapBuffers();
SDL_GL_SwapBuffers();
#endif
break;
#endif
default:
break;
}
if (!vid_isfullscreen)
@ -381,6 +463,7 @@ qboolean GLVID_ApplyGammaRamps (unsigned int gammarampsize, unsigned short *ramp
return true;
}
#endif
return false;
}
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 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
extern void (APIENTRY *qglGenQueriesARB)(GLsizei n, 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

View File

@ -19,63 +19,64 @@ static void PPAPI_audio_callback(void *sample_buffer, uint32_t len,
soundcardinfo_t *sc = user_data;
unsigned int framesz;
if (sc)
{
int curtime = S_GetMixerTime(sc);
framesz = sc->sn.numchannels * sc->sn.samplebits/8;
//might as well dump it directly...
sc->sn.buffer = sample_buffer;
sc->sn.samples = len / (sc->sn.samplebits/8);
S_PaintChannels (sc, curtime + (len / framesz));
sc->sn.samples = 0;
sc->sn.buffer = NULL;
{
int curtime = S_GetMixerTime(sc);
framesz = sc->sn.numchannels * sc->sn.samplebytes;
//might as well dump it directly...
sc->sn.buffer = sample_buffer;
sc->sn.samples = len / sc->sn.samplebytes;
S_PaintChannels (sc, curtime + (len / framesz));
sc->sn.samples = 0;
sc->sn.buffer = NULL;
sc->snd_sent += len;
}
}
static void PPAPI_Shutdown(soundcardinfo_t *sc)
{
audio_interface->StopPlayback((PP_Resource)sc->handle);
ppb_core->ReleaseResource((PP_Resource)sc->handle);
}
static unsigned int PPAPI_GetDMAPos(soundcardinfo_t *sc)
{
sc->sn.samplepos = sc->snd_sent / (sc->sn.samplebits/8);
return sc->sn.samplepos;
}
static void PPAPI_UnlockBuffer(soundcardinfo_t *sc, void *buffer)
{
}
static void *PPAPI_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx)
{
*sampidx = 0;
return sc->sn.buffer;
}
static void PPAPI_Submit(soundcardinfo_t *sc, int start, int end)
{
static void PPAPI_Shutdown(soundcardinfo_t *sc)
{
audio_interface->StopPlayback((PP_Resource)sc->handle);
ppb_core->ReleaseResource((PP_Resource)sc->handle);
}
int PPAPI_InitCard (soundcardinfo_t *sc, int cardnum)
static unsigned int PPAPI_GetDMAPos(soundcardinfo_t *sc)
{
sc->sn.samplepos = sc->snd_sent / sc->sn.samplebytes;
return sc->sn.samplepos;
}
static void PPAPI_UnlockBuffer(soundcardinfo_t *sc, void *buffer)
{
}
static void *PPAPI_LockBuffer(soundcardinfo_t *sc, unsigned int *sampidx)
{
*sampidx = 0;
return sc->sn.buffer;
}
static void PPAPI_Submit(soundcardinfo_t *sc, int start, int end)
{
}
static qboolean PPAPI_InitCard (soundcardinfo_t *sc, const char *cardname)
{
PP_Resource config;
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*/
if (cardnum != 0)
return 2;
if (cardname && *cardname)
return false; //only use the default device
/*the docs say only two sample rates are allowed*/
if (sc->sn.speed <= 44100)
sc->sn.speed = 44100;
else
sc->sn.speed = 48000;
/*we can't choose these two settings*/
sc->sn.samplebits = 16;
/*we can't choose these settings at all*/
sc->sn.samplebytes = 2;
sc->sn.sampleformat = QSF_S16;
sc->sn.numchannels = 2;
#ifdef PPB_AUDIO_CONFIG_INTERFACE_1_1
@ -107,10 +108,15 @@ int PPAPI_InitCard (soundcardinfo_t *sc, int cardnum)
if (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);
PR_SaveCallStack (progfuncs, stack, &ofs, sizeof(stack));
PR_RunError (&progfuncs->funcs, stack);
free(stack);
return -1;
}
#endif

View File

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

View File

@ -14244,13 +14244,13 @@ void QCC_PR_ParseDefs (char *classname, pbool fatal)
/*aliasof =*/ QCC_PR_ParseName();
else if (pr_token_type == tt_immediate)
{
/*aliasof =*/ pr_immediate_string;
//aliasof = copy(pr_immediate_string);
QCC_PR_Lex();
}
QCC_PR_Expect(")");
}
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
{
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;
preprocessorhack = true;
}
else// if (preprocessorhack)
else if (preprocessorhack)
{
*d++ = '\n';
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);
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_size_t (PNGAPI *qpng_get_rowbytes) PNGARG((png_const_structp png_ptr, png_const_inforp info_ptr)) PSTATIC(png_get_rowbytes);
#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);
#endif
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_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_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);
#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);
#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);

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)
{
char name[MAX_OSPATH];
const char *text;
const char *otext, *text;
vfsfile_t *file;
char unitext[8192], *out = unitext;
int err;
snprintf(name, MAX_OSPATH, "%s.log", PR_GetStringOfs(prinst, OFS_PARM0));
text = PF_VarString(prinst, 2, pr_globals);
PR_CleanText(text);
otext = text = PF_VarString(prinst, 2, pr_globals);
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);
if (file == NULL)
@ -6908,12 +6917,12 @@ static void QCBUILTIN PF_logtext(pubprogfuncs_t *prinst, struct globalvars_s *pr
}
else
{
VFS_WRITE(file, text, strlen(text));
VFS_WRITE(file, unitext, out-unitext);
VFS_CLOSE (file);
}
if (G_FLOAT(OFS_PARM1))
Con_Printf("%s", text);
Con_Printf("%s", otext);
}
#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
{"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
{"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_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_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
@ -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
{"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_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.")},
{"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.")},
@ -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."},
{"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)."},
{"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;
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
extern cvar_t sv_listen_q3;
#endif
@ -1874,8 +1877,8 @@ static void SV_Status_f (void)
if (sv_listen_q3.ival) Con_Printf(" Q3");
#endif
#ifdef HAVE_DTLS
if (sv_listen_dtls.ival >= 2)
Con_Printf(" +DTLS");
if (sv_listen_dtls.ival >= 3)
Con_Printf(" DTLS-only");
else if (sv_listen_dtls.ival)
Con_Printf(" DTLS");
#endif

View File

@ -1933,7 +1933,6 @@ void SVDP_EmitEntitiesUpdate (client_t *client, client_frame_t *frame, packet_en
{
unsigned int bits;
int outno, outmax = frame->maxresend;
qboolean overflow = false;
struct resendinfo_s *resend = frame->resend;
MSG_WriteByte(msg, svcdp_entities);
@ -1950,10 +1949,7 @@ void SVDP_EmitEntitiesUpdate (client_t *client, client_frame_t *frame, packet_en
if (!bits)
continue;
if (msg->cursize + 50 > msg->maxsize)
{
overflow = true;
break; /*give up if it gets full. FIXME: bone data is HUGE.*/
}
if (outno >= outmax)
{ //expand the frames. may need some copying...
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;
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_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.");
@ -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");
#endif
#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
cvar_t sv_reportheartbeats = CVAR("sv_reportheartbeats", "1");
cvar_t sv_highchars = CVAR("sv_highchars", "1");
@ -1633,6 +1634,24 @@ qboolean SVC_GetChallenge (qboolean respond_dp)
over+=sizeof(lng);
}
#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)
@ -1661,6 +1680,7 @@ qboolean SVC_GetChallenge (qboolean respond_dp)
return true;
}
#ifdef SVRANKING
static void VARGS SV_OutOfBandPrintf (int q2, netadr_t *adr, char *format, ...)
{
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);
}
#endif
qboolean SV_ChallengePasses(int challenge)
{
@ -1721,6 +1742,7 @@ qboolean SV_ChallengePasses(int challenge)
return false;
}
#ifdef NQPROT
//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.
//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;
}
#endif
void VARGS SV_RejectMessage(int protocol, char *format, ...)
{
@ -5154,11 +5177,11 @@ void SV_InitLocal (void)
Cvar_Register(&sv_autosave, cvargroup_servercontrol);
#endif
#endif
Cmd_AddCommand ("savegame_legacy", SV_LegacySavegame_f);
Cmd_AddCommandAD ("savegame", SV_Savegame_f, SV_Savegame_c, NULL);
Cmd_AddCommandAD ("loadgame", SV_Loadgame_f, SV_Savegame_c, NULL);
Cmd_AddCommandAD ("save", SV_Savegame_f, SV_Savegame_c, NULL);
Cmd_AddCommandAD ("load", SV_Loadgame_f, SV_Savegame_c, NULL);
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, "Saves the game to the named location.");
Cmd_AddCommandAD ("loadgame", SV_Loadgame_f, SV_Savegame_c, "Loads an existing saved game.");
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, "Loads an existing saved game.");
SV_MVDInit();

View File

@ -2026,6 +2026,7 @@ void SV_Begin_Core(client_t *split)
}
}
#ifndef NOLEGACY
split->dp_ping = NULL;
split->dp_pl = NULL;
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_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
uniform sampler2DShadow s_shadowmap;
//FIXME: shadowmaps need to be atlased!
uniform vec4 l_shadowmapproj; //light projection matrix info
uniform vec2 l_shadowmapscale; //xy are the texture scale, z is 1, w is the scale.
vec3 ShadowmapCoord(vec4 cubeproj)
@ -179,6 +180,9 @@ void main ()
float nDotL = dot(norm, lightDir);
float lightDiffuse = max(0.0, nDotL) * atten;
/*calc specular term*/
//fixme
//fixme: apply fog
//fixme: output a specular term
//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.
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.
program_t *prog = vprog;
struct pipeline_s *pipe;
while(prog->pipelines)
{
@ -5030,7 +5031,7 @@ static qboolean BE_GenerateRefraction(batch_t *batch, shader_t *bs)
extern cvar_t r_refractreflect_scale;
float oldil;
int oldbem;
struct vk_rendertarg *targ;
// struct vk_rendertarg *targ;
//these flags require rendering some view as an fbo
if (r_refdef.recurse)
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.
oldbem = shaderstate.mode;
oldil = shaderstate.identitylighting;
targ = vk.rendertarg;
// targ = vk.rendertarg;
if (bs->flags & SHADER_HASREFLECT)
{
@ -5812,7 +5813,7 @@ void VKBE_DestroyShadowBuffer(struct vk_shadowbuffer *buf)
{
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);
}
}

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;
void R2D_Console_Resize(void);
#ifndef MULTITHREAD
#define Sys_LockConditional(c)
#define Sys_UnlockConditional(c)
#endif
const char *vklayerlist[] =
{
#if 1
@ -142,11 +147,16 @@ void VK_DestroyVkTexture(vk_image_t *img)
if (img->memory)
vkFreeMemory(vk.device, img->memory, vkallocationcb);
}
static void VK_DestroyVkTexture_Delayed(void *w)
{
VK_DestroyVkTexture(w);
}
static void VK_DestroySwapChain(void)
{
uint32_t i;
#ifdef MULTITHREAD
if (vk.submitcondition)
{
Sys_LockConditional(vk.submitcondition);
@ -159,6 +169,7 @@ static void VK_DestroySwapChain(void)
Sys_WaitOnThread(vk.submitthread);
vk.submitthread = NULL;
}
#endif
while (vk.work)
{
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));
}
static void VK_DestroySampler(struct vk_frameend *w)
static void VK_DestroySampler(void *w)
{
VkSampler s = *(VkSampler*)w;
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->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
// VK_FencedCheck();
// VK_DestroyVkTexture(tex->vkimage);
@ -1322,7 +1333,6 @@ qboolean VK_LoadTextureMips (texid_t tex, const struct pendingtextureinfo *mips)
bci.size = 0;
for (i = 0; i < mipcount; i++)
{
VkImageSubresource subres = {0};
VkBufferImageCopy region;
//figure out the number of 'blocks' in the image.
//for non-compressed formats this is just the width directly.
@ -2318,7 +2328,9 @@ void VKVID_QueueGetRGBData (void (*gotrgbdata) (void *rgbdata, intptr_t bytest
VkAssert(vkCreateBuffer(vk.device, &bci, vkallocationcb, &capt->buffer));
vkGetBufferMemoryRequirements(vk.device, capt->buffer, &mem_reqs);
memAllocInfo.allocationSize = mem_reqs.size;
memAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
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);
VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &capt->memory));
VkAssert(vkBindBufferMemory(vk.device, capt->buffer, capt->memory, 0));
@ -2849,6 +2861,7 @@ qboolean VK_SCR_UpdateScreen (void)
if (vk.neednewswapchain && !vk.frame)
{
#ifdef MULTITHREAD
//kill the thread
if (vk.submitthread)
{
@ -2858,6 +2871,7 @@ qboolean VK_SCR_UpdateScreen (void)
Sys_WaitOnThread(vk.submitthread);
vk.submitthread = NULL;
}
#endif
//make sure any work is actually done BEFORE the swapchain gets destroyed
while (vk.work)
{
@ -2871,10 +2885,12 @@ qboolean VK_SCR_UpdateScreen (void)
VK_CreateSwapChain();
vk.neednewswapchain = false;
#ifdef MULTITHREAD
if (vk.allowsubmissionthread && (vk_submissionthread.ival || !*vk_submissionthread.string))
{
vk.submitthread = Sys_CreateThread("vksubmission", VK_Submit_Thread, NULL, THREADP_HIGHEST, 0);
}
#endif
}
if (!VK_SCR_GrabBackBuffer())
@ -3225,6 +3241,7 @@ static void VK_Submit_DoWork(void)
}
}
#ifdef MULTITHREAD
//oh look. a thread.
//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.
@ -3241,6 +3258,7 @@ int VK_Submit_Thread(void *arg)
Sys_UnlockConditional(vk.submitcondition);
return true;
}
#endif
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;
#ifdef MULTITHREAD
if (vk.submitthread && !vk.neednewswapchain)
Sys_ConditionSignal(vk.submitcondition);
else
#endif
VK_Submit_DoWork();
Sys_UnlockConditional(vk.submitcondition);
}
@ -3335,7 +3355,7 @@ void VK_CheckTextureFormats(void)
}
//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;
VkResult err;
@ -3366,7 +3386,9 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat
for (e = 0; e < countof(knowndevexts); e++)
*knowndevexts[e].flag = false;
#ifdef MULTITHREAD
vk.allowsubmissionthread = true;
#endif
vk.neednewswapchain = true;
vk.triplebuffer = info->triplebuffer;
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...
{
qboolean surfext = false;
uint32_t count, i;
uint32_t count, i, j;
VkExtensionProperties *ext;
vkEnumerateInstanceExtensionProperties(NULL, &count, NULL);
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;
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;
else if (sysextname && !strcmp(ext[i].extensionName, sysextname))
{
extensions[extensions_count++] = sysextname;
vk.khr_swapchain = true;
}
else if (sysextname && !strcmp(ext[i].extensionName, VK_KHR_SURFACE_EXTENSION_NAME))
else if (sysextnames && !strcmp(ext[i].extensionName, VK_KHR_SURFACE_EXTENSION_NAME))
{
extensions[extensions_count++] = VK_KHR_SURFACE_EXTENSION_NAME;
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);
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;
}
}
@ -3873,7 +3901,9 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat
else //16bit depth is guarenteed in vulkan
vk.depthformat = VK_FORMAT_D16_UNORM;
#ifdef MULTITHREAD
vk.submitcondition = Sys_CreateConditional();
#endif
{
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;
#ifdef MULTITHREAD
if (vk.allowsubmissionthread && (vk_submissionthread.ival || !*vk_submissionthread.string))
{
vk.submitthread = Sys_CreateThread("vksubmission", VK_Submit_Thread, NULL, THREADP_HIGHEST, 0);
}
#endif
}
return true;
}
@ -3935,8 +3967,10 @@ void VK_Shutdown(void)
vkDestroySurfaceKHR(vk.instance, vk.surface, vkallocationcb);
if (vk.instance)
vkDestroyInstance(vk.instance, vkallocationcb);
#ifdef MULTITHREAD
if (vk.submitcondition)
Sys_DestroyConditional(vk.submitcondition);
#endif
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);
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_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;
GL_Init(info, GLVID_getsdlglfunction);
if (!GL_Init(info, GLVID_getsdlglfunction))
return false;
qglViewport (0, 0, vid.pixelwidth, vid.pixelheight);

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@
#include "libavutil/imgutils.h"
#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.
/*
@ -224,16 +225,29 @@ having them tied to the libavformat network IO.
if(avformat_find_stream_info(ctx->pFormatCtx, NULL)>=0)
{
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 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;
break;
}
}
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;
pCodec=avcodec_find_decoder(ctx->pACodecCtx->codec_id);
#endif
ctx->pAFrame=av_frame_alloc();
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;
for(i=0; i<ctx->pFormatCtx->nb_streams; i++)
for(i=0; i<ctx->pFormatCtx->nb_streams && ctx->videoStream==-1; i++)
{
#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;
break;
}
}
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;
pCodec=avcodec_find_decoder(ctx->pVCodecCtx->codec_id);
#endif
ctx->num = ctx->pFormatCtx->streams[ctx->videoStream]->time_base.num;
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
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;
AVPacket packet;
#if HAVE_DECOUPLED_API
#else
int frameFinished;
#endif
qboolean repainted = false;
int64_t curtime;
@ -304,6 +331,101 @@ static qboolean VARGS AVDec_DisplayFrame(void *vctx, qboolean nosound, qboolean
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?
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
av_packet_unref(&packet);
#endif
}
if (forcevideo || repainted)
@ -451,16 +574,11 @@ static void AVDec_ChangeStream(void *vctx, char *newstream)
static void AVDec_Rewind(void *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);
}
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;
}

View File

@ -35,6 +35,14 @@
#define cef_v8context_get_current_context pcef_v8context_get_current_context
#define cef_post_task pcef_post_task
#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_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 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_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
#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 *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);
}
}*/
static cef_string_t makecefstring(char *str)
{
cef_string_t r = {NULL};
cef_string_from_utf8(str, strlen(str), &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)
{
@ -247,6 +271,7 @@ typedef struct
cef_display_handler_t display_handler;
cef_request_handler_t request_handler;
cef_life_span_handler_t life_span_handler;
cef_context_menu_handler_t context_menu_handler;
cef_browser_t *thebrowser;
void *videodata;
@ -256,7 +281,9 @@ typedef struct
int desiredwidth;
int desiredheight;
char *consolename; //for internal plugin use.
qboolean fullscreen;
cef_string_utf8_t currenturl;
cef_string_utf8_t currenticon;
cef_string_utf8_t currenttitle;
cef_string_utf8_t currentstatus;
@ -278,6 +305,7 @@ static int browser_release(browser_t *br)
if (br->videodata)
free(br->videodata);
cef_string_utf8_clear(&br->currenturl);
cef_string_utf8_clear(&br->currenticon);
cef_string_utf8_clear(&br->currenttitle);
cef_string_utf8_clear(&br->currentstatus);
@ -302,6 +330,7 @@ browser_subs(render_handler);
browser_subs(display_handler);
browser_subs(request_handler);
browser_subs(life_span_handler);
browser_subs(context_menu_handler);
#undef browser_subs
//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);
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)
{
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)
{
if (!strncmp(req, "getcvar_", 8))
if (!req)
return false;
else if (!strncmp(req, "getcvar_", 8))
{
Cvar_Update(&cef_allowcvars);
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_list_value_t *args = message->get_argument_list(message);
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);
id2 = args->get_int(args, 3);
@ -478,13 +515,14 @@ static int CEF_CALLBACK browser_on_process_message_received(cef_client_t* self,
{
str = makecefstring(buffer);
args->set_string(args, 1, &str);
cef_string_clear(&str);
}
else
args->set_null(args, 1);
cef_release(args);
cef_string_utf8_clear(&queryname);
cef_string_clear(&str);
cef_string_userfree_free(cmdunusable);
if (cmdunusable)
cef_string_userfree_free(cmdunusable);
browser->send_process_message(browser, source_process, reply);
handled = true;
}
@ -519,8 +557,22 @@ static void CEF_CALLBACK browser_on_paint(cef_render_handler_t *self, cef_browse
if (type != PET_VIEW)
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)
free(br->videodata);
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);
}
else
{
{ //try to save cpu time by copying only the dirty parts
while (dirtyRectsCount --> 0)
{
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);
}
//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
//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)
@ -599,12 +668,48 @@ static void CEF_CALLBACK browser_on_title_change(cef_display_handler_t* self, ce
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)
{
browser_t *br = (browser_t*)((char*)self - offsetof(browser_t, display_handler));
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
// Con_Printf("new url: %s\n", url.ToString().c_str());
cef_release(browser);
@ -680,24 +785,74 @@ browser_subs(render_handler);
browser_subs(display_handler);
browser_subs(request_handler);
browser_subs(life_span_handler);
browser_subs(context_menu_handler);
#undef browser_subs
nb->client.get_life_span_handler = browser_get_life_span_handler;
nb->client.get_render_handler = browser_get_render_handler;
nb->client.get_context_menu_handler = browser_get_context_menu_handler;
nb->client.get_dialog_handler = NULL;
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.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_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->display_handler.on_console_message = browser_on_console_message;
nb->display_handler.on_title_change = browser_on_title_change;
nb->render_handler.on_cursor_change = NULL;
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_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_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_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->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->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->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)
{
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.
, 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
);
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);
// *exception = makecefstring("SOME KIND OF EXCEPTION!");
*retval = makev8string("OH LOOK! A STRING!");
*retval = makev8string("");
if (argumentsCount)
{
@ -955,7 +1115,8 @@ static int CEF_CALLBACK fsfunc_execute(cef_v8handler_t* self, const cef_string_t
browser->send_process_message(browser, PID_BROWSER, msg);
cef_string_userfree_free(setting);
if (setting)
cef_string_userfree_free(setting);
}
else
{
@ -1011,7 +1172,7 @@ typedef struct
size_t offset;
size_t datasize;
unsigned int resultcode;
cef_string_t mimetype;
char *responseheaders;
} fteresource_t;
static void CEF_CALLBACK resource_handler_addref(cef_base_t* self)
{
@ -1027,45 +1188,93 @@ static int CEF_CALLBACK resource_handler_release(cef_base_t* self)
VFS_CLOSE(rh->fh);
if (rh->data)
free(rh->data);
cef_string_clear(&rh->mimetype);
free(rh->responseheaders);
free(rh);
return true;
}
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)
{
fteresource_t *rh = (fteresource_t*)((char*)self - offsetof(fteresource_t, rh));
cef_string_userfree_t url = request->get_url(request);
cef_string_utf8_t u8_url = {NULL};
cef_string_userfree_t url = request->get_url(request), method;
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_to_utf8(url->str, url->length, &u8_url);
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
q = strchr(u8_url.str, '?');
if (q)
*q = 0;
for(e = q?q:u8_url.str+strlen(u8_url.str); e > u8_url.str; )
{
char *q = strchr(u8_url.str, '?');
char *e;
if (q)
*q = 0;
for(e = q?q:u8_url.str+strlen(u8_url.str); e > u8_url.str; )
e--;
if (*e == '/')
break; //no extension
if (*e == '.')
{
e--;
if (*e == '/')
break; //no extension
if (*e == '.')
{
e++;
cef_string_from_utf8(e, strlen(e), &ext);
break;
}
e++;
cef_string_from_utf8(e, strlen(e), &ext);
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.
//(also blocks any fancy http:// parsing that an engine might do)
if (!strncmp(u8_url.str, "fte://data/", 11))
@ -1074,14 +1283,17 @@ static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t*
if (rh->fh)
{
cef_string_userfree_t mt = cef_get_mime_type(&ext);
cef_string_copy(mt->str, mt->length, &rh->mimetype);
cef_string_userfree_free(mt);
if (mt)
{
res_catfield_csuf(&rh->responseheaders, "Content-Type", mt);
cef_string_userfree_free(mt);
}
rh->resultcode = 200;
}
else
{
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->datasize = strlen(rh->data);
}
@ -1090,16 +1302,21 @@ static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t*
{
struct pubprogfuncs_s *progs;
const char *page;
const char *respheaders = NULL;
const char *reqheaders = NULL;
if (ext.str)
{
cef_string_userfree_t mt = cef_get_mime_type(&ext);
if (mt)
{
cef_string_copy(mt->str, mt->length, &rh->mimetype);
res_catfield_csuf((char**)&respheaders, "Content-Type", mt);
cef_string_userfree_free(mt);
}
}
if(q)
*q = '?'; //put it back so the qc can get the full url.
rh->resultcode = 404;
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);
((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);
if (((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;
}
else
@ -1138,6 +1400,9 @@ static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t*
else
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
rh->data = strdup(page);
rh->datasize = strlen(rh->data);
@ -1145,7 +1410,7 @@ static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t*
else
{
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->datasize = strlen(rh->data);
}
@ -1158,9 +1423,24 @@ static int CEF_CALLBACK resource_handler_process_request(cef_resource_handler_t*
cef_release(request);
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)
{
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)
*response_length = VFS_GETLEN(rh->fh);
@ -1169,9 +1449,38 @@ static void CEF_CALLBACK resource_handler_get_response_headers(cef_resource_hand
else
*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);
cef_string_clear(&key);
cef_string_clear(&val);
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)
@ -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.get_response_headers = resource_handler_get_response_headers;
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;
cef_addref(&rh->rh);
cef_release(browser);
@ -1294,7 +1606,10 @@ cef_request_context_t *Cef_GetRequestContext(void)
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_browser_settings_t browserSettings = {sizeof(browserSettings)};
@ -1323,6 +1638,9 @@ static void *Cef_Create(const char *name)
cef_main_args_t mainargs = {0};
cef_settings_t settings = {sizeof(settings)};
if (!Cef_Init(true))
return NULL;
//const char *ua = "FTEBrowser";
//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)))
cef_string_from_utf8(utf8, strlen(utf8), &settings.log_file);
// CefString(&settings.resources_dir_path).FromASCII("");
// CefString(&settings.locales_dir_path).FromASCII("");
if (pFS_NativePath("", FS_BINARYPATH, utf8, sizeof(utf8)))
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
{
@ -1348,7 +1669,7 @@ static void *Cef_Create(const char *name)
#else
settings.log_severity = LOGSEVERITY_DISABLE;
#endif
settings.background_color = 0xffffffff;
settings.background_color = 0x00ffffff;
// settings.single_process = true;
#ifdef _WIN32
// 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.remote_fonts = STATE_DISABLED;
browserSettings.plugins = STATE_DISABLED;
browserSettings.background_color = 0xffffffff;
browserSettings.background_color = 0x00ffffff;
window_info.windowless_rendering_enabled = true;
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;
}
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)
{
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;
else if (!strcmp(field, "status"))
ret = browser->currentstatus.str;
else if (!strcmp(field, "icon"))
ret = browser->currenticon.str;
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_post_task, "cef_post_task"},
{(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}
};
if (!Sys_LoadLibrary("libcef", ceffuncs))
@ -1965,7 +2302,8 @@ qintptr_t Plug_Init(qintptr_t *args)
decoderfuncs.structsize = sizeof(media_decoder_funcs_t);
decoderfuncs.drivername = "cef";
decoderfuncs.createdecoder = Cef_Create;
decoderfuncs.createdecoder = Cef_CreateOld;
// decoderfuncs.createdecoderCB = Cef_Create;
decoderfuncs.decodeframe = Cef_DisplayFrame;
decoderfuncs.shutdown = Cef_Destroy;
decoderfuncs.cursormove = Cef_CursorMove;
@ -1981,9 +2319,6 @@ qintptr_t Plug_Init(qintptr_t *args)
return false;
}
if (!Cef_Init(true))
return false;
if (Plug_Export("ExecuteCommand", Cef_ExecuteCommand))
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"))
{ //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"))
@ -392,7 +392,7 @@ static qboolean JCL_JingleSend(jclient_t *jcl, struct c2c_s *c2c, char *action)
if (!strcmp(action, "session-accept"))
{
if (c2c->content[c].method == transportmode)
XML_AddParameter(jingle, "responder", jcl->jid);
XML_AddParameter(jingle, "responder", jcl->fulljid);
else
action = "transport-replace";
}

View File

@ -62,7 +62,7 @@ void XMPP_FT_Frame(jclient_t *jcl)
{
//server has accepted us, woo.
//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));
//in hex
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;
struct buf_ctx buf = {NULL, 0, 0};
if (!t)
return;
XML_DumpToBuf(&buf, t, indent);
buf_cat(&buf, "", 1);

View File

@ -129,18 +129,13 @@ typedef struct jclient_s
int serverport;
char domain[256];
char username[256];
char password[256];
char resource[256];
char certificatedomain[256];
int forcetls; //-1=off, 0=ifpossible, 1=fail if can't upgrade, 2=old-style tls
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 vcardphotohash[20]; //20-byte sha1 hash.
enum
@ -151,9 +146,6 @@ typedef struct jclient_s
} vcardphotohashstatus;
qboolean vcardphotohashchanged; //forces a presence send.
char authnonce[256];
int authmode;
int instreampos;
qboolean connecting; //still waiting for intial stream tag
@ -166,19 +158,56 @@ typedef struct jclient_s
char curquakeserver[2048];
char defaultnamespace[2048]; //should be 'jabber:client' or blank (and spammy with all the extra xmlns attribs)
struct
struct sasl_ctx_s
{
char saslmethod[64];
char obtainurl[256];
char refreshurl[256];
char clientid[256];
char clientsecret[256];
char *useraccount;
char *scope;
char *accesstoken; //one-shot access token
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)
} oauth2;
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
{
char saslmethod[64];
char obtainurl[256];
char refreshurl[256];
char clientid[256];
char clientsecret[256];
char *useraccount;
char *scope;
char *accesstoken; //one-shot access token
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)
} oauth2;
} sasl;
struct iq_s
{

View File

@ -315,6 +315,9 @@ BUILTIN(void, Net_Close, (qhandle_t socket));
#define ARGNAMES ,sock,certhostname
BUILTINR(int, Net_SetTLSClient, (qhandle_t sock, const char *certhostname));
#undef ARGNAMES
#define ARGNAMES ,sock,outdata,datalen
BUILTINR(int, Net_GetTLSBinding, (qhandle_t sock, char *outdata, int *datalen));
#undef ARGNAMES
#define ARGNAMES ,inputbuffer,buffersize
BUILTINR(int, ReadInputBuffer, (void *inputbuffer, int buffersize));
@ -468,6 +471,7 @@ void Plug_InitStandardBuiltins(void)
CHECKBUILTIN(Net_Send);
CHECKBUILTIN(Net_Close);
CHECKBUILTIN(Net_SetTLSClient);
CHECKBUILTIN(Net_GetTLSBinding);
//random things
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(void, Net_Close, (qhandle_t socket));
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 NET_CLIENTPORT -1
#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>
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]
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 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.
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]
obsolete, see rotation.
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.
rotationspeed <min> [max]
obsolete, see rotation.
beamtexstep <value>
only valid if the effect is a beam.
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
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>
controls how the particle scales over time
specifies the change in the particle scale per second.
@ -84,6 +108,9 @@ count <min> <max>
alpha <value>
specifies the initial alpha value of the effect
alpharand <value>
specifies a randomized additonal value added to each particle's initial alpha.
alphadelta <value>
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
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.
veladd <value>
@ -105,6 +132,20 @@ veladd <value>
orgadd <value>
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>>
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.
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.
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.
@ -163,19 +204,24 @@ green <value>
blue <value>
rgb <red> <green> <blue>
rgb <value>
rgbf <red> <green> <blue>
Specifies the initial red, green, and/or blue values for each particle.
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>
greenrand <value>
bluerand <value>
rgbrand <red> <green> <blue>
rgbrand <value>
rgbrandf <red> <green> <blue>
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.
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.
The rgbrandf form takes values scaled by 1 instead of 255.
redrandsync <value>
greenrandsync <value>
@ -192,6 +238,7 @@ greendelta <value>
bluedelta <value>
rgbdelta <red> <green> <blue>
rgbdelta <value>
rgbdeltaf <red> <green> <blue>
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.
@ -201,7 +248,8 @@ rgbdeltatime <value>
rampmode <mode>
mode may be one of:
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.
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.
blend <mode>
mode may be one of: add, subtract, blendcolour/blendcolor, blend
if the texture used is actually a shader, this is ignored.
If the texture used is actually a shader then that shader's blend mode will take precidence.
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]
This affects how particles are positioned when they first spawn, and their initial velocities.
@ -254,6 +314,7 @@ spawnparam2 <value>
obsolete. see spawnmode.
up <value>
obsoleted by orgbias.
the particle's starting origin is moved upwards by this amount (worldspace).
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.
cdecal/decal: particles are spawned only upon bsp geometry. They are clipped by it.
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
obsolete.
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>
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.
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.
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.
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>
value 0 means no cubemap.
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.
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.
spawnstain <radius> <r> <g> <b>
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>
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.