playdemo accepts https urls now. will start playing before the file has finished downloading, to avoid unnecessary delays.

reworked network addresses to separate address family and connection type. this should make banning people more reliable, as well as simplifying a whole load of logic (no need to check for ipv4 AND ipv6).
tcpconnect will keep trying to connect even if the connection wasn't instant, instead of giving up instantly.
rewrote tcp connections quite a bit. sv_port_tcp now handles qtv+qizmo+http+ws+rtcbroker+tls equivalents.
qtv_streamport is now a legacy cvar and now acts equivalently to sv_port_tcp (but still separate).
rewrote screenshot and video capture code to use strides. this solves image-is-upside down issues with vulkan.
ignore alt key in browser port. oh no! no more red text! oh no! no more alt-being-wrongly-down-and-being-unable-to-type-anything-without-forcing-alt-released!
reworked audio decoder interface. now has clearly defined success/unavailable/end-of-file results. this should solve a whole load of issues with audio streaming.
fixed various openal audio streaming issues too. openal also got some workarounds for emscripten's poor emulation.
fixed ogg decoder to retain sync properly if seeked.
updated menu_media a bit. now reads vorbis comments/id3v1 tags to get proper track names. also saves the playlist so you don't have to manually repopulate the list so it might actually be usable now (after how many years?)
r_stains now defaults to 0, and is no longer enabled by presets. use decals if you want that sort of thing.
added fs_noreexec cvar, so configs will not be reexeced on gamedir change. this also means defaults won't be reapplied, etc.
added 'nvvk' renderer on windows, using nvidia's vulkan-inside-opengl gl extension. mostly just to see how much slower it is.
fixed up the ftp server quite a lot. more complete, more compliant, and should do ipv6 properly to-boot. file transfers also threaded.
fixed potential crash inside runclientphys.
experimental sv_antilag=3 setting. totally untested. the aim is to avoid missing due to lagged knockbacks. may be expensive for the server.
browser port's websockets support fixed. experimental support for webrtc ('works for me', requires a broker server).
updated avplug(renamed to ffmpeg so people know what it is) to use ffmpeg 3.2.4 properly, with its new encoder api. should be much more robust... also added experimental audio decoder for game music etc (currently doesn't resample, so playback rates are screwed, disabled by cvar).

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5097 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2017-05-10 02:08:58 +00:00
parent f6cbd61760
commit 484e8bbfc2
117 changed files with 7612 additions and 3444 deletions

View File

@ -30,6 +30,7 @@ int cls_lastto;
int cls_lasttype;
void CL_PlayDemo(char *demoname, qboolean usesystempath);
void CL_PlayDemoFile(vfsfile_t *f, char *demoname, qboolean issyspath);
char lastdemoname[256];
static qboolean lastdemowassystempath;
@ -72,16 +73,13 @@ void CL_StopPlayback (void)
Media_CaptureDemoEnd();
VFS_CLOSE (cls.demoinfile);
if (cls.demoinfile)
VFS_CLOSE (cls.demoinfile);
cls.demoinfile = NULL;
cls.state = ca_disconnected;
cls.demoplayback = DPB_NONE;
cls.demoseeking = false; //just in case
if (cls.demoindownload)
cls.demoindownload->status = DL_FAILED;
cls.demoindownload = NULL;
if (cls.timedemo)
CL_FinishTimeDemo ();
@ -320,7 +318,7 @@ int readdemobytes(int *readpos, void *data, int len)
if (*readpos+len > demobuffersize)
{
if (i < 0 || (i == 0 && len && !cls.demoinfile->seekingisabadplan))
if (i < 0 || (i == 0 && len && cls.demoinfile->seekstyle < SS_SLOW))
{ //0 means no data available yet, don't error on that (unless we can seek, in which case its not a stream and we won't get any more data later on).
endofdemo = true;
return 0;
@ -435,8 +433,16 @@ void CL_DemoJump_f(void)
cls.demoseektime = newtime;
else
{
vfsfile_t *df = cls.demoinfile;
Con_Printf("Rewinding demo\n");
CL_PlayDemo(lastdemoname, lastdemowassystempath);
if (df->seekstyle != SS_UNSEEKABLE)
{
VFS_SEEK(df, 0);
cls.demoinfile = NULL;
CL_PlayDemoFile(df, lastdemoname, lastdemowassystempath);
}
else
CL_PlayDemo(lastdemoname, lastdemowassystempath);
//now fastparse it.
cls.demoseektime = newtime;
@ -2072,10 +2078,11 @@ void CL_PlayDemo_f (void)
#ifdef WEBCLIENT
#if 1
if (!strncmp(Cmd_Argv(1), "ftp://", 6) || !strncmp(Cmd_Argv(1), "http://", 7))
if (!strncmp(Cmd_Argv(1), "ftp://", 6) || !strncmp(Cmd_Argv(1), "http://", 7) || !strncmp(Cmd_Argv(1), "https://", 7))
{
if (Cmd_ExecLevel == RESTRICT_LOCAL)
HTTP_CL_Get(Cmd_Argv(1), COM_SkipPath(Cmd_Argv(1)), CL_PlayDownloadedDemo);
Host_RunFile(Cmd_Argv(1), strlen(Cmd_Argv(1)), NULL);
// HTTP_CL_Get(Cmd_Argv(1), COM_SkipPath(Cmd_Argv(1)), CL_PlayDownloadedDemo);
return;
}
#endif
@ -2092,16 +2099,8 @@ void CL_PlayDemo_f (void)
CL_PlayDemo(demoname, false);
}
void CL_DemoStreamFullyDownloaded(struct dl_download *dl)
{
//let the file get closed by the demo playback code.
dl->file = NULL;
//kill the reference now that its done.
if (cls.demoindownload == dl)
cls.demoindownload = NULL;
}
//dl is provided so that we can receive files via chunked/gziped http downloads and on systems that don't provide sockets etc. its tracked so we can cancel the download if the client aborts playback early.
void CL_PlayDemoStream(vfsfile_t *file, struct dl_download *dl, char *filename, qboolean issyspath, int demotype, float bufferdelay)
void CL_PlayDemoStream(vfsfile_t *file, char *filename, qboolean issyspath, int demotype, float bufferdelay)
{
int protocol = CP_UNKNOWN;
@ -2137,9 +2136,6 @@ void CL_PlayDemoStream(vfsfile_t *file, struct dl_download *dl, char *filename,
return;
}
if (dl)
dl->notifycomplete = CL_DemoStreamFullyDownloaded;
//
// disconnect from server
//
@ -2151,7 +2147,6 @@ void CL_PlayDemoStream(vfsfile_t *file, struct dl_download *dl, char *filename,
//
// open the demo file
//
cls.demoindownload = dl;
cls.demoinfile = file;
if (!cls.demoinfile)
{
@ -2212,20 +2207,20 @@ void CL_PlayDemoFile(vfsfile_t *f, char *demoname, qboolean issyspath)
if (!Q_strcasecmp(demoname + strlen(demoname) - 3, "dm2") ||
!Q_strcasecmp(demoname + strlen(demoname) - 6, "dm2.gz"))
{
CL_PlayDemoStream(f, NULL, demoname, issyspath, DPB_QUAKE2, 0);
CL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKE2, 0);
return;
}
#endif
if (!Q_strcasecmp(demoname + strlen(demoname) - 3, "mvd") ||
!Q_strcasecmp(demoname + strlen(demoname) - 6, "mvd.gz"))
{
CL_PlayDemoStream(f, NULL, demoname, issyspath, DPB_MVD, 0);
CL_PlayDemoStream(f, demoname, issyspath, DPB_MVD, 0);
return;
}
if (!Q_strcasecmp(demoname + strlen(demoname) - 3, "qwd") ||
!Q_strcasecmp(demoname + strlen(demoname) - 6, "qwd.gz"))
{
CL_PlayDemoStream(f, NULL, demoname, issyspath, DPB_QUAKEWORLD, 0);
CL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKEWORLD, 0);
return;
}
@ -2252,7 +2247,7 @@ void CL_PlayDemoFile(vfsfile_t *f, char *demoname, qboolean issyspath)
ft *= -1;
if (chr == '\n')
{
CL_PlayDemoStream(f, NULL, demoname, issyspath, DPB_NETQUAKE, 0);
CL_PlayDemoStream(f, demoname, issyspath, DPB_NETQUAKE, 0);
return;
}
VFS_SEEK(f, start);
@ -2293,7 +2288,7 @@ void CL_PlayDemoFile(vfsfile_t *f, char *demoname, qboolean issyspath)
if (protocol >= PROTOCOL_VERSION_Q2_DEMO_MIN && protocol <= PROTOCOL_VERSION_Q2_DEMO_MAX)
{
VFS_SEEK(f, start);
CL_PlayDemoStream(f, NULL, demoname, issyspath, DPB_QUAKE2, 0);
CL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKE2, 0);
return;
}
break;
@ -2309,7 +2304,7 @@ void CL_PlayDemoFile(vfsfile_t *f, char *demoname, qboolean issyspath)
//could also be .qwz or .dmz or whatever that nq extension is. we don't support either.
//mvd and qwd have no identifying markers, other than the extension.
CL_PlayDemoStream(f, NULL, demoname, issyspath, DPB_QUAKEWORLD, 0);
CL_PlayDemoStream(f, demoname, issyspath, DPB_QUAKEWORLD, 0);
}
#ifdef WEBCLIENT
void CL_PlayDownloadedDemo(struct dl_download *dl)
@ -2609,7 +2604,7 @@ void CL_QTVPoll (void)
if (streamavailable)
{
CL_PlayDemoStream(qtvrequest, NULL, NULL, false, iseztv?DPB_EZTV:DPB_MVD, BUFFERTIME);
CL_PlayDemoStream(qtvrequest, NULL, false, iseztv?DPB_EZTV:DPB_MVD, BUFFERTIME);
qtvrequest = NULL;
demo_resetcache(qtvrequestsize - (tail-qtvrequestbuffer), tail);
return;
@ -2894,7 +2889,7 @@ void CL_QTVPlay_f (void)
if (raw)
{
VFS_WRITE(newf, msg, msglen);
CL_PlayDemoStream(qtvrequest, NULL, qtvhostname, false, DPB_MVD, BUFFERTIME);
CL_PlayDemoStream(qtvrequest, qtvhostname, false, DPB_MVD, BUFFERTIME);
}
else
{

View File

@ -568,18 +568,19 @@ cvar_t cl_pitchspeed = CVAR("cl_pitchspeed","150");
cvar_t cl_anglespeedkey = CVAR("cl_anglespeedkey","1.5");
#define GATHERBIT(bname,bit) if (bname.state[pnum] & 3) {bits |= bit;} bname.state[pnum] &= ~2;
void CL_GatherButtons (usercmd_t *cmd, int pnum)
{
unsigned int bits = 0;
if (in_attack .state[pnum] & 3) bits |= 1; in_attack.state[pnum] &= ~2;
if (in_jump .state[pnum] & 3) bits |= 2; in_jump.state[pnum] &= ~2;
if (in_use .state[pnum] & 3) bits |= 4; in_use.state[pnum] &= ~2;
if (in_button3.state[pnum] & 3) bits |= 4; in_button3.state[pnum] &= ~2; //yup, flag 4 twice.
if (in_button4.state[pnum] & 3) bits |= 8; in_button4.state[pnum] &= ~2;
if (in_button5.state[pnum] & 3) bits |= 16; in_button5.state[pnum] &= ~2;
if (in_button6.state[pnum] & 3) bits |= 32; in_button6.state[pnum] &= ~2;
if (in_button7.state[pnum] & 3) bits |= 64; in_button7.state[pnum] &= ~2;
if (in_button8.state[pnum] & 3) bits |= 128; in_button8.state[pnum] &= ~2;
GATHERBIT(in_attack, 1);
GATHERBIT(in_jump, 2);
GATHERBIT(in_use, 4);
GATHERBIT(in_button3, 4); //yup, flag 4 twice.
GATHERBIT(in_button4, 8);
GATHERBIT(in_button5, 16);
GATHERBIT(in_button6, 32);
GATHERBIT(in_button7, 64);
GATHERBIT(in_button8, 128);
cmd->buttons = bits;
}

View File

@ -1061,7 +1061,15 @@ void CL_CheckForResend (void)
if (contype & 1)
{
Q_snprintfz (data, sizeof(data), "%c%c%c%cgetchallenge\n", 255, 255, 255, 255);
keeptrying &= NET_SendPacket (NS_CLIENT, strlen(data), data, &connectinfo.adr)==NETERR_SENT;
switch(NET_SendPacket (NS_CLIENT, strlen(data), data, &connectinfo.adr))
{
case NETERR_CLOGGED: //temporary failure
case NETERR_SENT: //yay, works!
break;
default:
keeptrying = false;
break;
}
}
/*NQ*/
#ifdef NQPROT
@ -1093,7 +1101,15 @@ void CL_CheckForResend (void)
MSG_WriteString(&sb, "getchallenge");
*(int*)sb.data = LongSwap(NETFLAG_CTL | sb.cursize);
keeptrying &= NET_SendPacket (NS_CLIENT, sb.cursize, sb.data, &connectinfo.adr)==NETERR_SENT;
switch(NET_SendPacket (NS_CLIENT, sb.cursize, sb.data, &connectinfo.adr))
{
case NETERR_CLOGGED: //temporary failure
case NETERR_SENT: //yay, works!
break;
default:
keeptrying = false;
break;
}
}
#endif
@ -1318,7 +1334,7 @@ void CL_IRCConnect_f (void)
{
CL_Disconnect_f ();
if (FTENET_AddToCollection(cls.sockets, "TCP", Cmd_Argv(2), NA_IRC, false))
if (FTENET_AddToCollection(cls.sockets, "TCP", Cmd_Argv(2), NA_IP, NP_IRC, false))
{
char *server;
server = Cmd_Argv (1);
@ -1747,7 +1763,7 @@ void CL_Disconnect (void)
#ifdef TCPCONNECT
//disconnects it, without disconnecting the others.
FTENET_AddToCollection(cls.sockets, "conn", NULL, NA_INVALID, false);
FTENET_AddToCollection(cls.sockets, "conn", NULL, NA_INVALID, NP_DGRAM, false);
#endif
Cvar_ForceSet(&cl_servername, "none");
@ -3418,17 +3434,11 @@ void CL_ReadPackets (void)
{
case CP_NETQUAKE:
#ifdef NQPROT
switch(NQNetChan_Process(&cls.netchan))
if(NQNetChan_Process(&cls.netchan))
{
case NQP_ERROR:
break;
case NQP_DATAGRAM://datagram
// cls.netchan.incoming_sequence = cls.netchan.outgoing_sequence - 3;
case NQP_RELIABLE://reliable
MSG_ChangePrimitives(cls.netchan.netprim);
CL_WriteDemoMessage (&net_message, msg_readcount);
CLNQ_ParseServerMessage ();
break;
}
#endif
break;
@ -4314,7 +4324,7 @@ void Host_RunFileNotify(struct dl_download *dl)
#define HRF_OPENED (1<<4)
#define HRF_DOWNLOADED (1<<5) //file was actually downloaded, and not from the local system
#define HRF_WAITING (1<<6) //file looks important enough that we should wait for it to start to download or something to see what file type it is.
#define HRF_WAITING (1<<6) //file looks important enough that we should wait for it to start to download or something before we try doing other stuff.
// (1<<7)
#define HRF_DEMO_MVD (1<<8)
@ -4328,12 +4338,14 @@ void Host_RunFileNotify(struct dl_download *dl)
#define HRF_PACKAGE (1<<15) //pak or pk3 that should be installed.
#define HRF_ARCHIVE (1<<16) //zip - treated as a multiple-file 'installer'
#define HRF_MODEL (1<<17)
#define HRF_CONFIG (1<<18) //exec it on the console...
#define HRF_ACTION (HRF_OVERWRITE|HRF_NOOVERWRITE|HRF_ABORT)
#define HRF_DEMO (HRF_DEMO_MVD|HRF_DEMO_QWD|HRF_DEMO_DM2|HRF_DEMO_DEM)
#define HRF_FILETYPES (HRF_DEMO|HRF_QTVINFO|HRF_MANIFEST|HRF_BSP|HRF_PACKAGE|HRF_MODEL|HRF_ARCHIVE)
#define HRF_FILETYPES (HRF_DEMO|HRF_QTVINFO|HRF_MANIFEST|HRF_BSP|HRF_PACKAGE|HRF_ARCHIVE|HRF_MODEL|HRF_CONFIG)
typedef struct {
unsigned int flags;
struct dl_download *dl;
vfsfile_t *srcfile;
vfsfile_t *dstfile;
char fname[1]; //system path or url.
@ -4341,13 +4353,108 @@ typedef struct {
extern int waitingformanifest;
void Host_DoRunFile(hrf_t *f);
void CL_PlayDemoStream(vfsfile_t *file, struct dl_download *, char *filename, qboolean issyspath, int demotype, float bufferdelay);
void CL_PlayDemoStream(vfsfile_t *file, char *filename, qboolean issyspath, int demotype, float bufferdelay);
void CL_ParseQTVDescriptor(vfsfile_t *f, const char *name);
qboolean FS_PathURLCache(char *url, char *path, size_t pathsize);
//guesses the file type based upon its file extension. mdl/md3/iqm distinctions are not important, so we can usually get away with this in the context of quake.
unsigned int Host_GuessFileType(const char *mimetype, const char *filename)
{
if (mimetype)
{
if (!strcmp(mimetype, "application/x-qtv")) //what uses this?
return HRF_QTVINFO;
else if (!strcmp(mimetype, "text/x-quaketvident"))
return HRF_QTVINFO;
else if (!strcmp(mimetype, "application/x-fteplugin"))
return HRF_MANIFEST;
else if (!strcmp(mimetype, "application/x-ftemanifest"))
return HRF_MANIFEST;
else if (!strcmp(mimetype, "application/x-multiviewdemo"))
return HRF_DEMO_MVD;
else if (!strcmp(mimetype, "application/zip"))
return HRF_ARCHIVE;
// else if (!strcmp(mimetype, "application/x-ftebsp"))
// return HRF_BSP;
// else if (!strcmp(mimetype, "application/x-ftepackage"))
// return HRF_PACKAGE;
}
if (filename)
{ //find the query or location part of the url, so we can ignore extra stuff.
struct
{
unsigned int type;
const char *ext;
} exts[] =
{
//demo formats
{HRF_DEMO_QWD, "qwd"},
{HRF_DEMO_QWD, "qwd.gz"},
{HRF_DEMO_MVD, "mvd"},
{HRF_DEMO_MVD, "mvd.gz"},
{HRF_DEMO_DM2, "dm2"},
{HRF_DEMO_DM2, "dm2.gz"},
{HRF_DEMO_DEM, "dem"},
{HRF_DEMO_DEM, "dem.gz"},
{HRF_QTVINFO, "qtv"},
//other stuff
{HRF_MANIFEST, "fmf"},
{HRF_BSP, "bsp"},
{HRF_BSP, "map"},
{HRF_CONFIG, "cfg"},
{HRF_CONFIG, "rc"},
{HRF_PACKAGE, "pak"},
{HRF_PACKAGE, "pk3"},
{HRF_PACKAGE, "pk4"},
{HRF_PACKAGE, "wad"},
{HRF_ARCHIVE, "zip"},
//model formats
{HRF_MODEL, "mdl"},
{HRF_MODEL, "md2"},
{HRF_MODEL, "md3"},
{HRF_MODEL, "iqm"},
{HRF_MODEL, "psk"},
{HRF_MODEL, "zym"},
{HRF_MODEL, "dpm"},
//sprites
{HRF_MODEL, "spr"},
{HRF_MODEL, "spr2"},
//static stuff
{HRF_MODEL, "obj"},
{HRF_MODEL, "lwo"},
{HRF_MODEL, "ase"},
};
size_t i;
const char *ext;
const char *stop = filename+strlen(filename);
const char *tag = strchr(filename, '?');
if (tag && tag < stop)
stop = tag;
tag = strchr(filename, '#');
if (tag && tag < stop)
stop = tag;
ext = COM_GetFileExtension(filename, stop);
if (!Q_strstopcasecmp(ext, stop, ".php")) //deal with extra extensions the easy way
ext = COM_GetFileExtension(filename, stop=ext);
if (!Q_strstopcasecmp(ext, stop, ".gz")) //deal with extra extensions the easy way
ext = COM_GetFileExtension(filename, ext);
if (*ext == '.')
ext++;
for (i = 0; i < countof(exts); i++)
if (!Q_strstopcasecmp(ext, stop, exts[i].ext))
return exts[i].type;
}
return 0;
}
void Host_RunFileDownloaded(struct dl_download *dl)
{
hrf_t *f = dl->user_ctx;
if(!f) //download was previously cancelled.
return;
if (dl->status == DL_FAILED)
{
f->flags |= HRF_ABORT;
@ -4355,6 +4462,9 @@ void Host_RunFileDownloaded(struct dl_download *dl)
}
else
{
if (f->srcfile) //erk?
VFS_CLOSE(f->srcfile);
f->flags |= HRF_OPENED;
f->srcfile = dl->file;
dl->file = NULL;
}
@ -4373,50 +4483,10 @@ qboolean Host_BeginFileDownload(struct dl_download *dl, char *mimetype)
waitingformanifest--;
}
if (mimetype && !(f->flags & HRF_FILETYPES))
{
if (!strcmp(mimetype, "application/x-qtv")) //what uses this?
f->flags |= HRF_QTVINFO;
else if (!strcmp(mimetype, "text/x-quaketvident"))
f->flags |= HRF_QTVINFO;
else if (!strcmp(mimetype, "application/x-fteplugin"))
f->flags |= HRF_MANIFEST;
else if (!strcmp(mimetype, "application/x-ftemanifest"))
f->flags |= HRF_MANIFEST;
else if (!strcmp(mimetype, "application/x-multiviewdemo"))
f->flags |= HRF_DEMO_MVD;
else if (!strcmp(mimetype, "application/zip"))
f->flags |= HRF_ARCHIVE;
// else if (!strcmp(mimetype, "application/x-ftebsp"))
// f->flags |= HRF_BSP;
// else if (!strcmp(mimetype, "application/x-ftepackage"))
// f->flags |= HRF_PACKAGE;
if (f->flags & HRF_MANIFEST)
waitingformanifest++;
}
if (!(f->flags & HRF_FILETYPES))
{
char ext[8];
COM_FileExtension(f->fname, ext, sizeof(ext));
if (!strcmp(ext, "qwd"))
f->flags |= HRF_DEMO_QWD;
else if (!strcmp(ext, "mvd"))
f->flags |= HRF_DEMO_MVD;
else if (!strcmp(ext, "dm2"))
f->flags |= HRF_DEMO_DM2;
else if (!strcmp(ext, "dem"))
f->flags |= HRF_DEMO_DEM;
else if (!strcmp(ext, "qtv"))
f->flags |= HRF_QTVINFO;
else if (!strcmp(ext, "fmf"))
f->flags |= HRF_MANIFEST;
else if (!strcmp(ext, "bsp"))
f->flags |= HRF_BSP;
else if (!strcmp(ext, "pak") || !strcmp(ext, "pk3"))
f->flags |= HRF_PACKAGE;
else
f->flags |= Host_GuessFileType(mimetype, f->fname);
if (!(f->flags & HRF_FILETYPES))
{
if (mimetype)
Con_Printf("mime type \"%s\" and file extension of \"%s\" not recognised\n", mimetype, f->fname);
@ -4428,27 +4498,33 @@ qboolean Host_BeginFileDownload(struct dl_download *dl, char *mimetype)
return false;
}
if (f->flags & HRF_MANIFEST)
if ((f->flags & HRF_MANIFEST) && !(f->flags & HRF_WAITING))
{
f->flags |= HRF_WAITING;
waitingformanifest++;
}
}
//seeking means we can rewind
if (f->flags & HRF_DEMO_QWD)
CL_PlayDemoStream((dl->file = VFSPIPE_Open()), dl, f->fname, true, DPB_QUAKEWORLD, 0);
CL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, true, DPB_QUAKEWORLD, 0);
else if (f->flags & HRF_DEMO_MVD)
CL_PlayDemoStream((dl->file = VFSPIPE_Open()), dl, f->fname, true, DPB_MVD, 0);
CL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, true, DPB_MVD, 0);
#ifdef Q2CLIENT
else if (f->flags & HRF_DEMO_DM2)
CL_PlayDemoStream((dl->file = VFSPIPE_Open()), dl, f->fname, true, DPB_QUAKE2, 0);
CL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, true, DPB_QUAKE2, 0);
#endif
#ifdef NQPROT
//fixme: the demo code can't handle the cd track like this.
// else if (f->flags & HRF_DEMO_DEM)
// CL_PlayDemoStream((dl->file = VFSPIPE_Open()), dl, f->fname, DPB_NETQUAKE, 0);
else if (f->flags & HRF_DEMO_DEM)
{ //fixme: the demo code can't handle the cd track with streamed/missing-so-far writes.
dl->file = VFSPIPE_Open(1, true); //make sure the reader will be seekable, so we can rewind.
// CL_PlayDemoStream((dl->file = VFSPIPE_Open(2, true)), f->fname, DPB_NETQUAKE, 0);
}
#endif
else if (f->flags & (HRF_MANIFEST | HRF_QTVINFO))
{
//just use a pipe instead of a temp file, working around an issue with temp files on android
dl->file = VFSPIPE_Open();
dl->file = VFSPIPE_Open(1, false);
return true;
}
else if (f->flags & HRF_ARCHIVE)
@ -4476,9 +4552,11 @@ qboolean Host_BeginFileDownload(struct dl_download *dl, char *mimetype)
//demos stream, so we want to continue the http download, but we don't want to do anything with the result.
if (f->flags & HRF_DEMO)
result = true;
f->flags |= HRF_ABORT;
Host_DoRunFile(f);
else
{
f->flags |= HRF_ABORT;
Host_DoRunFile(f);
}
return result;
}
void Host_RunFilePrompted(void *ctx, int button)
@ -4527,7 +4605,8 @@ void Host_DoRunFile(hrf_t *f)
if (f->flags & HRF_ABORT)
{
if (f->flags & HRF_MANIFEST)
done:
if (f->flags & HRF_WAITING)
waitingformanifest--;
if (f->srcfile)
@ -4540,8 +4619,6 @@ void Host_DoRunFile(hrf_t *f)
if (!(f->flags & HRF_FILETYPES))
{
char ext[8];
#ifdef WEBCLIENT
if (isurl(f->fname) && !f->srcfile)
{
@ -4552,51 +4629,38 @@ void Host_DoRunFile(hrf_t *f)
dl = HTTP_CL_Get(f->fname, NULL, Host_RunFileDownloaded);
if (dl)
{
f->flags |= HRF_WAITING|HRF_DOWNLOADED;
f->flags |= HRF_DOWNLOADED;
dl->notifystarted = Host_BeginFileDownload;
dl->user_ctx = f;
waitingformanifest++;
if (!(f->flags & HRF_WAITING))
{
f->flags |= HRF_WAITING;
waitingformanifest++;
}
return;
}
}
}
#endif
//if we get here, we have no mime type to give us any clues.
COM_FileExtension(f->fname, ext, sizeof(ext));
if (!Q_strcasecmp(ext, "qwd"))
f->flags |= HRF_DEMO_QWD;
else if (!Q_strcasecmp(ext, "mvd"))
f->flags |= HRF_DEMO_MVD;
else if (!Q_strcasecmp(ext, "dm2"))
f->flags |= HRF_DEMO_DM2;
else if (!Q_strcasecmp(ext, "dem"))
f->flags |= HRF_DEMO_DEM;
else if (!Q_strcasecmp(ext, "qtv"))
f->flags |= HRF_QTVINFO;
else if (!Q_strcasecmp(ext, "fmf"))
f->flags |= HRF_MANIFEST;
else if (!Q_strcasecmp(ext, "bsp"))
f->flags |= HRF_BSP;
else if (!Q_strcasecmp(ext, "pak") || !Q_strcasecmp(ext, "pk3") || !Q_strcasecmp(ext, "pk4") || !Q_strcasecmp(ext, "wad"))
f->flags |= HRF_PACKAGE;
else if (!Q_strcasecmp(ext, "mdl") || !Q_strcasecmp(ext, "md2") || !Q_strcasecmp(ext, "md3") || !Q_strcasecmp(ext, "iqm")
|| !Q_strcasecmp(ext, "psk") || !Q_strcasecmp(ext, "zym") || !Q_strcasecmp(ext, "dpm") || !Q_strcasecmp(ext, "spr") || !Q_strcasecmp(ext, "spr2")
|| !Q_strcasecmp(ext, "obj") || !Q_strcasecmp(ext, "lwo") || !Q_strcasecmp(ext, "ase"))
f->flags |= HRF_MODEL;
f->flags |= Host_GuessFileType(NULL, f->fname);
//if we still don't know what it is, give up.
if (!(f->flags & HRF_FILETYPES))
{
Con_Printf("Host_DoRunFile: unknown filetype\n");
f->flags |= HRF_ABORT;
Host_DoRunFile(f);
return;
goto done;
}
if (f->flags & HRF_MANIFEST)
waitingformanifest++;
{
if (!(f->flags & HRF_WAITING))
{
f->flags |= HRF_WAITING;
waitingformanifest++;
}
}
}
if (f->flags & HRF_DEMO)
@ -4605,9 +4669,7 @@ void Host_DoRunFile(hrf_t *f)
FS_FixupGamedirForExternalFile(f->fname, loadcommand, sizeof(loadcommand));
Cbuf_AddText(va("playdemo \"%s\"\n", loadcommand), RESTRICT_LOCAL);
f->flags |= HRF_ABORT;
Host_DoRunFile(f);
return;
goto done;
}
else if (f->flags & HRF_BSP)
{
@ -4617,9 +4679,7 @@ void Host_DoRunFile(hrf_t *f)
{
COM_StripExtension(qname+5, loadcommand, sizeof(loadcommand));
Cbuf_AddText(va("map \"%s\"\n", loadcommand), RESTRICT_LOCAL);
f->flags |= HRF_ABORT;
Host_DoRunFile(f);
return;
goto done;
}
snprintf(loadcommand, sizeof(loadcommand), "map \"%s\"\n", shortname);
@ -4672,9 +4732,7 @@ void Host_DoRunFile(hrf_t *f)
}
}
f->flags |= HRF_ABORT;
Host_DoRunFile(f);
return;
goto done;
}
}
}
@ -4684,9 +4742,7 @@ void Host_DoRunFile(hrf_t *f)
Con_Printf("%s is not within the current gamedir\n", f->fname);
else
Cbuf_AddText(va("modelviewer \"%s\"\n", loadcommand), RESTRICT_LOCAL);
f->flags |= HRF_ABORT;
Host_DoRunFile(f);
return;
goto done;
}
else if (f->flags & HRF_ARCHIVE)
{
@ -4703,16 +4759,35 @@ void Host_DoRunFile(hrf_t *f)
{
COM_Gamedir("", packagespaths);
}
f->flags |= HRF_ABORT;
Host_DoRunFile(f);
return;
goto done;
}
else if (f->flags & HRF_CONFIG)
{
if (!(f->flags & HRF_ACTION))
{
Key_Dest_Remove(kdm_console);
M_Menu_Prompt(Host_RunFilePrompted, f, va("Exec %s?\n", COM_SkipPath(f->fname)), "Yes", NULL, "Cancel");
return;
}
if (f->flags & HRF_OPENED)
{
size_t len = VFS_GETLEN(f->srcfile);
char *fdata = BZ_Malloc(len+2);
if (fdata)
{
VFS_READ(f->srcfile, fdata, len);
fdata[len++] = '\n';
fdata[len] = 0;
Cbuf_AddText(fdata, RESTRICT_INSECURE);
BZ_Free(fdata);
}
goto done;
}
}
else if (!(f->flags & HRF_QTVINFO))
{
Con_Printf("Host_DoRunFile: filetype not handled\n");
f->flags |= HRF_ABORT;
Host_DoRunFile(f);
return;
goto done;
}
//at this point we need the file to have been opened.
@ -4740,9 +4815,7 @@ void Host_DoRunFile(hrf_t *f)
if (!f->srcfile)
{
Con_Printf("Unable to open %s\n", f->fname);
f->flags |= HRF_ABORT;
Host_DoRunFile(f);
return;
goto done;
}
if (f->flags & HRF_MANIFEST)
@ -4757,9 +4830,7 @@ void Host_DoRunFile(hrf_t *f)
CL_ParseQTVDescriptor(f->srcfile, f->fname);
f->srcfile = NULL;
f->flags |= HRF_ABORT;
Host_DoRunFile(f);
return;
goto done;
}
VFS_SEEK(f->srcfile, 0);
@ -4768,7 +4839,7 @@ void Host_DoRunFile(hrf_t *f)
if (f->dstfile)
{
//do a real diff.
if (f->srcfile->seekingisabadplan || VFS_GETLEN(f->srcfile) != VFS_GETLEN(f->dstfile))
if (f->srcfile->seekstyle == SS_UNSEEKABLE || VFS_GETLEN(f->srcfile) != VFS_GETLEN(f->dstfile))
{
//if we can't seek, or the sizes differ, just assume that the file is modified.
haschanged = true;
@ -4794,6 +4865,7 @@ void Host_DoRunFile(hrf_t *f)
{
if (!(f->flags & HRF_ACTION))
{
Key_Dest_Remove(kdm_console);
M_Menu_Prompt(Host_RunFilePrompted, f, va("File already exists.\nWhat would you like to do?\n%s\n", displayname), "Overwrite", "Run old", "Cancel");
return;
}
@ -4802,6 +4874,7 @@ void Host_DoRunFile(hrf_t *f)
{
if (!(f->flags & HRF_ACTION))
{
Key_Dest_Remove(kdm_console);
M_Menu_Prompt(Host_RunFilePrompted, f, va("File appears new.\nWould you like to install\n%s\n", displayname), "Install!", "", "Cancel");
return;
}
@ -4814,6 +4887,11 @@ void Host_DoRunFile(hrf_t *f)
f->dstfile = FS_OpenVFS(qname, "wb", FS_GAMEONLY);
if (f->dstfile)
{
#ifdef FTE_TARGET_WEB
VFS_SEEK(f->dstfile, VFS_GETLEN(f->srcfile));
VFS_WRITE(f->dstfile, "zomg", 0); //hack to ensure the file is there, avoiding excessive copies.
VFS_SEEK(f->dstfile, 0);
#endif
while(1)
{
len = VFS_READ(f->srcfile, buffer, sizeof(buffer));
@ -4897,6 +4975,8 @@ qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file)
else
Con_Printf("Unknown url command: %s\n", cmd);
if(file)
VFS_CLOSE(file);
Z_Free(t);
return true;
}
@ -4904,6 +4984,9 @@ qboolean Host_RunFile(const char *fname, int nlen, vfsfile_t *file)
f = Z_Malloc(sizeof(*f) + nlen);
memcpy(f->fname, fname, nlen);
f->fname[nlen] = 0;
f->srcfile = file;
if (file)
f->flags |= HRF_OPENED;
Con_Printf("Opening external file: %s\n", f->fname);
@ -5601,6 +5684,7 @@ void CL_ExecInitialConfigs(char *resetcommand)
Cbuf_AddText ("exec frontend.cfg\n", RESTRICT_LOCAL);
#endif
Cbuf_AddText ("cl_warncmd 1\n", RESTRICT_LOCAL); //and then it's allowed to start moaning.
COM_ParsePlusSets(true);
com_parseutf8.ival = com_parseutf8.value;
@ -5841,10 +5925,6 @@ void Host_Shutdown(void)
M_Shutdown(true);
#ifdef PLUGINS
Plug_Shutdown(false);
#endif
#ifdef CSQC_DAT
CSQC_Shutdown();
#endif
@ -5853,12 +5933,16 @@ void Host_Shutdown(void)
UI_Stop();
#endif
// Host_WriteConfiguration ();
S_Shutdown(true);
CDAudio_Shutdown ();
IN_Shutdown ();
R_ShutdownRenderer(true);
S_Shutdown(true);
#ifdef PLUGINS
Plug_Shutdown(false);
#endif
// Host_WriteConfiguration ();
#ifdef CL_MASTER
MasterInfo_Shutdown();
#endif

View File

@ -31,11 +31,13 @@ static qboolean CL_CheckModelResources (char *name);
#ifdef NQPROT
static char *CLNQ_ParseProQuakeMessage (char *s);
#endif
static void DLC_Poll(qdownload_t *dl);
static void CL_ProcessUserInfo (int slot, player_info_t *player);
char cl_dp_csqc_progsname[128];
int cl_dp_csqc_progssize;
int cl_dp_csqc_progscrc;
int cl_dp_serverextension_download;
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;
#ifdef AVAIL_ZLIB
#ifndef ZEXPORT
@ -45,7 +47,7 @@ int cl_dp_serverextension_download;
#endif
char *svc_qwstrings[] =
static char *svc_qwstrings[] =
{
"svc_bad",
"svc_nop",
@ -175,7 +177,7 @@ char *svc_qwstrings[] =
"???",
};
char *svc_nqstrings[] =
static char *svc_nqstrings[] =
{
"nqsvc_bad",
"nqsvc_nop",
@ -655,7 +657,7 @@ void CL_GetDownloadSizes(unsigned int *filecount, qofs_t *totalsize, qboolean *s
}
}
void CL_DisenqueDownload(char *filename)
static void CL_DisenqueDownload(char *filename)
{
downloadlist_t *dl, *nxt;
if(cl.downloadlist) //remove from enqued download list
@ -683,7 +685,7 @@ void CL_DisenqueDownload(char *filename)
}
#ifdef WEBCLIENT
void CL_WebDownloadFinished(struct dl_download *dl)
static void CL_WebDownloadFinished(struct dl_download *dl)
{
if (dl->status == DL_FAILED)
{
@ -701,7 +703,7 @@ void CL_WebDownloadFinished(struct dl_download *dl)
}
#endif
void CL_SendDownloadStartRequest(char *filename, char *localname, unsigned int flags)
static void CL_SendDownloadStartRequest(char *filename, char *localname, unsigned int flags)
{
static int dlsequence;
qdownload_t *dl;
@ -839,7 +841,7 @@ void CL_DownloadFinished(qdownload_t *dl)
}
}
qboolean CL_CheckFile(const char *filename)
static qboolean CL_CheckFile(const char *filename)
{
if (strstr (filename, ".."))
{
@ -985,7 +987,7 @@ static qboolean CL_CheckMD2Skins (qbyte *precache_model)
return ret;
}
qboolean CL_CheckHLBspWads(char *file)
static qboolean CL_CheckHLBspWads(char *file)
{
lump_t lump;
dheader_t *dh;
@ -1034,7 +1036,7 @@ qboolean CL_CheckHLBspWads(char *file)
return false;
}
qboolean CL_CheckQ2BspWals(char *file)
static qboolean CL_CheckQ2BspWals(char *file)
{
qboolean gotone = false;
#ifdef Q2BSPS
@ -1110,7 +1112,7 @@ static qboolean CL_CheckModelResources (char *name)
Model_NextDownload
=================
*/
void Model_CheckDownloads (void)
static void Model_CheckDownloads (void)
{
char *s;
int i;
@ -1167,7 +1169,7 @@ void Model_CheckDownloads (void)
}
}
int CL_LoadModels(int stage, qboolean dontactuallyload)
static int CL_LoadModels(int stage, qboolean dontactuallyload)
{
int i;
@ -1419,7 +1421,7 @@ int CL_LoadModels(int stage, qboolean dontactuallyload)
return stage;
}
int CL_LoadSounds(int stage, qboolean dontactuallyload)
static int CL_LoadSounds(int stage, qboolean dontactuallyload)
{
int i;
float giveuptime = Sys_DoubleTime()+0.1; //small things get padded into a single frame
@ -1490,7 +1492,7 @@ void Sound_CheckDownload(const char *s)
Sound_NextDownload
=================
*/
void Sound_CheckDownloads (void)
static void Sound_CheckDownloads (void)
{
int i;
@ -1720,7 +1722,7 @@ void CL_SendDownloadReq(sizebuf_t *msg)
#include <zlib.h>
#endif
char *ZLibDownloadDecode(int *messagesize, char *input, int finalsize)
static char *ZLibDownloadDecode(int *messagesize, char *input, int finalsize)
{
char *outbuf = Hunk_TempAlloc(finalsize);
z_stream zs;
@ -1911,7 +1913,7 @@ qboolean DL_Begun(qdownload_t *dl)
return true;
}
void DL_Completed(qdownload_t *dl, qofs_t start, qofs_t end)
static void DL_Completed(qdownload_t *dl, qofs_t start, qofs_t end)
{
struct dlblock_s *prev = NULL, *b, *n, *e;
if (end <= start)
@ -2028,7 +2030,7 @@ void DL_Completed(qdownload_t *dl, qofs_t start, qofs_t end)
static float chunkrate;
void CL_ParseChunkedDownload(qdownload_t *dl)
static void CL_ParseChunkedDownload(qdownload_t *dl)
{
qbyte *svname;
int flag;
@ -2318,7 +2320,7 @@ static void DLC_RequestDownloadChunks(qdownload_t *dl, float frametime)
}
}
void DLC_Poll(qdownload_t *dl)
static void DLC_Poll(qdownload_t *dl)
{
static float lasttime;
DLC_RequestDownloadChunks(dl, realtime - lasttime);
@ -2457,7 +2459,7 @@ CL_ParseDownload
A download message has been received from the server
=====================
*/
void CL_ParseDownload (qboolean zlib)
static void CL_ParseDownload (qboolean zlib)
{
extern cvar_t cl_dlemptyterminate;
int size, percent;
@ -2641,7 +2643,7 @@ qboolean CL_ParseOOBDownload(void)
return true;
}
void CLDP_ParseDownloadData(void)
static void CLDP_ParseDownloadData(void)
{
qdownload_t *dl = cls.download;
unsigned char buffer[1<<16];
@ -2669,7 +2671,7 @@ void CLDP_ParseDownloadData(void)
MSG_WriteShort(&cls.netchan.message, size);
}
void CLDP_ParseDownloadBegin(char *s)
static void CLDP_ParseDownloadBegin(char *s)
{
qdownload_t *dl = cls.download;
char buffer[8192];
@ -2714,7 +2716,7 @@ void CLDP_ParseDownloadBegin(char *s)
}
}
void CLDP_ParseDownloadFinished(char *s)
static void CLDP_ParseDownloadFinished(char *s)
{
qdownload_t *dl = cls.download;
unsigned short runningcrc = 0;
@ -2861,7 +2863,7 @@ void CL_StopUpload(void)
upload_pos = upload_size = 0;
}
qboolean CL_StartUploadFile(char *filename)
static qboolean CL_StartUploadFile(char *filename)
{
if (!COM_CheckParm("-fileul"))
{
@ -2870,7 +2872,10 @@ qboolean CL_StartUploadFile(char *filename)
}
if (cls.state < ca_onserver)
{
Con_Printf("not connected\n");
return false; // gotta be connected
}
CL_StopUpload();
@ -2894,7 +2899,7 @@ qboolean CL_StartUploadFile(char *filename)
=====================================================================
*/
#ifdef CLIENTONLY
float nextdemotime;
static float nextdemotime;
#endif
void CL_ClearParseState(void)
@ -2931,7 +2936,7 @@ void CL_ClearParseState(void)
CL_ParseServerData
==================
*/
void CLQW_ParseServerData (void)
static void CLQW_ParseServerData (void)
{
int pnum;
int clnum;
@ -3223,7 +3228,7 @@ void CLQW_ParseServerData (void)
}
#ifdef Q2CLIENT
void CLQ2_ParseServerData (void)
static void CLQ2_ParseServerData (void)
{
char *str;
int i;
@ -3393,7 +3398,7 @@ void CL_ParseEstablished(void)
}
#ifdef NQPROT
void CLNQ_ParseProtoVersion(void)
static void CLNQ_ParseProtoVersion(void)
{
int protover;
struct netprim_s netprim;
@ -3534,7 +3539,7 @@ static int CL_Darkplaces_Particle_Precache(const char *pname)
//FIXME: move to header
void CL_KeepaliveMessage(void){}
void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caution.
static void CLNQ_ParseServerData(void) //Doesn't change gamedir - use with caution.
{
int nummodels, numsounds;
char *str;
@ -3729,7 +3734,7 @@ Con_DPrintf ("CL_SignonReply: %i\n", cls.signon);
}
#define DEFAULT_VIEWHEIGHT 22
void CLNQ_ParseClientdata (void)
static void CLNQ_ParseClientdata (void)
{
int i;
const int seat = 0;
@ -3885,7 +3890,7 @@ void CLNQ_ParseClientdata (void)
CL_ParseSoundlist
==================
*/
void CL_ParseSoundlist (qboolean lots)
static void CL_ParseSoundlist (qboolean lots)
{
int numsounds;
char *str;
@ -3961,7 +3966,7 @@ void CL_ParseSoundlist (qboolean lots)
CL_ParseModellist
==================
*/
void CL_ParseModellist (qboolean lots)
static void CL_ParseModellist (qboolean lots)
{
int nummodels;
char *str;
@ -4037,10 +4042,8 @@ void CL_ParseModellist (qboolean lots)
SCR_SetLoadingFile("loading data");
}
void CL_ProcessUserInfo (int slot, player_info_t *player);
#ifdef Q2CLIENT
void CLQ2_ParseClientinfo(int i, char *s)
static void CLQ2_ParseClientinfo(int i, char *s)
{
char *model, *name;
player_info_t *player;
@ -4089,7 +4092,7 @@ void CLQ2_ParseClientinfo(int i, char *s)
CL_ProcessUserInfo (i, player);
}
void CLQ2_ParseConfigString (void)
static void CLQ2_ParseConfigString (void)
{
unsigned int i;
char *s;
@ -4259,7 +4262,7 @@ qboolean CL_CheckBaselines (int size)
CL_ParseBaseline
==================
*/
void CL_ParseBaseline (entity_state_t *es, int baselinetype2)
static void CL_ParseBaseline (entity_state_t *es, int baselinetype2)
{
int i;
unsigned int bits;
@ -4289,7 +4292,7 @@ void CL_ParseBaseline (entity_state_t *es, int baselinetype2)
es->trans = (bits & FITZ_B_ALPHA) ? MSG_ReadByte() : 255;
es->scale = (bits & RMQFITZ_B_SCALE) ? MSG_ReadByte() : 16;
}
void CL_ParseBaselineDelta (void)
static void CL_ParseBaselineDelta (void)
{
entity_state_t es;
@ -4302,33 +4305,7 @@ void CL_ParseBaselineDelta (void)
memcpy(cl_baselines + es.number, &es, sizeof(es));
}
void CLNQ_ParseBaseline2 (entity_state_t *es, qboolean dp)
{
int i;
int bits;
memcpy(es, &nullentitystate, sizeof(entity_state_t));
if (dp)
bits = FITZ_B_LARGEMODEL|FITZ_B_LARGEFRAME;
else
bits = MSG_ReadByte();
es->modelindex = (bits & FITZ_B_LARGEMODEL) ? MSG_ReadShort() : MSG_ReadByte();
es->frame = (bits & FITZ_B_LARGEFRAME) ? MSG_ReadShort() : MSG_ReadByte();
es->colormap = MSG_ReadByte();
es->skinnum = MSG_ReadByte();
for (i=0 ; i<3 ; i++)
{
es->origin[i] = MSG_ReadCoord ();
es->angles[i] = MSG_ReadAngle ();
}
es->trans = (bits & FITZ_B_ALPHA) ? MSG_ReadByte() : 255;
es->scale = (bits & RMQFITZ_B_SCALE) ? MSG_ReadByte() : 16;
}
void CLQ2_Precache_f (void)
static void CLQ2_Precache_f (void)
{
Model_CheckDownloads();
Sound_CheckDownloads();
@ -4353,7 +4330,7 @@ like torches
=====================
*/
void R_StaticEntityToRTLight(int i);
void CL_ParseStaticProt (int baselinetype)
static void CL_ParseStaticProt (int baselinetype)
{
entity_t *ent;
int i;
@ -4486,7 +4463,7 @@ void CL_ParseStaticProt (int baselinetype)
CL_ParseStaticSound
===================
*/
void CL_ParseStaticSound (qboolean large)
static void CL_ParseStaticSound (qboolean large)
{
extern cvar_t cl_staticsounds;
vec3_t org;
@ -4525,7 +4502,7 @@ ACTION MESSAGES
CL_ParseStartSoundPacket
==================
*/
void CLQW_ParseStartSoundPacket(void)
static void CLQW_ParseStartSoundPacket(void)
{
vec3_t pos;
int channel, ent;
@ -4579,7 +4556,7 @@ void CLQW_ParseStartSoundPacket(void)
}
#ifdef Q2CLIENT
void CLQ2_ParseStartSoundPacket(void)
static void CLQ2_ParseStartSoundPacket(void)
{
vec3_t pos_v;
float *pos;
@ -4674,7 +4651,7 @@ void CLQ2_ParseStartSoundPacket(void)
#endif
#if defined(NQPROT) || defined(PEXT_SOUNDDBL)
void CLNQ_ParseStartSoundPacket(void)
static void CLNQ_ParseStartSoundPacket(void)
{
vec3_t pos, vel;
int channel, ent;
@ -4954,7 +4931,7 @@ void CL_NewTranslation (int slot)
CL_UpdateUserinfo
==============
*/
void CL_ProcessUserInfo (int slot, player_info_t *player)
static void CL_ProcessUserInfo (int slot, player_info_t *player)
{
int i;
char *col;
@ -5030,7 +5007,7 @@ void CL_ProcessUserInfo (int slot, player_info_t *player)
CL_UpdateUserinfo
==============
*/
void CL_UpdateUserinfo (void)
static void CL_UpdateUserinfo (void)
{
int slot;
player_info_t *player;
@ -5062,7 +5039,7 @@ void CL_UpdateUserinfo (void)
CL_SetInfo
==============
*/
void CL_ParseSetInfo (void)
static void CL_ParseSetInfo (void)
{
int slot;
player_info_t *player;
@ -5094,7 +5071,7 @@ void CL_ParseSetInfo (void)
CL_ServerInfo
==============
*/
void CL_ServerInfo (void)
static void CL_ServerInfo (void)
{
// int slot;
// player_info_t *player;
@ -5153,7 +5130,7 @@ static void CL_SetStat_Internal (int pnum, int stat, int ivalue, float fvalue)
}
#ifdef NQPROT
void CL_SetStatMovevar(int pnum, int stat, float value)
static void CL_SetStatMovevar(int pnum, int stat, float value)
{
switch(stat)
{
@ -5247,7 +5224,7 @@ static void CL_SetStatNumeric (int pnum, int stat, int ivalue, float fvalue)
#endif
}
void CL_SetStatString (int pnum, int stat, char *value)
static void CL_SetStatString (int pnum, int stat, char *value)
{
if (stat < 0 || stat >= MAX_CL_STATS)
return;
@ -5275,7 +5252,7 @@ void CL_SetStatString (int pnum, int stat, char *value)
CL_MuzzleFlash
==============
*/
void CL_MuzzleFlash (int entnum)
static void CL_MuzzleFlash (int entnum)
{
dlight_t *dl;
player_state_t *pl;
@ -5363,7 +5340,7 @@ void CL_MuzzleFlash (int entnum)
#ifdef Q2CLIENT
void Q2S_StartSound(vec3_t origin, int entnum, int entchannel, sfx_t *sfx, float fvol, float attenuation, float timeofs);
void CLQ2_ParseMuzzleFlash (void)
static void CLQ2_ParseMuzzleFlash (void)
{
vec3_t fv, rv, dummy;
dlight_t *dl;
@ -5556,7 +5533,7 @@ void CLQ2_ParseMuzzleFlash (void)
}
}
void CLQ2_ParseMuzzleFlash2 (void)
static void CLQ2_ParseMuzzleFlash2 (void)
{
int ent;
int flash_number;
@ -5570,7 +5547,7 @@ void CLQ2_ParseMuzzleFlash2 (void)
CLQ2_RunMuzzleFlash2(ent, flash_number);
}
void CLQ2_ParseInventory (int seat)
static void CLQ2_ParseInventory (int seat)
{
unsigned int i;
for (i=0 ; i<Q2MAX_ITEMS ; i++)
@ -5579,7 +5556,7 @@ void CLQ2_ParseInventory (int seat)
#endif
//return if we want to print the message.
char *CL_ParseChat(char *text, player_info_t **player, int *msgflags)
static char *CL_ParseChat(char *text, player_info_t **player, int *msgflags)
{
extern cvar_t cl_chatsound, cl_nofake, cl_teamchatsound, cl_enemychatsound;
int flags;
@ -5672,7 +5649,7 @@ char *CL_ParseChat(char *text, player_info_t **player, int *msgflags)
}
// CL_PlayerColor: returns color and mask for player_info_t
int CL_PlayerColor(player_info_t *plr, qboolean *name_coloured)
static int CL_PlayerColor(player_info_t *plr, qboolean *name_coloured)
{
char *t;
unsigned int c;
@ -5966,8 +5943,8 @@ void CL_PrintChat(player_info_t *plr, char *msg, int plrflags)
// CL_PrintStandardMessage: takes non-chat net messages and performs name coloring
// NOTE: msg is considered destroyable
char acceptedchars[] = {'.', '?', '!', '\'', ',', ':', ' ', '\0'};
void CL_PrintStandardMessage(char *msgtext, int printlevel)
static char acceptedchars[] = {'.', '?', '!', '\'', ',', ':', ' ', '\0'};
static void CL_PrintStandardMessage(char *msgtext, int printlevel)
{
int i;
player_info_t *p;
@ -6047,8 +6024,8 @@ void CL_PrintStandardMessage(char *msgtext, int printlevel)
Con_Printf("%s", fullmessage);
}
char printtext[4096];
void CL_ParsePrint(char *msg, int level)
static char printtext[4096];
static void CL_ParsePrint(char *msg, int level)
{
char n;
if (strlen(printtext) + strlen(msg) >= sizeof(printtext))
@ -6193,8 +6170,8 @@ static void CL_ParseTeamInfo(void)
#endif
char stufftext[4096];
void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds from network segregation.
static char stufftext[4096];
static void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds from network segregation.
{
#ifdef NQPROT
if (!*stufftext && *msg == 1 && !cls.allow_csqc)
@ -6409,7 +6386,7 @@ void CL_ParseStuffCmd(char *msg, int destsplit) //this protects stuffcmds from n
}
}
void CL_ParsePrecache(void)
static void CL_ParsePrecache(void)
{
int i, code = (unsigned short)MSG_ReadShort();
char *s = MSG_ReadString();
@ -6464,7 +6441,7 @@ void CL_ParsePrecache(void)
}
}
void Con_HexDump(qbyte *packet, size_t len)
static void Con_HexDump(qbyte *packet, size_t len)
{
int i;
int pos;
@ -6501,7 +6478,7 @@ void CL_DumpPacket(void)
Con_HexDump(net_message.data, net_message.cursize);
}
void CL_ParsePortalState(void)
static void CL_ParsePortalState(void)
{
int mode = MSG_ReadByte();
int a1, a2;
@ -7126,7 +7103,7 @@ void CLQW_ParseServerMessage (void)
}
#ifdef Q2CLIENT
void CLQ2_ParseZPacket(void)
static void CLQ2_ParseZPacket(void)
{
#ifndef AVAIL_ZLIB
Host_EndGame ("CLQ2_ParseZPacket: zlib not supported in this build");
@ -7171,7 +7148,7 @@ void CLQ2_ParseZPacket(void)
msg_badread = false;
#endif
}
void CLR1Q2_ParseSetting(void)
static void CLR1Q2_ParseSetting(void)
{
int setting = MSG_ReadLong();
int value = MSG_ReadLong();
@ -7467,7 +7444,7 @@ static char *CLNQ_ParseProQuakeMessage (char *s)
return s;
}
qboolean CLNQ_ParseNQPrints(char *s)
static qboolean CLNQ_ParseNQPrints(char *s)
{
int i;
char *start = s;
@ -7569,7 +7546,7 @@ qboolean CLNQ_ParseNQPrints(char *s)
return false;
}
void CLNQ_CheckPlayerIsSpectator(int i)
static void CLNQ_CheckPlayerIsSpectator(int i)
{
cl.players[i].spectator =
(cl.players[i].frags==-999) || //DP mods tend to use -999

View File

@ -2119,89 +2119,105 @@ typedef struct _TargaHeader {
#if defined(AVAIL_JPEGLIB) && !defined(NO_JPEG)
qboolean screenshotJPEG(char *filename, enum fs_relative fsroot, int compression, qbyte *screendata, int screenwidth, int screenheight, enum uploadfmt fmt);
qboolean screenshotJPEG(char *filename, enum fs_relative fsroot, int compression, qbyte *screendata, int bytestride, int screenwidth, int screenheight, enum uploadfmt fmt);
#endif
#ifdef AVAIL_PNGLIB
int Image_WritePNG (char *filename, enum fs_relative fsroot, int compression, void **buffers, int numbuffers, int width, int height, enum uploadfmt fmt);
int Image_WritePNG (char *filename, enum fs_relative fsroot, int compression, void **buffers, int numbuffers, int bytestride, int width, int height, enum uploadfmt fmt);
#endif
void WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, int width, int height);
void WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, int bytestride, int width, int height);
qboolean WriteTGA(char *filename, enum fs_relative fsroot, qbyte *rgb_buffer, int width, int height, enum uploadfmt fmt)
qboolean WriteTGA(char *filename, enum fs_relative fsroot, qbyte *fte_restrict rgb_buffer, int bytestride, int width, int height, enum uploadfmt fmt)
{
size_t c, i;
vfsfile_t *vfs;
if (fmt != TF_BGRA32 && fmt != TF_RGB24 && fmt != TF_RGBA32 && fmt != TF_BGR24)
if (fmt != TF_BGRA32 && fmt != TF_RGB24 && fmt != TF_RGBA32 && fmt != TF_BGR24 && fmt != TF_RGBX32 && fmt != TF_BGRX32)
return false;
FS_CreatePath(filename, fsroot);
vfs = FS_OpenVFS(filename, "wb", fsroot);
if (vfs)
{
int ipx,opx;
qboolean rgb;
unsigned char header[18];
memset (header, 0, 18);
header[2] = 2; // uncompressed type
if (fmt == TF_BGRA32 || fmt == TF_RGBA32)
{
rgb = fmt==TF_RGBA32;
ipx = 4;
opx = 4;
}
else if (fmt == TF_RGBX32 || fmt == TF_BGRX32)
{
rgb = fmt==TF_RGBX32;
ipx = 4;
opx = 3;
}
else
{
rgb = fmt==TF_RGB24;
ipx = 3;
opx = 3;
}
header[2] = 2; // uncompressed type
header[12] = width&255;
header[13] = width>>8;
header[14] = height&255;
header[15] = height>>8;
header[16] = 24; // pixel size
header[16] = opx*8; // pixel size
header[17] = 0x00; // flags
if (fmt == TF_BGRA32)
{
#if 0
header[16] = 32;
#else
qbyte tmp[3];
// compact+swap
c = width*height;
for (i=0 ; i<c ; i++)
{
tmp[0] = rgb_buffer[i*4+0];
tmp[1] = rgb_buffer[i*4+1];
tmp[2] = rgb_buffer[i*4+2];
rgb_buffer[i*3+0] = tmp[0];
rgb_buffer[i*3+1] = tmp[1];
rgb_buffer[i*3+2] = tmp[2];
}
c *= 3;
#endif
if (bytestride < 0)
{ //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
}
else if (fmt == TF_BGR24)
c = width*height*3;
else if (fmt == TF_RGBA32)
{
int s = 3;
qbyte tmp[3];
#if 0
s = 4;
header[16] = s*8;
#endif
// compact+swap
c = width*height;
for (i=0 ; i<c ; i++)
{
tmp[0] = rgb_buffer[i*4+0];
tmp[1] = rgb_buffer[i*4+1];
tmp[2] = rgb_buffer[i*4+2];
rgb_buffer[i*s+0] = tmp[2];
rgb_buffer[i*s+1] = tmp[1];
rgb_buffer[i*s+2] = tmp[0];
}
c *= s;
}
else if (fmt == TF_RGB24)
{
qbyte temp;
// swap r+b in place
c = width*height*3;
for (i=0 ; i<c ; i+=3)
{
temp = rgb_buffer[i];
rgb_buffer[i] = rgb_buffer[i+2];
rgb_buffer[i+2] = temp;
}
else //our data is top-down, set up the header to also be top-down.
header[17] = 0x20;
if (ipx == opx && !rgb)
{ //can just directly write it
//bgr24, bgra24
c = width*height*opx;
}
else
c = 0;
{
//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
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];
}
}
else
{ //(bgr24), bgrx32, (bgra32)
qbyte tmp[3];
// compact in place
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];
}
}
c *= opx;
}
VFS_WRITE(vfs, header, sizeof(header));
VFS_WRITE(vfs, rgb_buffer, c);
VFS_CLOSE(vfs);
@ -2242,13 +2258,24 @@ int MipColor(int r, int g, int b)
return best;
}
qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer, int numbuffers, int width, int height, enum uploadfmt fmt)
qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer, int numbuffers, int bytestride, int width, int height, enum uploadfmt fmt)
{
#if defined(AVAIL_PNGLIB) || defined(AVAIL_JPEGLIB)
extern cvar_t scr_sshot_compression;
#endif
char ext[8];
void *nbuffers[2];
if (!bytestride)
bytestride = width*4;
if (bytestride < 0)
{ //fix up the buffers so callers don't have to.
int nb = numbuffers;
for (numbuffers = 0; numbuffers < nb && numbuffers < countof(nbuffers); numbuffers++)
nbuffers[numbuffers] = (char*)buffer[numbuffers] - bytestride*(height-1);
buffer = nbuffers;
}
COM_FileExtension(filename, ext, sizeof(ext));
@ -2258,14 +2285,14 @@ qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer,
//png can do bgr+rgb
//rgba bgra will result in an extra alpha chan
//actual stereo is also supported. huzzah.
return Image_WritePNG(filename, fsroot, scr_sshot_compression.value, buffer, numbuffers, width, height, fmt);
return Image_WritePNG(filename, fsroot, scr_sshot_compression.value, buffer, numbuffers, bytestride, width, height, fmt);
}
else
#endif
#ifdef AVAIL_JPEGLIB
if (!Q_strcasecmp(ext, "jpeg") || !Q_strcasecmp(ext, "jpg"))
{
return screenshotJPEG(filename, fsroot, scr_sshot_compression.value, buffer[0], width, height, fmt);
return screenshotJPEG(filename, fsroot, scr_sshot_compression.value, buffer[0], bytestride, width, height, fmt);
}
else
#endif
@ -2278,15 +2305,16 @@ qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer,
{
int y, x, s;
qbyte *src, *dest;
qbyte *newbuf = buffer[0];
if (fmt == TF_RGB24 || fmt == TF_RGBA32)
qbyte *srcbuf = buffer[0], *dstbuf;
if (fmt == TF_RGB24 || fmt == TF_RGBA32 || fmt == TF_RGBX32)
{
dstbuf = malloc(width*height);
s = (fmt == TF_RGB24)?3:4;
// convert in-place to eight bit
for (y = 0; y < height; y++)
{
src = newbuf + (width * s * y);
dest = newbuf + (width * y);
src = srcbuf + (bytestride * y);
dest = dstbuf + (width * y);
for (x = 0; x < width; x++) {
*dest++ = MipColor(src[0], src[1], src[2]);
@ -2294,14 +2322,15 @@ qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer,
}
}
}
else if (fmt == TF_BGR24 || fmt == TF_BGRA32)
else if (fmt == TF_BGR24 || fmt == TF_BGRA32 || fmt == TF_BGRX32)
{
dstbuf = malloc(width*height);
s = (fmt == TF_BGR24)?3:4;
// convert in-place to eight bit
for (y = 0; y < height; y++)
{
src = newbuf + (width * s * y);
dest = newbuf + (width * y);
src = srcbuf + (bytestride * y);
dest = dstbuf + (width * y);
for (x = 0; x < width; x++) {
*dest++ = MipColor(src[2], src[1], src[0]);
@ -2312,10 +2341,11 @@ qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer,
else
return false;
WritePCXfile (filename, fsroot, newbuf, width, height, width, host_basepal, false);
WritePCXfile (filename, fsroot, dstbuf, width, height, width, host_basepal, false);
free(dstbuf);
}
else if (!Q_strcasecmp(ext, "tga")) //tga
return WriteTGA(filename, fsroot, buffer[0], width, height, fmt);
return WriteTGA(filename, fsroot, buffer[0], bytestride, width, height, fmt);
else //extension / type not recognised.
return false;
return true;
@ -2333,7 +2363,7 @@ static void SCR_ScreenShot_f (void)
int i;
vfsfile_t *vfs;
void *rgbbuffer;
int width, height;
int stride, width, height;
enum uploadfmt fmt;
if (!VID_GetRGBInfo)
@ -2378,10 +2408,10 @@ static void SCR_ScreenShot_f (void)
FS_NativePath(pcxname, FS_GAMEONLY, sysname, sizeof(sysname));
rgbbuffer = VID_GetRGBInfo(&width, &height, &fmt);
rgbbuffer = VID_GetRGBInfo(&stride, &width, &height, &fmt);
if (rgbbuffer)
{
if (SCR_ScreenShot(pcxname, FS_GAMEONLY, &rgbbuffer, 1, width, height, fmt))
if (SCR_ScreenShot(pcxname, FS_GAMEONLY, &rgbbuffer, 1, stride, width, height, fmt))
{
Con_Printf ("Wrote %s\n", sysname);
BZ_Free(rgbbuffer);
@ -2392,7 +2422,7 @@ static void SCR_ScreenShot_f (void)
Con_Printf (CON_ERROR "Couldn't write %s\n", sysname);
}
static void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, enum uploadfmt *fmt)
static void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, int *stride, enum uploadfmt *fmt)
{
int width, height;
void *buf;
@ -2426,11 +2456,11 @@ static void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, enum uploadfmt *f
if (!okay)
{
buf = NULL;
width = height = 0;
*stride = width = height = 0;
*fmt = TF_INVALID;
}
else
buf = VID_GetRGBInfo(&width, &height, fmt);
buf = VID_GetRGBInfo(stride, &width, &height, fmt);
R2D_RT_Configure(r_refdef.rt_destcolour[0].texname, 0, 0, 0, RT_IMAGEFLAGS);
Q_strncpyz(r_refdef.rt_destcolour[0].texname, "", sizeof(r_refdef.rt_destcolour[0].texname));
@ -2447,6 +2477,7 @@ static void *SCR_ScreenShot_Capture(int fbwidth, int fbheight, enum uploadfmt *f
static void SCR_ScreenShot_Mega_f(void)
{
int stride[2];
int width[2];
int height[2];
void *buffers[2];
@ -2522,7 +2553,7 @@ static void SCR_ScreenShot_Mega_f(void)
r_refdef.stereomethod = STEREO_LEFTONLY;
}
buffers[buf] = SCR_ScreenShot_Capture(fbwidth, fbheight, &fmt[buf]);
buffers[buf] = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride[buf], &fmt[buf]);
width[buf] = fbwidth;
height[buf] = fbheight;
@ -2539,7 +2570,7 @@ static void SCR_ScreenShot_Mega_f(void)
//okay, we drew something, we're good to save a screeny.
if (buffers[0])
{
if (SCR_ScreenShot(filename, FS_GAMEONLY, buffers, numbuffers, width[0], height[0], fmt[0]))
if (SCR_ScreenShot(filename, FS_GAMEONLY, buffers, numbuffers, stride[0], width[0], height[0], fmt[0]))
{
char sysname[1024];
FS_NativePath(filename, FS_GAMEONLY, sysname, sizeof(sysname));
@ -2559,6 +2590,7 @@ static void SCR_ScreenShot_VR_f(void)
{
char *screenyname = Cmd_Argv(1);
int width = atoi(Cmd_Argv(2));
int stride=0;
//we spin the camera around, taking slices from equirectangular screenshots
char filename[MAX_QPATH];
int height; //equirectangular 360 * 180 gives a nice clean ratio
@ -2628,7 +2660,7 @@ static void SCR_ScreenShot_VR_f(void)
r_refdef.eyeoffset[0] = sin(ang) * r_stereo_separation.value * 0.5;
r_refdef.eyeoffset[1] = cos(ang) * r_stereo_separation.value * 0.5;
r_refdef.eyeoffset[2] = 0;
buf = SCR_ScreenShot_Capture(width, height, &fmt);
buf = SCR_ScreenShot_Capture(width, height, &stride, &fmt);
switch(fmt)
{
case TF_BGRA32:
@ -2662,7 +2694,7 @@ static void SCR_ScreenShot_VR_f(void)
r_refdef.eyeoffset[0] *= -1;
r_refdef.eyeoffset[1] *= -1;
r_refdef.eyeoffset[2] = 0;
buf = SCR_ScreenShot_Capture(width, height, &fmt);
buf = SCR_ScreenShot_Capture(width, height, &stride, &fmt);
switch(fmt)
{
case TF_BGRA32:
@ -2689,7 +2721,7 @@ static void SCR_ScreenShot_VR_f(void)
if (fail)
Con_Printf ("Unable to capture suitable screen image\n");
else if (SCR_ScreenShot(filename, FS_GAMEONLY, &left_buffer, 1, width, height*2, TF_BGRA32))
else if (SCR_ScreenShot(filename, FS_GAMEONLY, &left_buffer, 1, stride, width, height*2, TF_BGRA32))
{
char sysname[1024];
FS_NativePath(filename, FS_GAMEONLY, sysname, sizeof(sysname));
@ -2706,7 +2738,7 @@ static void SCR_ScreenShot_VR_f(void)
void SCR_ScreenShot_Cubemap_f(void)
{
void *buffer;
int fbwidth, fbheight;
int stride, fbwidth, fbheight;
uploadfmt_t fmt;
char filename[MAX_QPATH];
char *fname = Cmd_Argv(1);
@ -2737,11 +2769,11 @@ void SCR_ScreenShot_Cubemap_f(void)
Q_snprintfz(filename, sizeof(filename), "cubemaps/%s%s", fname, sides[i].postfix);
COM_DefaultExtension (filename, scr_sshot_type.string, sizeof(filename));
buffer = SCR_ScreenShot_Capture(fbwidth, fbheight, &fmt);
buffer = SCR_ScreenShot_Capture(fbwidth, fbheight, &stride, &fmt);
if (buffer)
{
char sysname[1024];
if (SCR_ScreenShot(filename, FS_GAMEONLY, &buffer, 1, fbwidth, fbheight, fmt))
if (SCR_ScreenShot(filename, FS_GAMEONLY, &buffer, 1, stride, fbwidth, fbheight, fmt))
{
FS_NativePath(filename, FS_GAMEONLY, sysname, sizeof(sysname));
Con_Printf ("Wrote %s\n", sysname);
@ -2814,6 +2846,7 @@ SCR_RSShot
*/
qboolean SCR_RSShot (void)
{
int stride;
int truewidth;
int trueheight;
@ -2852,7 +2885,7 @@ qboolean SCR_RSShot (void)
//
// save the pcx file
//
newbuf = VID_GetRGBInfo(&truewidth, &trueheight, &fmt);
newbuf = VID_GetRGBInfo(&truewidth, &trueheight, &stride, &fmt);
if (fmt == TF_INVALID)
return false;

View File

@ -414,13 +414,9 @@ enum qdlabort
QDL_COMPLETED, //rename file, tell server.
};
qboolean DL_Begun(qdownload_t *dl);
void DL_Completed(qdownload_t *dl, qofs_t start, qofs_t end); //notifies the download logic that a chunk of the file is no longer needed.
void DL_Abort(qdownload_t *dl, enum qdlabort aborttype); //just frees the download's resources. does not delete the temp file.
qboolean CL_AllowArbitaryDownload(char *oldname, char *localfile);
//chunked downloads
void DLC_Poll(qdownload_t *dl);
//
// the client_static_t structure is persistant through an arbitrary number
@ -509,7 +505,6 @@ typedef struct
float demoseektime;
qboolean timedemo;
vfsfile_t *demoinfile;
struct dl_download *demoindownload;
float td_lastframe; // to meter out one message a frame
int td_startframe; // host_framecount at start
float td_starttime; // realtime at second frame of timedemo
@ -1674,7 +1669,7 @@ typedef struct
const char *description;
const char *defaultextension;
void *(VARGS *capture_begin) (char *streamname, int videorate, int width, int height, int *sndkhz, int *sndchannels, int *sndbits);
void (VARGS *capture_video) (void *ctx, void *data, int frame, int width, int height, enum uploadfmt fmt);
void (VARGS *capture_video) (void *ctx, int frame, void *data, int stride, int width, int height, enum uploadfmt fmt);
void (VARGS *capture_audio) (void *ctx, void *data, int bytes);
void (VARGS *capture_end) (void *ctx);
} media_encoder_funcs_t;

View File

@ -1005,7 +1005,7 @@ error:
#ifndef NPFTE
int Image_WritePNG (char *filename, enum fs_relative fsroot, int compression, void **buffers, int numbuffers, int width, int height, enum uploadfmt fmt)
int Image_WritePNG (char *filename, enum fs_relative fsroot, int compression, void **buffers, int numbuffers, int bufferstride, int width, int height, enum uploadfmt fmt)
{
char name[MAX_OSPATH];
int i;
@ -1015,7 +1015,7 @@ int Image_WritePNG (char *filename, enum fs_relative fsroot, int compression, vo
png_byte **row_pointers;
struct pngerr errctx;
int pxsize;
int stride = width;
int outwidth = width;
qbyte stereochunk = 0; //cross-eyed
png_unknown_chunk unknowns = {"sTER", &stereochunk, sizeof(stereochunk), PNG_HAVE_PLTE};
@ -1025,13 +1025,13 @@ int Image_WritePNG (char *filename, enum fs_relative fsroot, int compression, vo
if (numbuffers == 2)
{
stride = width;
if (stride & 7) //standard stereo images must be padded to 8 pixels width padding between
stride += 8-(stride & 7);
stride += width;
outwidth = width;
if (outwidth & 7) //standard stereo images must be padded to 8 pixels width padding between
outwidth += 8-(outwidth & 7);
outwidth += width;
}
else //arrange them all horizontally
stride = width * numbuffers;
outwidth = width * numbuffers;
if (!LibPNG_Init())
return false;
@ -1077,43 +1077,50 @@ err:
#endif
qpng_set_compression_level(png_ptr, Z_NO_COMPRESSION + (compression*(Z_BEST_COMPRESSION-Z_NO_COMPRESSION))/100);
if (fmt == TF_BGR24 || fmt == TF_BGRA32)
if (fmt == TF_BGR24 || fmt == TF_BGRA32 || fmt == TF_BGRX32)
qpng_set_bgr(png_ptr);
if (fmt == TF_RGBA32 || fmt == TF_BGRA32)
{
pxsize = 4;
qpng_set_IHDR(png_ptr, info_ptr, stride, height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
qpng_set_IHDR(png_ptr, info_ptr, outwidth, height, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
}
else if (fmt == TF_RGBX32 || fmt == TF_BGRX32)
{
pxsize = 4;
qpng_set_IHDR(png_ptr, info_ptr, outwidth, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
}
else
{
pxsize = 3;
qpng_set_IHDR(png_ptr, info_ptr, stride, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
qpng_set_IHDR(png_ptr, info_ptr, outwidth, height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
}
if (numbuffers == 2) //flag it as a standard stereographic image
qpng_set_unknown_chunks(png_ptr, info_ptr, &unknowns, 1);
qpng_write_info(png_ptr, info_ptr);
if (fmt == TF_RGBX32 || fmt == TF_BGRX32)
png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
if (numbuffers == 2)
{ //standard stereographic png image.
qbyte *pixels, *left, *right;
//we need to pack the data into a single image for libpng to use
row_pointers = Z_Malloc (sizeof(png_byte *) * height + stride*height*pxsize); //must be zeroed, because I'm too lazy to specially deal with padding.
row_pointers = Z_Malloc (sizeof(png_byte *) * height + outwidth*height*pxsize); //must be zeroed, because I'm too lazy to specially deal with padding.
if (!row_pointers)
goto err;
pixels = (qbyte*)row_pointers + height;
//png requires right then left, which is a bit weird.
right = pixels;
left = right + (stride-width)*pxsize;
left = right + (outwidth-width)*pxsize;
for (i = 0; i < height; i++)
{
if ((qbyte*)buffers[1])
memcpy(right + i*stride*pxsize, (qbyte*)buffers[1] + i*pxsize*width, pxsize * width);
memcpy(right + i*outwidth*pxsize, (qbyte*)buffers[1] + i*bufferstride, pxsize * width);
if ((qbyte*)buffers[0])
memcpy(left + i*stride*pxsize, (qbyte*)buffers[0] + i*pxsize*width, pxsize * width);
row_pointers[height - i - 1] = pixels + i * stride * pxsize;
memcpy(left + i*outwidth*pxsize, (qbyte*)buffers[0] + i*bufferstride, pxsize * width);
row_pointers[i] = pixels + i * outwidth * pxsize;
}
}
else if (numbuffers == 1)
@ -1122,14 +1129,14 @@ err:
if (!row_pointers)
goto err;
for (i = 0; i < height; i++)
row_pointers[height - i - 1] = (qbyte*)buffers[0] + i * width * pxsize;
row_pointers[i] = (qbyte*)buffers[0] + i * bufferstride;
}
else
{ //pack all images horizontally, because preventing people from doing the whole cross-eyed thing is cool, or something.
qbyte *pixels;
int j;
//we need to pack the data into a single image for libpng to use
row_pointers = BZ_Malloc (sizeof(png_byte *) * height + stride*height*pxsize);
row_pointers = BZ_Malloc (sizeof(png_byte *) * height + outwidth*height*pxsize);
if (!row_pointers)
goto err;
pixels = (qbyte*)row_pointers + height;
@ -1138,9 +1145,9 @@ err:
for (j = 0; j < numbuffers; j++)
{
if (buffers[j])
memcpy(pixels+(width*j + i*stride)*pxsize, (qbyte*)buffers[j] + i*pxsize*width, pxsize * width);
memcpy(pixels+(width*j + i*outwidth)*pxsize, (qbyte*)buffers[j] + i*bufferstride, pxsize * width);
}
row_pointers[height - i - 1] = pixels + i * stride * pxsize;
row_pointers[i] = pixels + i * outwidth * pxsize;
}
}
qpng_write_image(png_ptr, row_pointers);
@ -1647,9 +1654,9 @@ void ftejpeg_mem_dest (j_compress_ptr cinfo, vfsfile_t *vfs)
METHODDEF(void) jpeg_error_exit (j_common_ptr cinfo)
{
longjmp(((jpeg_error_mgr_wrapper *) cinfo->err)->setjmp_buffer, 1);
longjmp(((jpeg_error_mgr_wrapper *) cinfo->err)->setjmp_buffer, 1);
}
qboolean screenshotJPEG(char *filename, enum fs_relative fsroot, int compression, qbyte *screendata, int screenwidth, int screenheight, enum uploadfmt fmt)
qboolean screenshotJPEG(char *filename, enum fs_relative fsroot, int compression, qbyte *screendata, int stride, int screenwidth, int screenheight, enum uploadfmt fmt)
{
qbyte *buffer;
vfsfile_t *outfile;
@ -1657,58 +1664,61 @@ qboolean screenshotJPEG(char *filename, enum fs_relative fsroot, int compression
struct jpeg_compress_struct cinfo;
JSAMPROW row_pointer[1];
qbyte *rgbdata = NULL;
//convert in-place if needed.
//bgr->rgb may require copying out entirely for the first pixel to work properly.
if (fmt == TF_BGRA32)
{
qbyte *in=screendata, *out=screendata;
if (fmt == TF_BGRA32 || fmt == TF_BGRX32 || fmt == TF_BGR24)
{ //byteswap and strip alpha
size_t ps = (fmt == TF_BGR24)?3:4;
qbyte *in=screendata, *out=rgbdata=Hunk_TempAlloc(screenwidth*screenheight*3);
size_t y, x;
size_t sz = screenwidth*screenheight;
while(sz --> 0)
for (y = 0; y < screenheight; y++)
{
int r = in[2];
int g = in[1];
int b = in[0];
out[0] = r;
out[1] = g;
out[2] = b;
in+=4;
out+=3;
for (x = 0; x < screenwidth; x++)
{
int r = in[2];
int g = in[1];
int b = in[0];
out[0] = r;
out[1] = g;
out[2] = b;
in+=ps;
out+=3;
}
in-=screenwidth*ps;
in+=stride;
}
fmt = TF_RGB24;
stride = screenwidth*3;
screendata = rgbdata;
}
else if (fmt == TF_RGBA32)
{
qbyte *in=screendata, *out=screendata;
else if (fmt == TF_RGBA32 || fmt == TF_RGBX32 || (fmt == TF_RGB24 && stride < 0))
{ //strip alpha, no need to byteswap
size_t ps = (fmt == TF_RGB24)?3:4;
qbyte *in=screendata, *out=rgbdata=Hunk_TempAlloc(screenwidth*screenheight*3);
size_t y, x;
size_t sz = screenwidth*screenheight;
while(sz --> 0)
for (y = 0; y < screenheight; y++)
{
int r = in[0];
int g = in[1];
int b = in[2];
out[0] = r;
out[1] = g;
out[2] = b;
in+=4;
out+=3;
}
fmt = TF_RGB24;
}
else if (fmt == TF_BGR24)
{
qbyte *in=screendata, *out=screendata;
size_t sz = screenwidth*screenheight;
while(sz --> 0)
{
int r = in[0];
int g = in[1];
int b = in[2];
out[0] = r;
out[1] = g;
out[2] = b;
in+=3;
out+=3;
for (x = 0; x < screenwidth; x++)
{
int r = in[0];
int g = in[1];
int b = in[2];
out[0] = r;
out[1] = g;
out[2] = b;
in+=ps;
out+=3;
}
in-=screenwidth*ps;
in+=stride;
}
fmt = TF_RGB24;
stride = screenwidth*3;
screendata = rgbdata;
}
else if (fmt != TF_RGB24)
{
@ -1754,7 +1764,7 @@ qboolean screenshotJPEG(char *filename, enum fs_relative fsroot, int compression
while (cinfo.next_scanline < cinfo.image_height)
{
*row_pointer = &buffer[(cinfo.image_height - cinfo.next_scanline - 1) * cinfo.image_width * 3];
*row_pointer = &buffer[cinfo.next_scanline * stride];
qjpeg_write_scanlines(&cinfo, row_pointer, 1);
}
qjpeg_finish_compress(&cinfo);
@ -1803,9 +1813,6 @@ void WritePCXfile (const char *filename, enum fs_relative fsroot, qbyte *data, i
// pack the image
pack = (qbyte *)(pcx+1);
data += rowbytes * (height - 1);
for (i=0 ; i<height ; i++)
{
for (j=0 ; j<width ; j++)
@ -1820,7 +1827,6 @@ void WritePCXfile (const char *filename, enum fs_relative fsroot, qbyte *data, i
}
data += rowbytes - width;
data -= rowbytes * 2;
}
// write the palette
@ -2812,6 +2818,27 @@ qbyte *Read32BitImageFile(qbyte *buf, int len, int *width, int *height, qboolean
*hasalpha = foundalpha;
return data;
}
else if (w >= 3 && h >= 4 && w*h+sizeof(int)*2+768+2 == len)
{
qboolean foundalpha = false;
qbyte *in = (qbyte*)((int*)buf+2);
qbyte *palette = in + w*h+2, *p;
data = BZ_Malloc(w * h * sizeof(int));
for (i = 0; i < w * h; i++)
{
if (in[i] == 255)
foundalpha = true;
p = palette + 3*in[i];
data[(i<<2)+0] = p[2];
data[(i<<2)+1] = p[1];
data[(i<<2)+2] = p[0];
data[(i<<2)+3] = 255;
}
*width = w;
*height = h;
*hasalpha = foundalpha;
return data;
}
}
TRACE(("dbg: Read32BitImageFile: life sucks\n"));
@ -4984,7 +5011,7 @@ image_t *Image_GetTexture(const char *identifier, const char *subpath, unsigned
if (sizelimit)
dl->sizelimit = sizelimit;
dl->user_ctx = tex;
dl->file = VFSPIPE_Open();
dl->file = VFSPIPE_Open(1, false);
dl->isquery = true;
}
#ifdef MULTITHREAD

View File

@ -1299,6 +1299,7 @@ qboolean Key_EntryLine(unsigned char **line, int lineoffset, int *linepos, int k
return false;
}
}
#ifndef FTE_TARGET_WEB //browser port gets keys stuck down when task switching, especially alt+tab. don't confuse users.
else if (com_parseutf8.ival >= 0) //don't do this for iso8859-1. the major user of that is hexen2 which doesn't have these chars.
{
if (ctrl && !keydown[K_RALT])
@ -1331,6 +1332,7 @@ qboolean Key_EntryLine(unsigned char **line, int lineoffset, int *linepos, int k
if (keydown[K_LALT] && unicode > 32 && unicode < 128)
unicode |= 0xe080; // red char
}
#endif
unicode = utf8_encode(utf8, unicode, sizeof(utf8)-1);
if (unicode)

View File

@ -1072,7 +1072,6 @@ static void PM_PreparePackageList(void)
//figure out what we've previously installed.
if (!loadedinstalled)
{
char nat[MAX_OSPATH];
vfsfile_t *f = FS_OpenVFS(INSTALLEDFILES, "rb", FS_ROOT);
loadedinstalled = true;
if (f)
@ -1084,6 +1083,7 @@ static void PM_PreparePackageList(void)
#ifdef PLUGINS
{
int foundone = false;
char nat[MAX_OSPATH];
FS_NativePath("", FS_BINARYPATH, nat, sizeof(nat));
Con_DPrintf("Loading plugins from \"%s\"\n", nat);
Sys_EnumerateFiles(nat, "fteplug_*" ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, PM_EnumeratedPlugin, &foundone, NULL);
@ -1631,7 +1631,7 @@ static void PM_UpdatePackageList(qboolean autoupdate, int retry)
doautoupdate |= autoupdate;
//kick off the initial tier of downloads.
//kick off the initial tier of list-downloads.
for (i = 0; i < numdownloadablelists; i++)
{
if (downloadablelist[i].received)
@ -1645,13 +1645,13 @@ static void PM_UpdatePackageList(qboolean autoupdate, int retry)
{
downloadablelist[i].curdl->user_num = i;
downloadablelist[i].curdl->file = VFSPIPE_Open();
downloadablelist[i].curdl->file = VFSPIPE_Open(1, false);
downloadablelist[i].curdl->isquery = true;
DL_CreateThread(downloadablelist[i].curdl, NULL, NULL);
}
else
{
Con_Printf("Could not contact server - %s\n", downloadablelist[i].url);
Con_Printf("Could not contact updates server - %s\n", downloadablelist[i].url);
downloadablelist[i].received = -1;
}
}

View File

@ -4,7 +4,10 @@
#include "quakedef.h"
#ifdef GLQUAKE
#include "glquake.h"//fixme
#include "glquake.h"
#endif
#ifdef VKQUAKE
#include "../vk/vkrenderer.h"
#endif
#include "shader.h"
@ -134,6 +137,7 @@ cvar_t media_hijackwinamp = CVAR("media_hijackwinamp", "0");
#endif
int selectedoption=-1;
static int mouseselectedoption=-1;
int numtracks;
int nexttrack=-1;
mediatrack_t *tracks;
@ -142,6 +146,7 @@ char media_iofilename[MAX_OSPATH]="";
#if !defined(NOMEDIAMENU) && !defined(NOBUILTINMENUS)
void Media_LoadTrackNames (char *listname);
void Media_SaveTrackNames (char *listname);
int loadedtracknames;
#endif
qboolean Media_EvaluateNextTrack(void);
@ -177,8 +182,9 @@ float media_fadeouttime;
//return value is the new sample to start playing.
//*starttime says the time into the track that we should resume playing at
//this is on the main thread with the mixer locked, its safe to do stuff, but try not to block
char *Media_NextTrack(int musicchannelnum, float *starttime)
sfx_t *Media_NextTrack(int musicchannelnum, float *starttime)
{
sfx_t *s = NULL;
if (bgmvolume.value <= 0)
return NULL;
@ -208,7 +214,10 @@ char *Media_NextTrack(int musicchannelnum, float *starttime)
if (Media_EvaluateNextTrack())
{
media_playlistcurrent = MEDIA_PLAYLIST;
return media_currenttrack;
if (*media_currenttrack == '#')
return S_PrecacheSound2(media_currenttrack+1, true);
else
return S_PrecacheSound(media_currenttrack);
}
}
if (!media_playlistcurrent && (media_playlisttypes & MEDIA_CVARLIST))
@ -231,6 +240,8 @@ char *Media_NextTrack(int musicchannelnum, float *starttime)
}
else
*starttime = 0;
s = S_PrecacheSound(media_currenttrack);
}
}
}
@ -249,7 +260,7 @@ char *Media_NextTrack(int musicchannelnum, float *starttime)
#ifdef HAVE_JUKEBOX
media_playlistcurrent = MEDIA_GAMEMUSIC;
#endif
return "";
return NULL;
}
#endif
if (*media_playtrack)
@ -259,9 +270,10 @@ char *Media_NextTrack(int musicchannelnum, float *starttime)
Q_strncpyz(media_friendlyname, "", sizeof(media_friendlyname));
media_playlistcurrent = MEDIA_GAMEMUSIC;
#endif
s = S_PrecacheSound(media_currenttrack);
}
}
return media_currenttrack;
return s;
}
//begin cross-fading
@ -887,10 +899,15 @@ void M_Media_Add_f (void)
{
char *fname = Cmd_Argv(1);
if (!loadedtracknames)
Media_LoadTrackNames("sound/media.m3u");
if (Cmd_Argc() == 1)
Con_Printf("%s <track>\n", Cmd_Argv(0));
else
Media_AddTrack(fname);
Media_SaveTrackNames("sound/media.m3u");
}
void M_Media_Remove_f (void)
{
@ -900,6 +917,8 @@ void M_Media_Remove_f (void)
Con_Printf("%s <track>\n", Cmd_Argv(0));
else
Media_RemoveTrack(fname);
Media_SaveTrackNames("sound/media.m3u");
}
@ -920,7 +939,12 @@ void M_Media_Draw (menu_t *menu)
{
mediatrack_t *track;
int y;
int op, i;
int op, i, mop;
qboolean playing;
float time, duration;
char title[256];
playing = S_GetMusicInfo(0, &time, &duration, title, sizeof(title));
#define MP_Hightlight(x,y,text,hl) (hl?M_PrintWhite(x, y, text):M_Print(x, y, text))
@ -943,17 +967,36 @@ void M_Media_Draw (menu_t *menu)
else
{
M_Print (12, 32, "Currently playing:");
M_Print (12, 40, *media_friendlyname?media_friendlyname:media_currenttrack);
if (*title)
{
M_Print (12, 40, title);
M_Print (12, 48, media_currenttrack);
}
else
M_Print (12, 40, *media_friendlyname?media_friendlyname:media_currenttrack);
}
y=52;
y=60;
op = selectedoption - (vid.height-y)/16;
if (op + (vid.height-y)/8>numtracks)
op = numtracks - (vid.height-y)/8;
if (op < MEDIA_MIN)
op = MEDIA_MIN;
mop = (mousecursor_y - y)/8;
mop += MEDIA_MIN;
mouseselectedoption = MEDIA_MIN-1;
if (mousecursor_x < 12 + ((vid.width - 320)>>1) || mousecursor_x > 320-24 + ((vid.width - 320)>>1))
mop = mouseselectedoption;
while(op < 0)
{
if (op == mop)
{
float alphamax = 0.5, alphamin = 0.2;
mouseselectedoption = op;
R2D_ImageColours(.5,.4,0,(sin(realtime*2)+1)*0.5*(alphamax-alphamin)+alphamin);
R2D_FillBlock(12 + ((vid.width - 320)>>1), y, 320-24, 8);
R2D_ImageColours(1.0, 1.0, 1.0, 1.0);
}
switch(op)
{
case MEDIA_VOLUME:
@ -966,8 +1009,7 @@ void M_Media_Draw (menu_t *menu)
break;
case MEDIA_FASTFORWARD:
{
float time, duration;
if (S_GetMusicInfo(0, &time, &duration))
if (playing)
{
int itime = time;
int iduration = duration;
@ -1004,7 +1046,7 @@ void M_Media_Draw (menu_t *menu)
y+=8;
break;
case MEDIA_REPEAT:
if (media_shuffle.value)
if (!media_shuffle.value)
{
if (media_repeat.value)
MP_Hightlight (12, y, "Repeat on", op == selectedoption);
@ -1027,6 +1069,21 @@ void M_Media_Draw (menu_t *menu)
for (track = tracks, i=0; track && i<op; track=track->next, i++);
for (; track; track=track->next, y+=8, op++)
{
if (track->length != (int)duration && *title && !strcmp(track->filename, media_currenttrack))
{
Q_strncpyz(track->nicename, title, sizeof(track->nicename));
track->length = duration;
Media_SaveTrackNames("sound/media.m3u");
}
if (op == mop)
{
float alphamax = 0.5, alphamin = 0.2;
mouseselectedoption = op;
R2D_ImageColours(.5,.4,0,(sin(realtime*2)+1)*0.5*(alphamax-alphamin)+alphamin);
R2D_FillBlock(12 + ((vid.width - 320)>>1), y, 320-24, 8);
R2D_ImageColours(1.0, 1.0, 1.0, 1.0);
}
if (op == selectedoption)
M_PrintWhite (12, y, track->nicename);
else
@ -1146,8 +1203,14 @@ qboolean M_Media_Key (int key, menu_t *menu)
}
}
}
else if (key == K_ENTER || key == K_KP_ENTER)
else if (key == K_ENTER || key == K_KP_ENTER || key == K_MOUSE1)
{
if (key == K_MOUSE1)
{
if (mouseselectedoption < MEDIA_MIN)
return false;
selectedoption = mouseselectedoption;
}
switch(selectedoption)
{
case MEDIA_FASTFORWARD:
@ -1260,6 +1323,9 @@ qboolean M_Media_Key (int key, menu_t *menu)
void M_Menu_Media_f (void)
{
menu_t *menu;
if (!loadedtracknames)
Media_LoadTrackNames("sound/media.m3u");
Key_Dest_Add(kdm_emenu);
menu = M_CreateMenu(0);
@ -1272,6 +1338,20 @@ void M_Menu_Media_f (void)
void Media_SaveTrackNames (char *listname)
{
mediatrack_t *tr;
vfsfile_t *f = FS_OpenVFS(listname, "wb", FS_GAMEONLY);
if (!f)
return;
VFS_PRINTF(f, "#EXTM3U\n");
for (tr = tracks; tr; tr = tr->next)
{
VFS_PRINTF(f, "#EXTINF:%i,%s\n%s\n", tr->length, tr->nicename, tr->filename);
}
VFS_CLOSE(f);
}
//safeprints only.
void Media_LoadTrackNames (char *listname)
{
@ -1307,7 +1387,10 @@ void Media_LoadTrackNames (char *listname)
if (!trackname)
return;
lineend[-1]='\0';
if (lineend > data && lineend[-1] == '\r')
lineend[-1]='\0';
else
lineend[0]='\0';
filename = data = lineend+1;
@ -1315,22 +1398,15 @@ void Media_LoadTrackNames (char *listname)
if (lineend)
{
lineend[-1]='\0';
if (lineend > data && lineend[-1] == '\r')
lineend[-1]='\0';
else
lineend[0]='\0';
data = lineend+1;
}
newtrack = Z_Malloc(sizeof(mediatrack_t));
#ifndef _WIN32 //crossplatform - lcean up any dos names
if (filename[1] == ':')
{
snprintf(newtrack->filename, sizeof(newtrack->filename)-1, "/mnt/%c/%s", filename[0]-'A'+'a', filename+3);
while((filename = strchr(newtrack->filename, '\\')))
*filename = '/';
}
else
#endif
Q_strncpyz(newtrack->filename, filename, sizeof(newtrack->filename));
Q_strncpyz(newtrack->filename, filename, sizeof(newtrack->filename));
Q_strncpyz(newtrack->nicename, trackname, sizeof(newtrack->nicename));
newtrack->length = atoi(len);
newtrack->next = tracks;
@ -1374,17 +1450,7 @@ void Media_LoadTrackNames (char *listname)
#undef dwFlags
#undef lpFormat
#undef lpData
#undef cbData
#undef lTime
///temporary residence for media handling
#include "roq.h"
#ifdef HAVE_API_VFW
#if 0
@ -1563,7 +1629,7 @@ struct cin_s
} image;
struct {
roq_info *roqfilm;
struct roq_info_s *roqfilm;
// float lastmediatime;
float nextframetime;
} roq;
@ -2044,6 +2110,7 @@ cin_t *Media_Plugin_TryLoad(char *name)
//Quake3 RoQ Support
#ifdef Q3CLIENT
#include "roq.h"
static void Media_Roq_Shutdown(struct cin_s *cin)
{
roq_close(cin->roq.roqfilm);
@ -2810,6 +2877,7 @@ struct
int pbo_handle;
#endif
enum uploadfmt format;
int stride;
int width;
int height;
} offscreen_queue[4]; //ringbuffer of offscreen_captureframe...captureframe
@ -2899,13 +2967,13 @@ static void *QDECL capture_raw_begin (char *streamname, int videorate, int width
}
return ctx;
}
static void QDECL capture_raw_video (void *vctx, void *data, int frame, int width, int height, enum uploadfmt fmt)
static void QDECL capture_raw_video (void *vctx, int frame, void *data, int stride, int width, int height, enum uploadfmt fmt)
{
struct capture_raw_ctx *ctx = vctx;
char filename[MAX_OSPATH];
ctx->frames = frame+1;
Q_snprintfz(filename, sizeof(filename), "%s%8.8i.%s", ctx->videonameprefix, frame, ctx->videonameextension);
SCR_ScreenShot(filename, ctx->fsroot, &data, 1, width, height, fmt);
SCR_ScreenShot(filename, ctx->fsroot, &data, 1, stride, width, height, fmt);
if (capturethrottlesize.ival)
{
@ -3128,40 +3196,60 @@ static void *QDECL capture_avi_begin (char *streamname, int videorate, int width
return ctx;
}
static void QDECL capture_avi_video(void *vctx, void *vdata, int frame, int width, int height, enum uploadfmt fmt)
static void QDECL capture_avi_video(void *vctx, int frame, void *vdata, int stride, int width, int height, enum uploadfmt fmt)
{
//vfw api is bottom up.
struct capture_avi_ctx *ctx = vctx;
qbyte *data = vdata;
int c, i;
qbyte temp;
qbyte *data, *in, *out;
int x, y;
if (fmt == TF_BGRA32)
//we need to output a packed bottom-up bgr image.
//switch the input from logically top-down to bottom-up (regardless of the physical ordering of its rows)
in = (qbyte*)vdata + stride*(height-1);
stride = -stride;
if (fmt == TF_BGR24 && stride == width*3)
{ //no work needed!
data = in;
}
else
{
// truncate bgra to bgr
c = width*height;
for (i=0 ; i<c ; i++)
{
data[i*3+0] = data[i*4+0];
data[i*3+1] = data[i*4+1];
data[i*3+2] = data[i*4+2];
int ipx = (fmt == TF_BGR24||fmt == TF_RGB24)?3:4;
data = out = Hunk_TempAlloc(width*height*3);
if (fmt == TF_RGB24 || fmt == TF_RGBX32 || fmt == TF_RGBA32)
{ //byteswap + strip alpha
for (y = height; y --> 0; out += width*3, in += stride)
{
for (x = 0; x < width; x++)
{
out[x*3+0] = in[x*ipx+2];
out[x*3+1] = in[x*ipx+1];
out[x*3+2] = in[x*ipx+0];
}
}
}
else if (fmt == TF_BGR24 || fmt == TF_BGRX32 || fmt == TF_BGRA32)
{ //just strip alpha (or just flip)
for (y = height; y --> 0; out += width*3, in += stride)
{
for (x = 0; x < width; x++)
{
out[x*3+0] = in[x*ipx+0];
out[x*3+1] = in[x*ipx+1];
out[x*3+2] = in[x*ipx+2];
}
}
}
else
{ //probably spammy, but oh well
Con_Printf("capture_avi_video: Unsupported image format\n");
return;
}
}
else if (fmt == TF_RGB24)
{
// swap rgb to bgr
c = width*height*3;
for (i=0 ; i<c ; i+=3)
{
temp = data[i];
data[i] = data[i+2];
data[i+2] = temp;
}
}
else if (fmt != TF_BGR24)
{
Con_Printf("Unsupported image format\n");
return;
}
//FIXME: if we're allocating memory anyway, can we not push this to a thread?
//write it
if (FAILED(qAVIStreamWrite(avi_video_stream(ctx), frame, 1, data, width*height * 3, ((frame%15) == 0)?AVIIF_KEYFRAME:0, NULL, NULL)))
Con_DPrintf("Recoring error\n");
@ -3188,15 +3276,31 @@ static media_encoder_funcs_t capture_avi =
#endif
#ifdef _DEBUG
struct capture_null_context
{
float starttime;
int frames;
};
static void QDECL capture_null_end(void *vctx)
{
struct capture_null_context *ctx = vctx;
float duration = Sys_DoubleTime() - ctx->starttime;
Con_Printf("%d video frames ignored, %g secs, %gfps\n", ctx->frames, duration, ctx->frames/duration);
Z_Free(ctx);
}
static void *QDECL capture_null_begin (char *streamname, int videorate, int width, int height, int *sndkhz, int *sndchannels, int *sndbits)
{
return (void*)~0;
struct capture_null_context *ctx = Z_Malloc(sizeof(*ctx));
*sndkhz = 11025;
*sndchannels = 2;
*sndbits = 32; //floats!
ctx->starttime = Sys_DoubleTime();
return ctx;
}
static void QDECL capture_null_video(void *vctx, void *vdata, int frame, int width, int height, enum uploadfmt fmt)
static void QDECL capture_null_video(void *vctx, int frame, void *vdata, int stride, int width, int height, enum uploadfmt fmt)
{
struct capture_null_context *ctx = vctx;
ctx->frames = frame+1;
}
static void QDECL capture_null_audio(void *vctx, void *data, int bytes)
{
@ -3301,10 +3405,19 @@ double Media_TweekCaptureFrameTime(double oldtime, double time)
return oldtime + time;
}
#ifdef VKQUAKE
static void Media_CapturedFrame (void *data, int bytestride, size_t width, size_t height, enum uploadfmt fmt)
{
if (currentcapture_funcs)
currentcapture_funcs->capture_video(currentcapture_ctx, offscreen_captureframe, data, bytestride, width, height, fmt);
offscreen_captureframe++;
}
#endif
void Media_RecordFrame (void)
{
char *buffer;
int truewidth, trueheight;
int bytestride, truewidth, trueheight;
enum uploadfmt fmt;
if (!currentcapture_funcs)
@ -3413,8 +3526,9 @@ 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, buffer, offscreen_captureframe, offscreen_queue[frame].width, offscreen_queue[frame].height, offscreen_queue[frame].format);
currentcapture_funcs->capture_video(currentcapture_ctx, offscreen_captureframe, firstrow, offscreen_queue[frame].stride, offscreen_queue[frame].width, offscreen_queue[frame].height, offscreen_queue[frame].format);
qglUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);
}
offscreen_captureframe++;
@ -3442,6 +3556,8 @@ void Media_RecordFrame (void)
break;
}
offscreen_queue[frame].stride = vid.pixelwidth*-imagesize;//gl is upside down
imagesize *= offscreen_queue[frame].width * offscreen_queue[frame].height;
qglGenBuffersARB(1, &offscreen_queue[frame].pbo_handle);
@ -3472,49 +3588,25 @@ void Media_RecordFrame (void)
}
else
#endif
#if 0//def VKQUAKE
if (offscreen_format != TF_INVALID && qrenderer == QR_VULKAN)
{
//try and collect any finished frames
while (offscreen_captureframe + countof(offscreen_queue) <= captureframe)
{
frame = offscreen_captureframe%countof(offscreen_queue);
vkFenceWait();
buffer = NULL;//qglMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);
if (buffer)
{
//FIXME: thread these (with audio too, to avoid races)
currentcapture_funcs->capture_video(currentcapture_ctx, buffer, offscreen_captureframe, offscreen_queue[frame].width, offscreen_queue[frame].height, offscreen_queue[frame].format);
//qglUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);
}
offscreen_captureframe++;
}
frame = captureframe%countof(offscreen_queue);
if (no frame yet)
{
create a buffer
map the buffer persistently
}
vkCopyImageToBuffer
vkSubmitFence
}
#ifdef VKQUAKE
if (qrenderer == QR_VULKAN)
VKVID_QueueGetRGBData(Media_CapturedFrame);
else
#endif
{
offscreen_captureframe = captureframe+1;
//submit the current video frame. audio will be mixed to match.
buffer = VID_GetRGBInfo(&truewidth, &trueheight, &fmt);
buffer = VID_GetRGBInfo(&bytestride, &truewidth, &trueheight, &fmt);
if (buffer)
{
currentcapture_funcs->capture_video(currentcapture_ctx, buffer, captureframe, truewidth, trueheight, fmt);
qbyte *firstrow = (bytestride<0)?buffer - bytestride*(trueheight-1):buffer;
currentcapture_funcs->capture_video(currentcapture_ctx, captureframe, firstrow, bytestride, truewidth, trueheight, fmt);
BZ_Free (buffer);
}
else
{
Con_DPrintf("Unable to grab video image\n");
currentcapture_funcs->capture_video(currentcapture_ctx, NULL, captureframe, 0, 0, TF_INVALID);
currentcapture_funcs->capture_video(currentcapture_ctx, captureframe, NULL, 0, 0, 0, TF_INVALID);
}
}
captureframe++;
@ -3709,7 +3801,8 @@ void Media_StopRecordFilm_f (void)
buffer = qglMapBufferARB(GL_PIXEL_PACK_BUFFER_ARB, GL_READ_ONLY_ARB);
if (buffer)
{
currentcapture_funcs->capture_video(currentcapture_ctx, buffer, offscreen_captureframe, offscreen_queue[frame].width, offscreen_queue[frame].height, offscreen_queue[frame].format);
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);
qglUnmapBufferARB(GL_PIXEL_PACK_BUFFER_ARB);
}
offscreen_captureframe++;
@ -3756,7 +3849,7 @@ void Media_StopRecordFilm_f (void)
capture_fakesounddevice = NULL;
if (recordingdemo) //start up their regular audio devices again.
Cmd_ExecuteString("snd_restart", RESTRICT_LOCAL);
S_DoRestart(false);
recordingdemo=false;
@ -3828,21 +3921,21 @@ static void Media_RecordFilm (char *recordingname, qboolean demo)
sndbits = 0;
}
vid.fbpwidth = vid.pixelwidth;
vid.fbpheight = vid.pixelheight;
if (demo && capturewidth.ival && captureheight.ival)
{
#ifdef GLQUAKE
if (demo && capturewidth.ival && captureheight.ival && qrenderer == QR_OPENGL && gl_config.ext_framebuffer_objects)
{
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.framebuffer = capturetexture;
}
else
if (qrenderer == QR_OPENGL && gl_config.ext_framebuffer_objects)
{
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.framebuffer = capturetexture;
}
#endif
{
vid.fbpwidth = vid.pixelwidth;
vid.fbpheight = vid.pixelheight;
}
offscreen_format = TF_INVALID;
@ -3897,11 +3990,25 @@ static void Media_RecordFilm_f (void)
if (Cmd_Argc() != 2)
{
int i;
Con_Printf("capture <filename>\nRecords video output in an avi file.\nUse capturerate and capturecodec to configure.\n");
Con_Printf("capture <filename>\nRecords video output in an avi file.\nUse capturerate and capturecodec to configure.\n\n");
for (i = 0; i < countof(pluginencodersfunc); i++)
{
if (pluginencodersfunc[i])
Con_Printf("%s: %s\n", pluginencodersfunc[i]->drivername, pluginencodersfunc[i]->description);
Con_Printf("%s%s^7: %s\n", !strcmp(pluginencodersfunc[i]->drivername, capturedriver.string)?"^2":"^3", pluginencodersfunc[i]->drivername, pluginencodersfunc[i]->description);
}
Con_Printf("\n");
Con_Printf("Current capture settings:\n");
Con_Printf(" ^[/capturedriver %s^]\n", capturedriver.string);
Con_Printf(" ^[/capturecodec %s^]\n", capturecodec.string);
Con_Printf(" ^[/capturedemowidth %s^]\n", capturewidth.string);
Con_Printf(" ^[/capturedemoheight %s^]\n", captureheight.string);
Con_Printf(" ^[/capturerate %s^]\n", capturerate.string);
Con_Printf(" ^[/capturesound %s^]\n", capturesound.string);
if (capturesound.value)
{
Con_Printf(" ^[/capturesoundchannels %s^]\n", capturesoundchannels.string);
Con_Printf(" ^[/capturesoundbits %s^]\n", capturesoundbits.string);
}
return;
}
@ -4685,9 +4792,11 @@ typedef struct
unsigned int srcoffset; /*in bytes*/
unsigned int srclen; /*in bytes*/
qbyte srcdata[1];
char title[256];
} mp3decoder_t;
static void S_MP3_Purge(sfx_t *sfx)
static void QDECL S_MP3_Purge(sfx_t *sfx)
{
mp3decoder_t *dec = sfx->decoder.buf;
@ -4705,17 +4814,50 @@ static void S_MP3_Purge(sfx_t *sfx)
sfx->loadstate = SLS_NOTLOADED;
}
float S_MP3_Query(sfx_t *sfx, sfxcache_t *buf)
float QDECL S_MP3_Query(sfx_t *sfx, sfxcache_t *buf, char *title, size_t titlesize)
{
mp3decoder_t *dec = sfx->decoder.buf;
//we don't know unless we decode it all
if (buf)
{
}
if (titlesize && dec->srclen >= 128)
{ //id3v1 is a 128 byte blob at the end of the file.
char trimartist[31];
char trimtitle[31];
char *p;
struct
{
char tag[3]; //TAG
char title[30];
char artist[30];
char album[30];
char year[4];
char comment[30];//[28]+null+track
qbyte genre;
} *id3v1 = (void*)(dec->srcdata + dec->srclen-128);
if (id3v1->tag[0] == 'T' && id3v1->tag[1] == 'A' && id3v1->tag[2] == 'G')
{ //yup, there's an id3v1 tag there
memcpy(trimartist, id3v1->artist, 30);
for(p = trimartist+30; p>trimartist && p[-1] == ' '; )
p--;
*p = 0;
memcpy(trimtitle, id3v1->title, 30);
for(p = trimtitle+30; p>trimtitle && p[-1] == ' '; )
p--;
*p = 0;
if (*trimartist && *trimtitle)
Q_snprintfz(title, titlesize, "%.30s - %.30s", trimartist, trimtitle);
else if (*id3v1->title)
Q_snprintfz(title, titlesize, "%.30s", trimtitle);
}
return 1;//no real idea.
}
return 0;
}
/*must be thread safe*/
sfxcache_t *S_MP3_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int length)
sfxcache_t *QDECL S_MP3_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int length)
{
int newlen;
if (buf)
@ -4758,6 +4900,8 @@ sfxcache_t *S_MP3_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int le
strhdr.cbStruct = sizeof(strhdr);
strhdr.pbSrc = dec->srcdata + dec->srcoffset;
strhdr.cbSrcLength = dec->srclen - dec->srcoffset;
if (!strhdr.cbSrcLength)
break;
strhdr.pbDst = buffer;
strhdr.cbDstLength = sizeof(buffer);
@ -4794,11 +4938,13 @@ sfxcache_t *S_MP3_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int le
buf->data = dec->dstdata;
buf->length = dec->dstcount;
buf->loopstart = -1;
buf->numchannels = dec->srcchannels;
buf->soundoffset = dec->dststart;
buf->speed = snd_speed;
buf->width = dec->srcwidth/8;
if (dec->srclen == dec->srcoffset && start >= dec->dststart+dec->dstcount)
return NULL; //once we reach the EOF, start reporting errors.
}
return buf;
}
@ -4821,7 +4967,7 @@ typedef struct
#define MPEGLAYER3_ID_MPEG 1
#endif
qboolean S_LoadMP3Sound (sfx_t *s, qbyte *data, int datalen, int sndspeed)
qboolean QDECL S_LoadMP3Sound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed)
{
WAVEFORMATEX pcm_format;
MPEGLAYER3WAVEFORMAT mp3format;
@ -4843,6 +4989,7 @@ qboolean S_LoadMP3Sound (sfx_t *s, qbyte *data, int datalen, int sndspeed)
s->decoder.purge = S_MP3_Purge;
s->decoder.decodedata = S_MP3_Locate;
s->decoder.querydata = S_MP3_Query;
s->loopstart = -1;
dec->dstdata = NULL;
dec->dstcount = 0;
@ -4883,6 +5030,8 @@ qboolean S_LoadMP3Sound (sfx_t *s, qbyte *data, int datalen, int sndspeed)
}
S_MP3_Locate(s, NULL, 0, 100);
return true;
}
#endif

View File

@ -850,7 +850,7 @@ const char *presetexec[] =
"r_nolerp 0;"
, // nice options
"r_stains 0.75;"
// "r_stains 0.75;"
"gl_texturemode ll;"
#ifndef MINIMAL
// "r_particlesystem script;"
@ -2193,48 +2193,48 @@ void M_Menu_Singleplayer_Cheats_Hexen2 (void)
else
currentmap = 0;
MC_AddRedText(menu, 16, 170, y, "Hexen2 Singleplayer Cheats", false); y+=8;
MC_AddWhiteText(menu, 16, 170, y, "^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082 ", false); y+=8;
y+=8;
#ifndef CLIENTONLY
info->skillcombo = MC_AddCombo(menu,16,170, y, "Difficulty", skilloptions, currentskill); y+=8;
#endif
info->mapcombo = MC_AddCombo(menu,16,170, y, "Map", mapoptions, currentmap); y+=8;
#ifndef CLIENTONLY
MC_AddCheckBox(menu, 16, 170, y, "Cheats", &sv_cheats,0); y+=8;
#endif
MC_AddConsoleCommand(menu, 16, 170, y, "Toggle Godmode", "god\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Toggle Flymode", "fly\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Toggle Noclip", "noclip\n"); y+=8;
#ifndef CLIENTONLY
MC_AddSlider(menu, 16, 170, y, "Gravity", &sv_gravity,0,800,25); y+=8;
#endif
MC_AddSlider(menu, 16, 170, y, "Forward Speed", &cl_forwardspeed,0,1000,50); y+=8;
MC_AddSlider(menu, 16, 170, y, "Side Speed", &cl_sidespeed,0,1000,50); y+=8;
MC_AddSlider(menu, 16, 170, y, "Back Speed", &cl_backspeed,0,1000,50); y+=8;
#ifndef CLIENTONLY
MC_AddSlider(menu, 16, 170, y, "Max Movement Speed", &sv_maxspeed,0,1000,50); y+=8;
#endif
MC_AddConsoleCommand(menu, 16, 170, y, "Sheep Transformation", "impulse 14\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Change To Paladin (lvl3+)", "impulse 171\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Change To Crusader (lvl3+)", "impulse 172\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Change to Necromancer (lvl3+)", "impulse 173\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Change to Assassin (lvl3+)", "impulse 174\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Remove Monsters", "impulse 35\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Freeze Monsters", "impulse 36\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Unfreeze Monsters", "impulse 37\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Increase Level By 1", "impulse 40\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Increase Experience", "impulse 41\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Display Co-ordinates", "impulse 42\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "All Weapons & Mana", "impulse 9\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "All Weapons & Mana & Items", "impulse 43\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "No Enemy Targetting", "notarget\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Enable Crosshair", "crosshair 1\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "20 Of Each Artifact", "impulse 299\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Restart Map", "impulse 99\n"); y+=8;
MC_AddRedText(menu, 16, 170, y, "Hexen2 Singleplayer Cheats", false); y+=8;
MC_AddWhiteText(menu, 16, 170, y, "^Ue080^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue081^Ue082 ", false); y+=8;
y+=8;
#ifndef CLIENTONLY
info->skillcombo = MC_AddCombo(menu,16,170, y, "Difficulty", skilloptions, currentskill); y+=8;
#endif
info->mapcombo = MC_AddCombo(menu,16,170, y, "Map", mapoptions, currentmap); y+=8;
#ifndef CLIENTONLY
MC_AddCheckBox(menu, 16, 170, y, "Cheats", &sv_cheats,0); y+=8;
#endif
MC_AddConsoleCommand(menu, 16, 170, y, "Toggle Godmode", "god\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Toggle Flymode", "fly\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Toggle Noclip", "noclip\n"); y+=8;
#ifndef CLIENTONLY
MC_AddSlider(menu, 16, 170, y, "Gravity", &sv_gravity,0,800,25); y+=8;
#endif
MC_AddSlider(menu, 16, 170, y, "Forward Speed", &cl_forwardspeed,0,1000,50); y+=8;
MC_AddSlider(menu, 16, 170, y, "Side Speed", &cl_sidespeed,0,1000,50); y+=8;
MC_AddSlider(menu, 16, 170, y, "Back Speed", &cl_backspeed,0,1000,50); y+=8;
#ifndef CLIENTONLY
MC_AddSlider(menu, 16, 170, y, "Max Movement Speed", &sv_maxspeed,0,1000,50); y+=8;
#endif
MC_AddConsoleCommand(menu, 16, 170, y, "Sheep Transformation", "impulse 14\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Change To Paladin (lvl3+)", "impulse 171\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Change To Crusader (lvl3+)", "impulse 172\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Change to Necromancer (lvl3+)", "impulse 173\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Change to Assassin (lvl3+)", "impulse 174\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Remove Monsters", "impulse 35\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Freeze Monsters", "impulse 36\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Unfreeze Monsters", "impulse 37\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Increase Level By 1", "impulse 40\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Increase Experience", "impulse 41\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Display Co-ordinates", "impulse 42\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "All Weapons & Mana", "impulse 9\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "All Weapons & Mana & Items", "impulse 43\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "No Enemy Targetting", "notarget\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Enable Crosshair", "crosshair 1\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "20 Of Each Artifact", "impulse 299\n"); y+=8;
MC_AddConsoleCommand(menu, 16, 170, y, "Restart Map", "impulse 99\n"); y+=8;
y+=8;
MC_AddCommand(menu, 16, 170, y, "Apply Changes", M_Apply_SP_Cheats_H2); y+=8;
y+=8;
MC_AddCommand(menu, 16, 170, y, "Apply Changes", M_Apply_SP_Cheats_H2); y+=8;
menu->selecteditem = (union menuoption_s *)info->skillcombo;
menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, 250, 0, cursorpositionY, NULL, false);

View File

@ -436,19 +436,19 @@ menutext 0 24 "Cancel"
*/
void M_Script_Init(void)
{
Cmd_AddCommandD("menuclear", M_MenuS_Clear_f, "Pop the currently scripted menu.");
Cmd_AddCommandD("menucallback", M_MenuS_Callback_f, "Explicitly invoke the active script menu's callback function with the given option set.");
Cmd_AddCommand("conmenu", M_MenuS_Script_f);
Cmd_AddCommand("menubox", M_MenuS_Box_f);
Cmd_AddCommand("menuedit", M_MenuS_Edit_f);
Cmd_AddCommand("menueditpriv", M_MenuS_EditPriv_f);
Cmd_AddCommand("menutext", M_MenuS_Text_f);
Cmd_AddCommand("menutextbig", M_MenuS_TextBig_f);
Cmd_AddCommand("menupic", M_MenuS_Picture_f);
Cmd_AddCommand("menucheck", M_MenuS_CheckBox_f);
Cmd_AddCommand("menuslider", M_MenuS_Slider_f);
Cmd_AddCommand("menubind", M_MenuS_Bind_f);
Cmd_AddCommand("menucomboi", M_MenuS_Comboi_f);
Cmd_AddCommand("menucombos", M_MenuS_Combos_f);
Cmd_AddCommandD("menuclear", M_MenuS_Clear_f, "Pop the currently scripted menu.");
Cmd_AddCommandD("menucallback", M_MenuS_Callback_f, "Explicitly invoke the active script menu's callback function with the given option set.");
Cmd_AddCommandD("conmenu", M_MenuS_Script_f, "conmenu <callback>\nCreates a new (built-in) scripted menu. any following commands that define scipted menu items will add their items to this new menu. The callback will be called with argument 'cancel' when the menu is closed.");
Cmd_AddCommandD("menubox", M_MenuS_Box_f, "x y width height");
Cmd_AddCommandD("menuedit", M_MenuS_Edit_f, "x y caption cvarname");
Cmd_AddCommandD("menueditpriv", M_MenuS_EditPriv_f, "x y caption def");
Cmd_AddCommandD("menutext", M_MenuS_Text_f, "x y caption cbcommand");
Cmd_AddCommandD("menutextbig", M_MenuS_TextBig_f, "x y caption cbcommand");
Cmd_AddCommandD("menupic", M_MenuS_Picture_f, "x y picname");
Cmd_AddCommandD("menucheck", M_MenuS_CheckBox_f, "x y caption cvarname bitmask");
Cmd_AddCommandD("menuslider", M_MenuS_Slider_f, "x y caption cvarname min max");
Cmd_AddCommandD("menubind", M_MenuS_Bind_f, "x y caption bindcommand");
Cmd_AddCommandD("menucomboi", M_MenuS_Comboi_f, "x y caption cvarname [caption0] [caption1] ...");
Cmd_AddCommandD("menucombos", M_MenuS_Combos_f, "x y caption cvarname [caption0] [value0] [caption1] [value1] ...\nif 'caption0' is { then the options will be parsed from trailing lines\n");
}
#endif

View File

@ -1051,9 +1051,9 @@ void M_Menu_MediaFiles_f (void)
info->numext = 0;
#ifdef HAVE_JUKEBOX
info->ext[info->numext] = ".m3u";
info->command[info->numext] = "mediaplaylist";
info->numext++;
// info->ext[info->numext] = ".m3u";
// info->command[info->numext] = "mediaplaylist";
// info->numext++;
#if defined(AVAIL_MP3_ACM) || defined(FTE_TARGET_WEB)
info->ext[info->numext] = ".mp3";
info->command[info->numext] = "media_add";

View File

@ -119,7 +119,7 @@ extern void (*R_RenderView) (void); // must set r_refdef first
extern qboolean (*VID_Init) (rendererstate_t *info, unsigned char *palette);
extern void (*VID_DeInit) (void);
extern char *(*VID_GetRGBInfo) (int *truevidwidth, int *truevidheight, enum uploadfmt *fmt);
extern char *(*VID_GetRGBInfo) (int *stride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt); //if stride is negative, then the return value points to the last line intead of the first. this allows it to be freed normally.
extern void (*VID_SetWindowCaption) (const char *msg);
extern void SCR_Init (void);
@ -425,7 +425,7 @@ typedef struct rendererinfo_s {
void (*VID_DestroyCursor) (void *cursor); //may be null
void (*VID_SetWindowCaption) (const char *msg);
char *(*VID_GetRGBInfo) (int *truevidwidth, int *truevidheight, enum uploadfmt *fmt);
char *(*VID_GetRGBInfo) (int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt);
qboolean (*SCR_UpdateScreen) (void);

View File

@ -306,7 +306,7 @@ void SV_Master_Worker_Resolved(void *ctx, void *data, size_t a, size_t b)
if (sv.state)
{
//tcp masters require a route
if (na->type == NA_TCP || na->type == NA_TCPV6 || na->type == NA_TLSV4 || na->type == NA_TLSV6)
if (NET_AddrIsReliable(na))
NET_EnsureRoute(svs.sockets, master->cv.name, master->cv.string, false);
//q2+qw masters are given a ping to verify that they're still up
@ -636,6 +636,7 @@ int lastpollsockIPX;
#define FIRSTUDP6SOCKET (FIRSTUDP4SOCKET+POLLUDP4SOCKETS)
#define POLLTOTALSOCKETS (FIRSTUDP6SOCKET+POLLUDP6SOCKETS)
SOCKET pollsocketsList[POLLTOTALSOCKETS];
char pollsocketsBCast[POLLTOTALSOCKETS];
void Master_SetupSockets(void)
{
@ -1366,11 +1367,19 @@ void CLMaster_AddMaster_Worker_Resolved(void *ctx, void *data, size_t a, size_t
if (mast->mastertype == MT_BCAST) //broadcasts
{
if (mast->adr.type == NA_IP)
mast->adr.type = NA_BROADCAST_IP;
memset(mast->adr.address.ip+4, 0xff, sizeof(mast->adr.address.ip));
if (mast->adr.type == NA_IPX)
mast->adr.type = NA_BROADCAST_IPX;
{
memset(mast->adr.address.ipx+0, 0, 4);
memset(mast->adr.address.ipx+4, 0xff, 6);
}
if (mast->adr.type == NA_IPV6)
mast->adr.type = NA_BROADCAST_IP6;
{
memset(mast->adr.address.ip6, 0, sizeof(mast->adr.address.ip6));
mast->adr.address.ip6[0] = 0xff;
mast->adr.address.ip6[1] = 0x02;
mast->adr.address.ip6[15] = 0x01;
}
}
//fix up default ports if not specified
@ -1694,6 +1703,7 @@ qboolean Master_LoadMasterList (char *filename, qboolean withcomment, int defaul
void NET_SendPollPacket(int len, void *data, netadr_t to)
{
unsigned long bcast;
int ret;
struct sockaddr_qstorage addr;
@ -1705,39 +1715,66 @@ void NET_SendPollPacket(int len, void *data, netadr_t to)
if (lastpollsockIPX>=POLLIPXSOCKETS)
lastpollsockIPX=0;
if (pollsocketsList[FIRSTIPXSOCKET+lastpollsockIPX]==INVALID_SOCKET)
pollsocketsList[FIRSTIPXSOCKET+lastpollsockIPX] = IPX_OpenSocket(PORT_ANY, true);
{
pollsocketsList[FIRSTIPXSOCKET+lastpollsockIPX] = IPX_OpenSocket(PORT_ANY);
pollsocketsBCast[FIRSTIPXSOCKET+lastpollsockIPX] = false;
}
if (pollsocketsList[FIRSTIPXSOCKET+lastpollsockIPX]==INVALID_SOCKET)
return; //bother
bcast = !memcmp(to.address.ipx, "\0\0\0\0\xff\xff\xff\xff\xff\xff", sizeof(to.address.ipx));
if (pollsocketsBCast[FIRSTIPXSOCKET+lastpollsockIPX] != bcast)
{
if (setsockopt(pollsocketsList[FIRSTIPXSOCKET+lastpollsockIPX], SOL_SOCKET, SO_BROADCAST, (char *)&bcast, sizeof(bcast)) == -1)
return;
pollsocketsBCast[FIRSTIPXSOCKET+lastpollsockIPX] = bcast;
}
ret = sendto (pollsocketsList[FIRSTIPXSOCKET+lastpollsockIPX], data, len, 0, (struct sockaddr *)&addr, sizeof(addr) );
}
else
#endif
#ifdef IPPROTO_IPV6
#ifdef HAVE_IPV6
if (((struct sockaddr*)&addr)->sa_family == AF_INET6)
{
lastpollsockUDP6++;
if (lastpollsockUDP6>=POLLUDP6SOCKETS)
lastpollsockUDP6=0;
if (pollsocketsList[FIRSTUDP6SOCKET+lastpollsockUDP6]==INVALID_SOCKET)
pollsocketsList[FIRSTUDP6SOCKET+lastpollsockUDP6] = UDP6_OpenSocket(PORT_ANY, true);
{
pollsocketsList[FIRSTUDP6SOCKET+lastpollsockUDP6] = UDP6_OpenSocket(PORT_ANY);
pollsocketsBCast[FIRSTUDP6SOCKET+lastpollsockUDP6] = false;
}
if (pollsocketsList[FIRSTUDP6SOCKET+lastpollsockUDP6]==INVALID_SOCKET)
return; //bother
ret = sendto (pollsocketsList[FIRSTUDP6SOCKET+lastpollsockUDP6], data, len, 0, (struct sockaddr *)&addr, sizeof(addr) );
}
else
#endif
#ifdef HAVE_IPV4
if (((struct sockaddr*)&addr)->sa_family == AF_INET)
{
lastpollsockUDP4++;
if (lastpollsockUDP4>=POLLUDP4SOCKETS)
lastpollsockUDP4=0;
if (pollsocketsList[FIRSTUDP4SOCKET+lastpollsockUDP4]==INVALID_SOCKET)
pollsocketsList[FIRSTUDP4SOCKET+lastpollsockUDP4] = UDP_OpenSocket(PORT_ANY, true);
{
pollsocketsList[FIRSTUDP4SOCKET+lastpollsockUDP4] = UDP_OpenSocket(PORT_ANY);
pollsocketsBCast[FIRSTUDP4SOCKET+lastpollsockUDP4] = false;
}
if (pollsocketsList[FIRSTUDP4SOCKET+lastpollsockUDP4]==INVALID_SOCKET)
return; //bother
bcast = !memcmp(to.address.ip, "\xff\xff\xff\xff", sizeof(to.address.ip));
if (pollsocketsBCast[FIRSTUDP4SOCKET+lastpollsockUDP4] != bcast)
{
if (setsockopt(pollsocketsList[FIRSTUDP4SOCKET+lastpollsockUDP4], SOL_SOCKET, SO_BROADCAST, (char *)&bcast, sizeof(bcast)) == -1)
return;
pollsocketsBCast[FIRSTUDP4SOCKET+lastpollsockUDP4] = bcast;
}
ret = sendto (pollsocketsList[FIRSTUDP4SOCKET+lastpollsockUDP4], data, len, 0, (struct sockaddr *)&addr, sizeof(struct sockaddr_in) );
}
else
#endif
return;
if (ret == -1)

View File

@ -603,7 +603,7 @@ void QCBUILTIN PF_soundlength (pubprogfuncs_t *prinst, struct globalvars_s *pr_g
sfxcache_t cachebuf, *cache;
if (sfx->decoder.querydata)
{
G_FLOAT(OFS_RETURN) = sfx->decoder.querydata(sfx, NULL);
G_FLOAT(OFS_RETURN) = sfx->decoder.querydata(sfx, NULL, NULL, 0);
return;
}
else if (sfx->decoder.decodedata)

View File

@ -5624,7 +5624,7 @@ static void QCBUILTIN PF_resourcestatus(pubprogfuncs_t *prinst, struct globalvar
}
}
if (!sfx)
sfx = S_FindName(resname, doload);
sfx = S_FindName(resname, doload, false);
if (!sfx)
G_FLOAT(OFS_RETURN) = RESSTATE_NOTKNOWN;
else

View File

@ -169,7 +169,7 @@ qboolean r_softwarebanding;
cvar_t r_speeds = CVAR ("r_speeds", "0");
cvar_t r_stainfadeammount = CVAR ("r_stainfadeammount", "1");
cvar_t r_stainfadetime = CVAR ("r_stainfadetime", "1");
cvar_t r_stains = CVARFC("r_stains", IFMINIMAL("0","0.75"),
cvar_t r_stains = CVARFC("r_stains", IFMINIMAL("0","0"),
CVAR_ARCHIVE,
Cvar_Limiter_ZeroToOne_Callback);
cvar_t r_renderscale = CVARD("r_renderscale", "1", "Provides a way to enable subsampling or super-sampling");
@ -208,9 +208,8 @@ cvar_t scr_conalpha = CVARC ("scr_conalpha", "0.7",
cvar_t scr_consize = CVAR ("scr_consize", "0.5");
cvar_t scr_conspeed = CVAR ("scr_conspeed", "2000");
// 10 - 170
cvar_t scr_fov = CVARFDC("fov", "90",
CVAR_ARCHIVE, "field of vision, 1-170 degrees, standard fov is 90, nquake defaults to 108.",
SCR_Fov_Callback);
cvar_t scr_fov = CVARFCD("fov", "90", CVAR_ARCHIVE, SCR_Fov_Callback,
"field of vision, 1-170 degrees, standard fov is 90, nquake defaults to 108.");
cvar_t scr_printspeed = CVAR ("scr_printspeed", "16");
cvar_t scr_showpause = CVAR ("showpause", "1");
cvar_t scr_showturtle = CVAR ("showturtle", "0");
@ -218,9 +217,7 @@ cvar_t scr_turtlefps = CVAR ("scr_turtlefps", "10");
cvar_t scr_sshot_compression = CVAR ("scr_sshot_compression", "75");
cvar_t scr_sshot_type = CVAR ("scr_sshot_type", "png");
cvar_t scr_sshot_prefix = CVAR ("scr_sshot_prefix", "screenshots/fte-");
cvar_t scr_viewsize = CVARFC("viewsize", "100",
CVAR_ARCHIVE,
SCR_Viewsize_Callback);
cvar_t scr_viewsize = CVARFC("viewsize", "100", CVAR_ARCHIVE, SCR_Viewsize_Callback);
#ifdef ANDROID
cvar_t vid_conautoscale = CVARF ("vid_conautoscale", "2",
@ -319,8 +316,8 @@ cvar_t r_deluxmapping_cvar = CVARAFD ("r_deluxmapping", "0", "r_deluxemappin
qboolean r_deluxmapping;
cvar_t r_shaderblobs = CVARD ("r_shaderblobs", "0", "If enabled, can massively accelerate vid restarts / loading (especially with the d3d renderer). Can cause issues when upgrading engine versions, so this is disabled by default.");
cvar_t gl_compress = CVARFD ("gl_compress", "0", CVAR_ARCHIVE, "Enable automatic texture compression even for textures which are not pre-compressed.");
cvar_t gl_conback = CVARFDC ("gl_conback", "",
CVAR_RENDERERCALLBACK, "Specifies which conback shader/image to use. The Quake fallback is gfx/conback.lmp", R2D_Conback_Callback);
cvar_t gl_conback = CVARFCD ("gl_conback", "",
CVAR_RENDERERCALLBACK, R2D_Conback_Callback, "Specifies which conback shader/image to use. The Quake fallback is gfx/conback.lmp");
//cvar_t gl_detail = CVARF ("gl_detail", "0",
// CVAR_ARCHIVE);
//cvar_t gl_detailscale = CVAR ("gl_detailscale", "5");
@ -372,15 +369,15 @@ cvar_t gl_specular_fallbackexp = CVARF ("gl_specular_fallbackexp", "1", CVAR
cvar_t gl_texture_anisotropic_filtering = CVARFC("gl_texture_anisotropic_filtering", "0",
CVAR_ARCHIVE | CVAR_RENDERERCALLBACK,
Image_TextureMode_Callback);
cvar_t gl_texturemode = CVARFDC("gl_texturemode", "GL_LINEAR_MIPMAP_LINEAR",
CVAR_ARCHIVE | CVAR_RENDERERCALLBACK | CVAR_SAVE,
"Specifies how world/model textures appear. Typically 3 letters eg lln.\nFirst letter can be l(inear) or n(earest) and says how to sample from the mip (when downsampling).\nThe middle letter can . to disable mipmaps, or l or n to describe whether to blend between mipmaps.\nThe third letter says what to do when the texture is too low resolution and is thus the most noticable with low resolution textures, a n will make it look like lego, while an l will keep it smooth.", Image_TextureMode_Callback);
cvar_t gl_texturemode = CVARFCD("gl_texturemode", "GL_LINEAR_MIPMAP_LINEAR",
CVAR_ARCHIVE | CVAR_RENDERERCALLBACK | CVAR_SAVE, Image_TextureMode_Callback,
"Specifies how world/model textures appear. Typically 3 letters eg lln.\nFirst letter can be l(inear) or n(earest) and says how to sample from the mip (when downsampling).\nThe middle letter can . to disable mipmaps, or l or n to describe whether to blend between mipmaps.\nThe third letter says what to do when the texture is too low resolution and is thus the most noticable with low resolution textures, a n will make it look like lego, while an l will keep it smooth.");
cvar_t gl_mipcap = CVARAFC("d_mipcap", "0 1000", "gl_miptexLevel",
CVAR_ARCHIVE | CVAR_RENDERERCALLBACK,
Image_TextureMode_Callback);
cvar_t gl_texturemode2d = CVARFDC("gl_texturemode2d", "GL_LINEAR",
CVAR_ARCHIVE | CVAR_RENDERERCALLBACK,
"Specifies how 2d images are sampled. format is a 3-tupple ", Image_TextureMode_Callback);
cvar_t gl_texturemode2d = CVARFCD("gl_texturemode2d", "GL_LINEAR",
CVAR_ARCHIVE | CVAR_RENDERERCALLBACK, Image_TextureMode_Callback,
"Specifies how 2d images are sampled. format is a 3-tupple ");
cvar_t vid_triplebuffer = CVARAFD ("vid_triplebuffer", "1", "gl_triplebuffer", CVAR_ARCHIVE, "Specifies whether the hardware is forcing tripplebuffering on us, this is the number of extra page swaps required before old data has been completely overwritten.");
@ -973,7 +970,7 @@ void (*R_RenderView) (void); // must set r_refdef first
qboolean (*VID_Init) (rendererstate_t *info, unsigned char *palette);
void (*VID_DeInit) (void);
char *(*VID_GetRGBInfo) (int *truevidwidth, int *truevidheight, enum uploadfmt *fmt);
char *(*VID_GetRGBInfo) (int *stride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt);
void (*VID_SetWindowCaption) (const char *msg);
qboolean (*SCR_UpdateScreen) (void);
@ -1066,6 +1063,7 @@ extern rendererinfo_t swrendererinfo;
#ifdef VKQUAKE
extern rendererinfo_t vkrendererinfo;
//rendererinfo_t headlessvkrendererinfo;
extern rendererinfo_t nvvkrendererinfo;
#endif
#ifdef HEADLESSQUAKE
extern rendererinfo_t headlessrenderer;
@ -1095,6 +1093,9 @@ rendererinfo_t *rendererinfo[] =
#endif
#ifdef VKQUAKE
&vkrendererinfo,
#ifdef _WIN32
&nvvkrendererinfo,
#endif
#endif
#ifdef D3D8QUAKE
&d3d8rendererinfo,

View File

@ -20,7 +20,7 @@ typedef struct {
int idx[4];
} roq_qcell;
typedef struct {
typedef struct roq_info_s {
vfsfile_t *fp;
qofs_t maxpos; //addition for pack files. all seeks add this, all tells subtract this.
int buf_size;

View File

@ -122,7 +122,7 @@ typedef enum uploadfmt
TF_SYSTEMDECODE
} uploadfmt_t;
qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer, int numbuffers, int width, int height, enum uploadfmt fmt);
qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer, int numbuffers, int bytestride, int width, int height, enum uploadfmt fmt);
void SCR_DrawTwoDimensional(int uimenu, qboolean nohud);

View File

@ -526,7 +526,9 @@ static void OpenAL_ListenerUpdate(soundcardinfo_t *sc, int entnum, vec3_t origin
{
palListenerf(AL_GAIN, 1);
palListenerfv(AL_POSITION, oali->ListenPos);
#ifndef FTE_TARGET_WEB //webaudio sucks.
palListenerfv(AL_VELOCITY, oali->ListenVel);
#endif
palListenerfv(AL_ORIENTATION, oali->ListenOri);
}
}
@ -581,6 +583,8 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned
float pitch, cvolume;
int chnum = chan - sc->channel;
ALuint buf;
qboolean stream;
qboolean srcrel;
if (chnum >= oali->max_sources)
{
@ -638,17 +642,19 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned
palDeleteBuffers(1, &buf);
}
}
}
if (!schanged && sfx) //if we don't figure out when they've finished, they'll not get replaced properly.
{
palGetSourcei(src, AL_SOURCE_STATE, &buf);
if (buf != AL_PLAYING)
else if (!schanged && sfx) //if we don't figure out when they've finished, they'll not get replaced properly.
{
schanged = true;
if (chan->flags & CF_FORCELOOP)
chan->pos = 0;
else
sfx = chan->sfx = NULL;
palGetSourcei(src, AL_SOURCE_STATE, &buf);
if (buf != AL_PLAYING)
{
schanged = true;
if(sfx->loopstart != -1)
chan->pos = sfx->loopstart<<PITCHSHIFT;
else if (chan->flags & CF_FORCELOOP)
chan->pos = 0;
else
sfx = chan->sfx = NULL;
}
}
}
@ -673,7 +679,10 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned
if (!(chan->flags & CF_ABSVOLUME))
cvolume *= volume.value*voicevolumemod;
if (schanged || sfx->decoder.decodedata)
//openal doesn't support loopstart (entire sample loops or not at all), so if we're meant to skip the first half then we need to stream it.
stream = sfx->decoder.decodedata || sfx->loopstart > 0;
if (schanged || stream)
{
if (!sfx->openal_buffer)
{
@ -688,39 +697,63 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned
}
return; //not available yet
}
if (sfx->decoder.decodedata)
if (stream)
{
ALuint queuedbufs;
int offset;
sfxcache_t sbuf, *sc;
palGetSourcei(src, AL_BUFFERS_QUEUED, &buf);
if (buf <= 3)
palGetSourcei(src, AL_BUFFERS_QUEUED, &queuedbufs);
while (queuedbufs < 3)
{ //decode periodically instead of all at the start.
sc = sfx->decoder.decodedata(sfx, &sbuf, chan->pos>>PITCHSHIFT, 65536);
int tryduration = snd_speed*0.5;
ssamplepos_t pos = chan->pos>>PITCHSHIFT;
if (sfx->decoder.decodedata)
sc = sfx->decoder.decodedata(sfx, &sbuf, pos, tryduration);
else
{
sc = sfx->decoder.buf;
if (pos >= sc->length)
sc = NULL;
}
if (sc)
{
memcpy(&sbuf, sc, sizeof(sbuf));
//hack up the sound to offset it correctly
offset = (chan->pos>>PITCHSHIFT) - sbuf.soundoffset;
sbuf.data += offset * sc->width*sc->numchannels;
sbuf.length -= offset;
if (pos < sbuf.soundoffset || pos > sbuf.soundoffset+sbuf.length)
sbuf.length = 0; //didn't contain the requested samples... the decoder is struggling.
else
{
offset = pos - sbuf.soundoffset;
sbuf.data += offset * sc->width*sc->numchannels;
sbuf.length -= offset;
}
sbuf.soundoffset = 0;
if (!buf)
{ //queue 124 samples if we're starting/resetting a new stream this is to try to cover up discintinuities caused by low samle rates
sfxcache_t silence = sbuf;
if (sbuf.length > tryduration)
sbuf.length = tryduration; //don't bother queuing more than 3*0.5 secs
if (sbuf.length)
{
//build a buffer with it and queue it up.
//buffer will be purged later on when its unqueued
OpenAL_LoadCache(&buf, &sbuf, max(1,cvolume));
palSourceQueueBuffers(src, 1, &buf);
}
else
{ //decoder isn't ready yet, but didn't signal an error/eof. queue a little silence, because that's better than constant micro stutters
sfxcache_t silence;
silence.speed = snd_speed;
silence.width = 2;
silence.numchannels = 1;
silence.data = NULL;
silence.length = 0.1 * sbuf.speed;
silence.length = 0.1 * silence.speed;
silence.soundoffset = 0;
OpenAL_LoadCache(&buf, &silence, 1);
palSourceQueueBuffers(src, 1, &buf);
}
//build a buffer with it and queue it up.
//buffer will be purged later on when its unqueued
OpenAL_LoadCache(&buf, &sbuf, max(1,cvolume));
palSourceQueueBuffers(src, 1, &buf);
queuedbufs++;
//yay
chan->pos += sbuf.length<<PITCHSHIFT;
@ -731,26 +764,41 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned
}
else
{
palGetSourcei(src, AL_SOURCE_STATE, &buf);
if (buf != AL_PLAYING)
{
if (chan->flags & CF_FORCELOOP)
chan->pos = 0;
// else if(sbuf.loopstart != -1)
// chan->pos = sbuf.loopstart<<PITCHSHIFT;
else
{
chan->sfx = NULL;
if (sfx->decoder.ended)
{
if (!S_IsPlayingSomewhere(sfx))
sfx->decoder.ended(sfx);
}
}
return;
if(sfx->loopstart != -1)
chan->pos = sfx->loopstart<<PITCHSHIFT;
else if (chan->flags & CF_FORCELOOP)
chan->pos = 0;
else //we don't want to play anything more.
break;
if (!queuedbufs)
{ //queue 0.1 secs if we're starting/resetting a new stream this is to try to cover up discintinuities caused by packetloss or whatever
sfxcache_t silence;
silence.speed = snd_speed;
silence.width = 2;
silence.numchannels = 1;
silence.data = NULL;
silence.length = 0.1 * silence.speed;
silence.soundoffset = 0;
OpenAL_LoadCache(&buf, &silence, 1);
palSourceQueueBuffers(src, 1, &buf);
queuedbufs++;
}
}
}
if (!queuedbufs)
{
palGetSourcei(src, AL_SOURCE_STATE, &buf);
if (buf != AL_PLAYING)
{
chan->sfx = NULL;
if (sfx->decoder.ended)
{
if (!S_IsPlayingSomewhere(sfx))
sfx->decoder.ended(sfx);
}
return;
}
}
}
else
{
@ -771,15 +819,20 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned
}
palSourcef(src, AL_GAIN, min(cvolume, 1)); //openal only supports a max volume of 1. anything above is an error and will be clamped.
if ((chan->flags & CF_NOSPACIALISE) || (chan->entnum && chan->entnum == oali->ListenEnt) || !chan->dist_mult)
srcrel = (chan->flags & CF_NOSPACIALISE) || (chan->entnum && chan->entnum == oali->ListenEnt) || !chan->dist_mult;
if (srcrel)
{
palSourcefv(src, AL_POSITION, vec3_origin);
#ifndef FTE_TARGET_WEB //webaudio sucks.
palSourcefv(src, AL_VELOCITY, vec3_origin);
#endif
}
else
{
palSourcefv(src, AL_POSITION, chan->origin);
#ifndef FTE_TARGET_WEB //webaudio sucks.
palSourcefv(src, AL_VELOCITY, chan->velocity);
#endif
}
if (schanged)
@ -804,8 +857,8 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned
}
#endif
palSourcei(src, AL_LOOPING, (chan->flags & CF_FORCELOOP)?AL_TRUE:AL_FALSE);
if ((chan->flags & CF_NOSPACIALISE) || chan->entnum == oali->ListenEnt || !chan->dist_mult)
palSourcei(src, AL_LOOPING, (!stream && (chan->flags & CF_FORCELOOP))?AL_TRUE:AL_FALSE);
if (srcrel)
{
palSourcei(src, AL_SOURCE_RELATIVE, AL_TRUE);
// palSourcef(src, AL_ROLLOFF_FACTOR, 0.0f);
@ -819,22 +872,46 @@ static void OpenAL_ChannelUpdate(soundcardinfo_t *sc, channel_t *chan, unsigned
//this is disgustingly shit.
//logically we want to set the distance divisor to 1 and the rolloff factor to dist_mult.
//but openal clamps in an annoying order (probably to keep things signed in hardware) and webaudio refuses infinity, so we need to special case no attenuation to get around the issue
if (!chan->dist_mult)
if (srcrel)
{
palSourcef(src, AL_ROLLOFF_FACTOR, 0);
palSourcef(src, AL_REFERENCE_DISTANCE, 0);
palSourcef(src, AL_MAX_DISTANCE, 1); //doesn't matter, so long as its not a nan
#ifdef FTE_TARGET_WEB
switch(0) //emscripten omits it, and this is webaudio's default too.
#else
switch(s_al_distancemodel.ival)
#endif
{
default:
case 0:
case 1:
palSourcef(src, AL_ROLLOFF_FACTOR, 0);
palSourcef(src, AL_REFERENCE_DISTANCE, 1); //0 would be silent, or a division by 0
palSourcef(src, AL_MAX_DISTANCE, 1); //only used for clamped mode
break;
case 2:
case 3:
palSourcef(src, AL_ROLLOFF_FACTOR, 0);
palSourcef(src, AL_REFERENCE_DISTANCE, 0); //doesn't matter when rolloff is 0
palSourcef(src, AL_MAX_DISTANCE, 1); //doesn't matter, so long as its not a nan
break;
}
}
else
{
#ifdef FTE_TARGET_WEB
switch(2) //emscripten hardcodes it.
#else
switch(s_al_distancemodel.ival)
#endif
{
default:
case 0:
case 1:
palSourcef(src, AL_ROLLOFF_FACTOR, s_al_reference_distance.value);
palSourcef(src, AL_REFERENCE_DISTANCE, 1);
palSourcef(src, AL_MAX_DISTANCE, 1/chan->dist_mult); //clamp to the maximum distance you'd normally be allowed to hear... this is probably going to be annoying.
break;
case 2: //linear, mimic quake.
case 3: //linear clamped to further than ref distance
palSourcef(src, AL_ROLLOFF_FACTOR, 1);
palSourcef(src, AL_REFERENCE_DISTANCE, 0);
palSourcef(src, AL_MAX_DISTANCE, 1/chan->dist_mult);
@ -974,7 +1051,9 @@ static qboolean OpenAL_Init(soundcardinfo_t *sc, const char *devname)
PrintALError("alGensources for normal sources");
palListenerfv(AL_POSITION, oali->ListenPos);
#ifndef FTE_TARGET_WEB //webaudio sucks.
palListenerfv(AL_VELOCITY, oali->ListenVel);
#endif
palListenerfv(AL_ORIENTATION, oali->ListenOri);
return true;
@ -1007,24 +1086,38 @@ static void QDECL OnChangeALSettings (cvar_t *var, char *value)
switch (s_al_distancemodel.ival)
{
case 0:
//gain = AL_REFERENCE_DISTANCE / (AL_REFERENCE_DISTANCE + AL_ROLLOFF_FACTOR * (distance AL_REFERENCE_DISTANCE) )
palDistanceModel(AL_INVERSE_DISTANCE);
break;
case 1:
case 1: //openal's default mode
//istance = max(distance,AL_REFERENCE_DISTANCE);
//distance = min(distance,AL_MAX_DISTANCE);
//gain = AL_REFERENCE_DISTANCE / (AL_REFERENCE_DISTANCE + AL_ROLLOFF_FACTOR * (distance AL_REFERENCE_DISTANCE) )
palDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
break;
case 2:
case 2: //most quake-like
//distance = min(distance, AL_MAX_DISTANCE) // avoid negative gain
//gain = ( 1 AL_ROLLOFF_FACTOR * (distance AL_REFERENCE_DISTANCE) / (AL_MAX_DISTANCE AL_REFERENCE_DISTANCE) )
palDistanceModel(AL_LINEAR_DISTANCE);
break;
case 3:
//distance = max(distance, AL_REFERENCE_DISTANCE)
//distance = min(distance, AL_MAX_DISTANCE)
//gain = ( 1 AL_ROLLOFF_FACTOR * (distance AL_REFERENCE_DISTANCE) / (AL_MAX_DISTANCE AL_REFERENCE_DISTANCE) )
palDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
break;
case 4:
//gain = (distance / AL_REFERENCE_DISTANCE) ^ (- AL_ROLLOFF_FACTOR)
palDistanceModel(AL_EXPONENT_DISTANCE);
break;
case 5:
//distance = max(distance, AL_REFERENCE_DISTANCE)
//distance = min(distance, AL_MAX_DISTANCE)
//gain = (distance / AL_REFERENCE_DISTANCE) ^ (- AL_ROLLOFF_FACTOR)
palDistanceModel(AL_EXPONENT_DISTANCE_CLAMPED);
break;
case 6:
//gain = 1
palDistanceModel(AL_NONE);
break;
default:

View File

@ -36,7 +36,6 @@ static void S_StopAllSounds_f (void);
static void S_UpdateCard(soundcardinfo_t *sc);
static void S_ClearBuffer (soundcardinfo_t *sc);
sfx_t *S_FindName (const char *name, qboolean create);
// =======================================================================
// Internal sound data & structures
@ -147,13 +146,13 @@ cvar_t snd_voip_vad_delay = CVARD("cl_voip_vad_delay", "0.3", "Keeps sending vo
cvar_t snd_voip_capturingvol = CVARAFD("cl_voip_capturingvol", "0.5", NULL, CVAR_ARCHIVE, "Volume multiplier applied while capturing, to avoid your audio from being heard by others. Does not affect game volume when other speak (minimum of cl_voip_capturingvol and cl_voip_ducking is used).");
cvar_t snd_voip_showmeter = CVARAFD("cl_voip_showmeter", "1", NULL, CVAR_ARCHIVE, "Shows your speech volume above the standard hud. 0=hide, 1=show when transmitting, 2=ignore voice-activation disable");
cvar_t snd_voip_play = CVARAFDC("cl_voip_play", "1", NULL, CVAR_ARCHIVE, "Enables voip playback. Value is a volume scaler.", S_Voip_Play_Callback);
cvar_t snd_voip_play = CVARAFCD("cl_voip_play", "1", NULL, CVAR_ARCHIVE, S_Voip_Play_Callback, "Enables voip playback. Value is a volume scaler.");
cvar_t snd_voip_ducking = CVARAFD("cl_voip_ducking", "0.5", NULL, CVAR_ARCHIVE, "Scales game audio by this much when someone is talking to you. Does not affect your speaker volume when you speak (minimum of cl_voip_capturingvol and cl_voip_ducking is used).");
cvar_t snd_voip_micamp = CVARAFDC("cl_voip_micamp", "2", NULL, CVAR_ARCHIVE, "Amplifies your microphone when using voip.", 0);
cvar_t snd_voip_codec = CVARAFDC("cl_voip_codec", "", NULL, CVAR_ARCHIVE, "0: speex(@11khz). 1: raw. 2: opus. 3: speex(@8khz). 4: speex(@16). 5:speex(@32).", 0);
cvar_t snd_voip_noisefilter = CVARAFDC("cl_voip_noisefilter", "1", NULL, CVAR_ARCHIVE, "Enable the use of the noise cancelation filter.", 0);
cvar_t snd_voip_autogain = CVARAFDC("cl_voip_autogain", "0", NULL, CVAR_ARCHIVE, "Attempts to normalize your voice levels to a standard level. Useful for lazy people, but interferes with voice activation levels.", 0);
cvar_t snd_voip_opus_bitrate = CVARAFDC("cl_voip_opus_bitrate", "3000", NULL, CVAR_ARCHIVE, "For codecs with non-specific bitrates, this specifies the target bitrate to use.", 0);
cvar_t snd_voip_micamp = CVARAFD("cl_voip_micamp", "2", NULL, CVAR_ARCHIVE, "Amplifies your microphone when using voip.");
cvar_t snd_voip_codec = CVARAFD("cl_voip_codec", "", NULL, CVAR_ARCHIVE, "0: speex(@11khz). 1: raw. 2: opus. 3: speex(@8khz). 4: speex(@16). 5:speex(@32).");
cvar_t snd_voip_noisefilter = CVARAFD("cl_voip_noisefilter", "1", NULL, CVAR_ARCHIVE, "Enable the use of the noise cancelation filter.");
cvar_t snd_voip_autogain = CVARAFD("cl_voip_autogain", "0", NULL, CVAR_ARCHIVE, "Attempts to normalize your voice levels to a standard level. Useful for lazy people, but interferes with voice activation levels.");
cvar_t snd_voip_opus_bitrate = CVARAFD("cl_voip_opus_bitrate", "3000", NULL, CVAR_ARCHIVE, "For codecs with non-specific bitrates, this specifies the target bitrate to use.");
#endif
extern vfsfile_t *rawwritefile;
@ -1853,7 +1852,7 @@ void S_DoRestart (qboolean onlyifneeded)
{
if (!cl.sound_name[i][0])
break;
cl.sound_precache[i] = S_FindName (cl.sound_name[i], true);
cl.sound_precache[i] = S_FindName (cl.sound_name[i], true, false);
}
}
@ -2121,7 +2120,7 @@ S_FindName
also touches it
==================
*/
sfx_t *S_FindName (const char *name, qboolean create)
sfx_t *S_FindName (const char *name, qboolean create, qboolean syspath)
{
int i;
sfx_t *sfx;
@ -2134,7 +2133,7 @@ sfx_t *S_FindName (const char *name, qboolean create)
// see if already loaded
for (i=0 ; i < num_sfx ; i++)
if (!Q_strcmp(known_sfx[i].name, name))
if (!Q_strcmp(known_sfx[i].name, name) && known_sfx[i].syspath == syspath)
{
known_sfx[i].touched = true;
return &known_sfx[i];
@ -2147,7 +2146,8 @@ sfx_t *S_FindName (const char *name, qboolean create)
{
sfx = &known_sfx[i];
strcpy (sfx->name, name);
known_sfx[i].touched = true;
sfx->syspath = syspath;
sfx->touched = true;
num_sfx++;
}
@ -2240,7 +2240,7 @@ void S_TouchSound (char *name)
if (!sound_started)
return;
S_FindName (name, true);
S_FindName (name, true, false);
}
/*
@ -2249,14 +2249,14 @@ S_PrecacheSound
==================
*/
sfx_t *S_PrecacheSound (const char *name)
sfx_t *S_PrecacheSound2 (const char *name, qboolean syspath)
{
sfx_t *sfx;
if (nosound.ival || !known_sfx || !*name)
return NULL;
sfx = S_FindName (name, true);
sfx = S_FindName (name, true, syspath);
// cache it in
if (precache.ival && sndcardinfo)
@ -2708,7 +2708,7 @@ void S_StartSound(int entnum, int entchannel, sfx_t *sfx, vec3_t origin, vec3_t
S_UnlockMixer();
}
qboolean S_GetMusicInfo(int musicchannel, float *time, float *duration)
qboolean S_GetMusicInfo(int musicchannel, float *time, float *duration, char *title, size_t titlesize)
{
qboolean result = false;
soundcardinfo_t *sc;
@ -2716,6 +2716,9 @@ qboolean S_GetMusicInfo(int musicchannel, float *time, float *duration)
*time = 0;
*duration = 0;
if (titlesize)
*title = 0;
musicchannel += MUSIC_FIRST;
S_LockMixer();
@ -2724,18 +2727,23 @@ qboolean S_GetMusicInfo(int musicchannel, float *time, float *duration)
sfx = sc->channel[musicchannel].sfx;
if (sfx)
{
if (sfx->loadstate == SLS_LOADED && sfx->decoder.querydata)
*duration = sfx->decoder.querydata(sfx, NULL);
else if (sfx->decoder.buf)
Q_strncpyz(title, COM_SkipPath(sfx->name), titlesize);
if (sfx->loadstate == SLS_LOADED)
{
sfxcache_t *c = sfx->decoder.buf;
*duration = (float)c->length / c->speed;
}
else
*duration = 0;
if (sfx->decoder.querydata)
*duration = sfx->decoder.querydata(sfx, NULL, title, titlesize);
else if (sfx->decoder.buf)
{
sfxcache_t *c = sfx->decoder.buf;
*duration = (float)c->length / c->speed;
}
else
*duration = 0;
*time = (sc->channel[musicchannel].pos>>PITCHSHIFT) / (float)snd_speed; //the time into the sound, ignoring play rate.
result = true;
//FIXME: openal doesn't report the actual time.
*time = (sc->channel[musicchannel].pos>>PITCHSHIFT) / (float)snd_speed; //the time into the sound, ignoring play rate.
result = true;
}
}
}
S_UnlockMixer();
@ -3024,25 +3032,18 @@ void S_UpdateAmbientSounds (soundcardinfo_t *sc)
if (!chan->sfx)
{
float time = 0;
char *nexttrack = Media_NextTrack(i-MUSIC_FIRST, &time);
sfx_t *newmusic;
if (nexttrack && *nexttrack)
sfx_t *newmusic = Media_NextTrack(i-MUSIC_FIRST, &time);
if (newmusic && newmusic->loadstate != SLS_FAILED)
{
newmusic = S_PrecacheSound(nexttrack);
chan->sfx = newmusic;
chan->rate = 1<<PITCHSHIFT;
chan->pos = (int)(time * sc->sn.speed) * chan->rate;
changed = true;
if (newmusic && newmusic->loadstate != SLS_FAILED)
{
chan->sfx = newmusic;
chan->rate = 1<<PITCHSHIFT;
chan->pos = (int)(time * sc->sn.speed) * chan->rate;
changed = true;
chan->master_vol = bound(0, 1, 255);
chan->vol[0] = chan->vol[1] = chan->vol[2] = chan->vol[3] = chan->vol[4] = chan->vol[5] = chan->master_vol;
if (sc->ChannelUpdate)
sc->ChannelUpdate(sc, chan, changed);
}
chan->master_vol = bound(0, 1, 255);
chan->vol[0] = chan->vol[1] = chan->vol[2] = chan->vol[3] = chan->vol[4] = chan->vol[5] = chan->master_vol;
if (sc->ChannelUpdate)
sc->ChannelUpdate(sc, chan, changed);
}
}
if (chan->sfx)
@ -3664,7 +3665,7 @@ void S_SoundList_f(void)
else if (sfx->decoder.decodedata)
{
if (sfx->decoder.querydata)
sc = (sfx->decoder.querydata(sfx, &scachebuf) < 0)?NULL:&scachebuf;
sc = (sfx->decoder.querydata(sfx, &scachebuf, NULL, 0) < 0)?NULL:&scachebuf;
else
sc = NULL; //don't bother trying to actually decode anything here.
if (!sc)
@ -3683,7 +3684,7 @@ void S_SoundList_f(void)
size = (sc->soundoffset+sc->length)*sc->width*(sc->numchannels);
duration = (sc->soundoffset+sc->length) / sc->speed;
total += size;
if (sc->loopstart >= 0)
if (sfx->loopstart >= 0)
Con_Printf ("L");
else
Con_Printf (" ");
@ -3748,14 +3749,13 @@ void S_ClearRaw(void)
}
//returns an sfxcache_t stating where the data is
sfxcache_t *S_Raw_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int length)
sfxcache_t *QDECL S_Raw_Locate(sfx_t *sfx, sfxcache_t *buf, ssamplepos_t start, int length)
{
streaming_t *s = sfx->decoder.buf;
if (buf)
{
buf->data = s->data;
buf->length = s->length;
buf->loopstart = -1;
buf->numchannels = s->numchannels;
buf->soundoffset = 0;
buf->speed = snd_speed;

View File

@ -587,9 +587,9 @@ static qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, int inwidth
sc->soundoffset = 0;
sc->data = (qbyte*)(sc+1);
if (inloopstart == -1)
sc->loopstart = inloopstart;
sfx->loopstart = inloopstart;
else
sc->loopstart = inloopstart * scale;
sfx->loopstart = inloopstart * scale;
SND_ResampleStream (data,
inrate,
@ -612,7 +612,7 @@ static qboolean ResampleSfx (sfx_t *sfx, int inrate, int inchannels, int inwidth
#define DSPK_EXP 0.0433
/*
sfxcache_t *S_LoadDoomSpeakerSound (sfx_t *s, qbyte *data, int datalen, int sndspeed)
qboolean QDECL S_LoadDoomSpeakerSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed)
{
sfxcache_t *sc;
@ -688,7 +688,7 @@ sfxcache_t *S_LoadDoomSpeakerSound (sfx_t *s, qbyte *data, int datalen, int snds
return sc;
}
*/
static qboolean S_LoadDoomSound (sfx_t *s, qbyte *data, int datalen, int sndspeed)
static qboolean QDECL S_LoadDoomSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed)
{
// format data from Unofficial Doom Specs v1.6
unsigned short *dataus;
@ -717,7 +717,7 @@ static qboolean S_LoadDoomSound (sfx_t *s, qbyte *data, int datalen, int sndspee
}
#endif
static qboolean S_LoadWavSound (sfx_t *s, qbyte *data, int datalen, int sndspeed)
static qboolean QDECL S_LoadWavSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed)
{
wavinfo_t info;
@ -740,11 +740,11 @@ static qboolean S_LoadWavSound (sfx_t *s, qbyte *data, int datalen, int sndspeed
return ResampleSfx (s, info.rate, info.numchannels, info.width, info.samples, info.loopstart, data + info.dataofs);
}
qboolean S_LoadOVSound (sfx_t *s, qbyte *data, int datalen, int sndspeed);
qboolean QDECL S_LoadOVSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed);
#ifdef FTE_TARGET_WEB
//web browsers contain their own decoding libraries that our openal stuff can use.
static qboolean S_LoadBrowserFile (sfx_t *s, qbyte *data, int datalen, int sndspeed)
static qboolean QDECL S_LoadBrowserFile (sfx_t *s, qbyte *data, size_t datalen, int sndspeed)
{
sfxcache_t *sc;
s->decoder.buf = sc = BZ_Malloc(sizeof(sfxcache_t) + datalen);
@ -812,23 +812,12 @@ static void S_LoadSoundWorker (void *ctx, void *ctxdata, size_t a, size_t b)
char *name = s->name;
size_t filesize;
if (name[1] == ':' && name[2] == '\\')
s->loopstart = -1;
if (s->syspath)
{
vfsfile_t *f;
#ifndef _WIN32 //convert from windows to a suitable alternative.
char unixname[128];
Q_snprintfz(unixname, sizeof(unixname), "/mnt/%c/%s", name[0]-'A'+'a', name+3);
name = unixname;
while (*name)
{
if (*name == '\\')
*name = '/';
name++;
}
name = unixname;
#endif
if ((f = VFSOS_Open(name, "rb")))
{
filesize = VFS_GETLEN(f);

View File

@ -166,11 +166,11 @@ void S_PaintChannels(soundcardinfo_t *sc, int endtime)
{
scache = s->decoder.buf;
ch->pos += (end-sc->paintedtime)*ch->rate;
if (ch->pos > scache->length)
if ((ch->pos>>PITCHSHIFT) > scache->length)
{
ch->pos = 0;
if (scache->loopstart != -1)
ch->pos = scache->loopstart<<PITCHSHIFT;
if (s->loopstart != -1)
ch->pos = s->loopstart<<PITCHSHIFT;
else if (!(ch->flags & CF_FORCELOOP))
{
ch->sfx = NULL;
@ -188,31 +188,71 @@ void S_PaintChannels(soundcardinfo_t *sc, int endtime)
ltime = sc->paintedtime;
while (ltime < end)
{
ssamplepos_t spos = ch->pos>>PITCHSHIFT;
if (s->decoder.decodedata)
scache = s->decoder.decodedata(s, &scachebuf, ch->pos>>PITCHSHIFT, 1 + (((end - ltime) * ch->rate)>>PITCHSHIFT)); /*1 for luck - balances audio termination below*/
else
scache = s->decoder.buf;
if (!scache)
scache = s->decoder.decodedata(s, &scachebuf, spos, 1 + (((end - ltime) * ch->rate)>>PITCHSHIFT)); /*1 for luck - balances audio termination below*/
else if (s->decoder.buf)
{
ch->sfx = NULL;
break;
scache = s->decoder.buf;
if (spos >= scache->length)
scache = NULL; //EOF
}
else
scache = NULL;
if (!scache)
{ //hit eof, loop it or stop it
if (s->loopstart != -1) /*some wavs contain a loop offset directly in the sound file, such samples loop even if a non-looping builtin was used*/
{
ch->pos &= ~((-1)<<PITCHSHIFT); /*clear out all but the subsample offset*/
ch->pos += s->loopstart<<PITCHSHIFT; //ignore the offset if its off the end of the file
}
else if (ch->flags & CF_FORCELOOP) /*(static)channels which are explicitly looping always loop from the start*/
{
/*restart it*/
ch->pos = 0;
}
else
{ // channel just stopped
ch->sfx = NULL;
if (s->decoder.ended)
{
if (!S_IsPlayingSomewhere(s))
s->decoder.ended(s);
}
break;
}
//retry at that new offset (continue here could give an infinite loop)
spos = ch->pos>>PITCHSHIFT;
if (s->decoder.decodedata)
scache = s->decoder.decodedata(s, &scachebuf, spos, 1 + (((end - ltime) * ch->rate)>>PITCHSHIFT)); /*1 for luck - balances audio termination below*/
else if (s->decoder.buf)
{
scache = s->decoder.buf;
if (spos >= scache->length)
scache = NULL; //EOF
}
else
scache = NULL;
if (!scache)
break;
}
// find how many samples till the sample ends (clamp max length)
avail = scache->length;
if (avail > maxlen)
avail = snd_speed*10;
avail = (((int)(scache->soundoffset + avail)<<PITCHSHIFT) - ch->pos) / ch->rate;
if (spos < scache->soundoffset || spos > scache->soundoffset+scache->length)
avail = 0; //urm, we would be trying to read outside of the buffer. let mixing slip when there's no data available yet.
else
{
// find how many samples till the sample ends (clamp max length)
avail = scache->length;
if (avail > maxlen)
avail = snd_speed*10;
avail = (((int)(scache->soundoffset + avail)<<PITCHSHIFT) - ch->pos) / ch->rate;
}
// mix the smaller of how much is available or the time left
count = min(avail, end - ltime);
if (avail < 0)
{
Sys_Printf("sound already past end of buffer\n");
avail = 0;
count = 0;
}
if (count > 0)
{
if (ch->pos < 0) //sounds with a pos of 0 are delay-start sounds
@ -254,37 +294,8 @@ void S_PaintChannels(soundcardinfo_t *sc, int endtime)
ltime += count;
ch->pos += ch->rate * count;
}
if (count == avail)
{
if (scache->loopstart != -1) /*some wavs contain a loop offset directly in the sound file, such samples loop even if a non-looping builtin was used*/
{
if (scache->length <= scache->loopstart)
break;
ch->pos &= ~((-1)<<PITCHSHIFT); /*clear out all but the subsample offset*/
ch->pos += scache->loopstart<<PITCHSHIFT;
if (!scache->length)
{
scache->loopstart=-1;
break;
}
}
else if ((ch->flags & CF_FORCELOOP) && scache->length) /*(static)channels which are explicitly looping always loop from the start*/
{
/*restart it*/
ch->pos = 0;
}
else
{ // channel just stopped
ch->sfx = NULL;
if (s->decoder.ended)
{
if (!S_IsPlayingSomewhere(s))
s->decoder.ended(s);
}
break;
}
}
else
break;
}
}

View File

@ -77,12 +77,12 @@ typedef struct {
sfx_t *s;
} ovdecoderbuffer_t;
float OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf);
static sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssamplepos_t start, int length);
static void OV_CancelDecoder(sfx_t *s);
float QDECL OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf, char *name, size_t namesize);
static sfxcache_t *QDECL OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssamplepos_t start, int length);
static void QDECL OV_CancelDecoder(sfx_t *s);
static qboolean OV_StartDecode(unsigned char *start, unsigned long length, ovdecoderbuffer_t *buffer);
qboolean S_LoadOVSound (sfx_t *s, qbyte *data, int datalen, int sndspeed)
qboolean QDECL S_LoadOVSound (sfx_t *s, qbyte *data, size_t datalen, int sndspeed)
{
ovdecoderbuffer_t *buffer;
@ -95,6 +95,7 @@ qboolean S_LoadOVSound (sfx_t *s, qbyte *data, int datalen, int sndspeed)
buffer->decodedbytecount = 0;
buffer->s = s;
s->decoder.buf = buffer;
s->loopstart = -1;
if (!OV_StartDecode(data, datalen, buffer))
{
@ -121,7 +122,7 @@ qboolean S_LoadOVSound (sfx_t *s, qbyte *data, int datalen, int sndspeed)
return true;
}
float OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf)
float QDECL OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf, char *name, size_t namesize)
{
ovdecoderbuffer_t *dec = sfx->decoder.buf;
if (!dec)
@ -131,15 +132,33 @@ float OV_Query(struct sfx_s *sfx, struct sfxcache_s *buf)
buf->data = NULL; //you're not meant to actually be using the data here
buf->soundoffset = 0;
buf->length = p_ov_pcm_total(&dec->vf, -1);
buf->loopstart = -1;
buf->numchannels = dec->srcchannels;
buf->speed = dec->srcspeed;
buf->width = 2;
}
if (name)
{
vorbis_comment *c = p_ov_comment(&dec->vf, -1);
int i;
const char *artist = NULL;
const char *title = NULL;
for (i = 0; i < c->comments; i++)
{
if (!strncmp(c->user_comments[i], "ARTIST=", 7))
artist = c->user_comments[i]+7;
else if (!strncmp(c->user_comments[i], "TITLE=", 6))
title = c->user_comments[i]+6;
}
if (artist && title)
Q_snprintfz(name, namesize, "%s - %s", artist, title);
else if (title)
Q_snprintfz(name, namesize, "%s", title);
}
return p_ov_time_total(&dec->vf, -1);
}
static sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssamplepos_t start, int length)
static sfxcache_t *QDECL OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssamplepos_t start, int length)
{
extern int snd_speed;
extern cvar_t snd_linearresample_stream;
@ -161,13 +180,13 @@ static sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssam
// Con_Printf("Rewound to %i\n", start);
dec->failed = false;
/*something rewound, purge clear the buffer*/
dec->decodedbytecount = 0;
dec->decodedbytestart = start;
//check pos
//fixme: seeking might not be supported
p_ov_pcm_seek(&dec->vf, (dec->decodedbytestart * dec->srcspeed) / outspeed);
if (p_ov_pcm_seek(&dec->vf, start * (dec->srcspeed/(2.0*dec->srcchannels*outspeed))) == 0)
{
/*something rewound, purge clear the buffer*/
dec->decodedbytecount = 0;
dec->decodedbytestart = start;
}
}
/* if (start > dec->decodedbytestart + dec->decodedbytecount)
@ -191,8 +210,11 @@ static sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssam
}
else if (trim > dec->decodedbytecount)
{
dec->decodedbytecount = 0;
dec->decodedbytestart = start;
if (0==p_ov_pcm_seek(&dec->vf, start * (dec->srcspeed/(2.0*dec->srcchannels*outspeed))))
{
dec->decodedbytecount = 0;
dec->decodedbytestart = start;
}
// Con_Printf("trim > count\n");
}
else
@ -228,6 +250,8 @@ static sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssam
Con_Printf("ogg decoding failed\n");
break;
}
if (start >= dec->decodedbytestart+dec->decodedbytecount)
return NULL; //let the mixer know that we hit the end
break;
}
}
@ -253,6 +277,8 @@ static sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssam
Con_Printf("ogg decoding failed\n");
return NULL;
}
if (start >= dec->decodedbytestart+dec->decodedbytecount)
return NULL; //let the mixer know that we hit the end
break;
}
@ -278,7 +304,6 @@ static sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssam
buf->data = dec->decodedbuffer;
buf->soundoffset = dec->decodedbytestart / (2 * dec->srcchannels);
buf->length = dec->decodedbytecount / (2 * dec->srcchannels);
buf->loopstart = -1;
buf->numchannels = dec->srcchannels;
buf->speed = snd_speed;
buf->width = 2;
@ -291,7 +316,7 @@ static sfxcache_t *OV_DecodeSome(struct sfx_s *sfx, struct sfxcache_s *buf, ssam
if (s->loadstate != SLS_LOADING)
s->loadstate = SLS_NOTLOADED;
}*/
static void OV_CancelDecoder(sfx_t *s)
static void QDECL OV_CancelDecoder(sfx_t *s)
{
ovdecoderbuffer_t *dec;
s->loadstate = SLS_FAILED;

View File

@ -37,10 +37,10 @@ typedef struct
} portable_samplegroup_t;
typedef struct {
struct sfxcache_s *(*decodedata) (struct sfx_s *sfx, struct sfxcache_s *buf, ssamplepos_t start, int length); //return true when done.
float (*querydata) (struct sfx_s *sfx, struct sfxcache_s *buf); //reports length + original format info without actually decoding anything.
void (*ended) (struct sfx_s *sfx); //sound stopped playing and is now silent (allow rewinding or something).
void (*purge) (struct sfx_s *sfx); //sound is being purged from memory. destroy everything.
struct sfxcache_s *(QDECL *decodedata) (struct sfx_s *sfx, struct sfxcache_s *buf, ssamplepos_t start, int length); //return true when done.
float (QDECL *querydata) (struct sfx_s *sfx, struct sfxcache_s *buf, char *title, size_t titlesize); //reports length + original format info without actually decoding anything.
void (QDECL *ended) (struct sfx_s *sfx); //sound stopped playing and is now silent (allow rewinding or something).
void (QDECL *purge) (struct sfx_s *sfx); //sound is being purged from memory. destroy everything.
void *buf;
} sfxdecode_t;
@ -58,6 +58,9 @@ typedef struct sfx_s
int loadstate; //no more super-spammy
qboolean touched:1; //if the sound is still relevent
qboolean syspath:1; //if the sound is still relevent
int loopstart; //-1 or sample index to begin looping at once the sample ends
#ifdef AVAIL_OPENAL
unsigned int openal_buffer;
@ -68,7 +71,6 @@ typedef struct sfx_s
typedef struct sfxcache_s
{
usamplepos_t length; //sample count
int loopstart; //-1 or sample index to begin looping at once the sample ends
unsigned int speed;
unsigned int width;
unsigned int numchannels;
@ -90,17 +92,32 @@ typedef struct
unsigned char *buffer; // pointer to mixed pcm buffer (not directly used by mixer)
} dma_t;
#define CF_RELIABLE 1 // serverside only. yeah, evil. screw you.
//client and server
//CF_RELIABLE 1
#define CF_FORCELOOP 2 // forces looping. set on static sounds.
#define CF_NOSPACIALISE 4 // these sounds are played at a fixed volume in both speakers, but still gets quieter with distance.
//#define CF_PAUSED 8 // rate = 0. or something.
#define CF_ABSVOLUME 16 // ignores volume cvar.
//CF_ABSVOLUME
#define CF_NOREVERB 32 // disables reverb on this channel, if possible.
#define CF_FOLLOW 64 // follows the owning entity (stops moving if we lose track)
//#define CF_RESERVEDN 128 // reserved for things that should be networked.
//client only
#define CF_ABSVOLUME 16 // ignores volume cvar.
//client-internal
#define CF_AUTOSOUND 1024 // generated from q2 entities, which avoids breaking regular sounds, using it outside the sound system will probably break things.
//server only
#define CF_RELIABLE 1 // serverside only. yeah, evil. screw you.
#define CF_UNICAST 256 // serverside only. the sound is sent to msg_entity only.
#define CF_SENDVELOCITY 512 // serverside hint that velocity is important
#define CF_AUTOSOUND 1024 // generated from q2 entities, which avoids breaking regular sounds, using it outside the sound system will probably break things.
//#define CF_UNUSED 2048
//#define CF_UNUSED 4096
//#define CF_UNUSED 8192
//#define CF_UNUSED 16384
//#define CF_UNUSED 32768
#define CF_NETWORKED (CF_NOSPACIALISE|CF_NOREVERB|CF_FORCELOOP|CF_FOLLOW/*|CF_RESERVEDN*/)
typedef struct
{
@ -186,13 +203,14 @@ qboolean S_HaveOutput(void);
void S_Music_Clear(sfx_t *onlyifsample);
void S_Music_Seek(float time);
qboolean S_GetMusicInfo(int musicchannel, float *time, float *duration);
qboolean S_GetMusicInfo(int musicchannel, float *time, float *duration, char *title, size_t titlesize);
qboolean S_Music_Playing(int musicchannel);
float Media_CrossFade(int musicchanel, float vol, float time); //queries the volume we're meant to be playing (checks for fade out). -1 for no more, otherwise returns vol.
char *Media_NextTrack(int musicchanel, float *time); //queries the track we're meant to be playing now.
sfx_t *Media_NextTrack(int musicchanel, float *time); //queries the track we're meant to be playing now.
sfx_t *S_FindName (const char *name, qboolean create);
sfx_t *S_PrecacheSound (const char *sample);
sfx_t *S_FindName (const char *name, qboolean create, qboolean syspath);
sfx_t *S_PrecacheSound2 (const char *sample, qboolean syspath);
#define S_PrecacheSound(s) S_PrecacheSound2(s,false)
void S_TouchSound (char *sample);
void S_UntouchAll(void);
void S_ClearPrecache (void);
@ -293,7 +311,7 @@ void S_LocalSound (const char *s);
void S_LocalSound2 (const char *sound, int channel, float volume);
qboolean S_LoadSound (sfx_t *s);
typedef qboolean (*S_LoadSound_t) (sfx_t *s, qbyte *data, int datalen, int sndspeed);
typedef qboolean (QDECL *S_LoadSound_t) (sfx_t *s, qbyte *data, size_t datalen, int sndspeed);
qboolean S_RegisterSoundInputPlugin(S_LoadSound_t loadfnc); //called to register additional sound input plugins
void S_AmbientOff (void);

View File

@ -432,15 +432,23 @@ char *Sys_ConsoleInput (void)
{
return NULL;
}
void Sys_mkdir (char *path) //not all pre-unix systems have directories (including dos 1)
void Sys_mkdir (const char *path) //not all pre-unix systems have directories (including dos 1)
{
mkdir(path, 0777);
mkdir(path, 0755);
}
qboolean Sys_remove (char *path)
qboolean Sys_rmdir (const char *path)
{
if (rmdir (path) == 0)
return true;
if (errno == ENOENT)
return true;
return false;
}
qboolean Sys_remove (const char *path)
{
return !unlink(path);
}
qboolean Sys_Rename (char *oldfname, char *newfname)
qboolean Sys_Rename (const char *oldfname, const char *newfname)
{
return !rename(oldfname, newfname);
}

View File

@ -446,16 +446,23 @@ int Sys_FileTime (char *path)
}
void Sys_mkdir (char *path)
void Sys_mkdir (const char *path)
{
mkdir (path, 0777);
}
qboolean Sys_remove (char *path)
qboolean Sys_rmdir (const char *path)
{
if (rmdir (path) == 0)
return true;
if (errno == ENOENT)
return true;
return false;
}
qboolean Sys_remove (const char *path)
{
return system(va("rm \"%s\"", path));
}
qboolean Sys_Rename (char *oldfname, char *newfname)
qboolean Sys_Rename (const char *oldfname, const char *newfname)
{
return !rename(oldfname, newfname);
}

View File

@ -269,7 +269,7 @@ int Sys_EnumerateFiles(const char *gpath, const char *match, int (*func)(const c
return ret;
}
void Sys_mkdir(char *path)
void Sys_mkdir(const char *path)
{
BPTR lock;
@ -283,14 +283,19 @@ void Sys_mkdir(char *path)
}
}
qboolean Sys_remove(char *path)
qboolean Sys_rmdir (const char *path)
{
return false;
}
qboolean Sys_remove(const char *path)
{
if (path[0] == '.' && path[1] == '/')
path+= 2;
return DeleteFile(path);
}
qboolean Sys_Rename (char *oldfname, char *newfname)
qboolean Sys_Rename (const char *oldfname, const char *newfname)
{
return !rename(oldfname, newfname);
}

View File

@ -1,3 +1,7 @@
//as of firefox 52(march 2017), firefox no longer supports npapi (except for flash, the buggiest of them all, stupid stupid)
//chrome 42 disabled support by default, and completely removed by 45 (sept 2015).
//no browser with > 1% market share can actually use this.
#include "quakedef.h"
#include "winquake.h"
#define bool int //we ain't c++ (grr microsoft stdbool.h gief!)

View File

@ -666,12 +666,12 @@ qboolean Update_GetHomeDirectory(char *homedir, int homedirsize)
return false;
}
void Sys_mkdir (char *path)
void Sys_mkdir (const char *path)
{
CreateDirectory (path, NULL);
}
#else
void Sys_mkdir (char *path)
void Sys_mkdir (const char *path)
{
mkdir (path, 0777);
}
@ -792,7 +792,7 @@ qboolean Plug_GetDownloadedName(char *updatedpath, int updatedpathlen)
if (enginedownloadactive)
{
enginedownloadactive->user_ctx = NULL;
DL_CreateThread(enginedownloadactive, VFSPIPE_Open(), Update_Version_Updated);
DL_CreateThread(enginedownloadactive, VFSPIPE_Open(1, false), Update_Version_Updated);
}
}
}

View File

@ -119,7 +119,7 @@ double Sys_DoubleTime (void)
}
//create a directory
void Sys_mkdir (char *path)
void Sys_mkdir (const char *path)
{
#if WIN32
_mkdir (path);
@ -129,15 +129,31 @@ void Sys_mkdir (char *path)
#endif
}
qboolean Sys_rmdir (const char *path)
{
int ret;
#if WIN32
ret = _rmdir (path);
#else
//user, group, others
ret = rmdir (path, 0755); //WARNING: DO NOT RUN AS ROOT!
#endif
if (ret == 0)
return true;
if (errno == ENOENT)
return true;
return false;
}
//unlink a file
qboolean Sys_remove (char *path)
qboolean Sys_remove (const char *path)
{
remove(path);
return true;
}
qboolean Sys_Rename (char *oldfname, char *newfname)
qboolean Sys_Rename (const char *oldfname, const char *newfname)
{
return !rename(oldfname, newfname);
}

View File

@ -126,14 +126,24 @@ void Sys_Quit (void)
exit(0);
}
void Sys_mkdir (char *path)
void Sys_mkdir (const char *path)
{
wchar_t wide[MAX_OSPATH];
widen(wide, sizeof(wide), path);
CreateDirectoryW(wide, NULL);
}
qboolean Sys_rmdir (const char *path)
{
RemoveDirectoryW(wide)
qboolean Sys_remove (char *path)
if (rmdir (path) == 0)
return true;
if (errno == ENOENT)
return true;
return false;
}
qboolean Sys_remove (const char *path)
{
wchar_t wide[MAX_OSPATH];
widen(wide, sizeof(wide), path);
@ -144,7 +154,7 @@ qboolean Sys_remove (char *path)
return false; //other errors? panic
}
qboolean Sys_Rename (char *oldfname, char *newfname)
qboolean Sys_Rename (const char *oldfname, const char *newfname)
{
wchar_t oldwide[MAX_OSPATH];
wchar_t newwide[MAX_OSPATH];
@ -389,10 +399,27 @@ dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs)
{
int i;
HMODULE lib;
DWORD err;
lib = LoadLibraryU(name);
if (!lib)
{
err = GetLastError();
switch(err)
{
case ERROR_MOD_NOT_FOUND:
break;
case ERROR_BAD_EXE_FORMAT:
Con_Printf("Error ERROR_BAD_EXE_FORMAT loading %s\n", name);
break;
case ERROR_PROC_NOT_FOUND:
Con_Printf("Error ERROR_PROC_NOT_FOUND loading %s\n", name);
break;
default:
Con_Printf("Error %u loading %s\n", err, name);
break;
}
if (!strstr(COM_SkipPath(name), ".dll"))
{ //.dll implies that it is a system dll, or something that is otherwise windows-specific already.
char libname[MAX_OSPATH];
@ -1014,7 +1041,7 @@ FILE IO
===============================================================================
*/
void Sys_mkdir (char *path)
void Sys_mkdir (const char *path)
{
if (WinNT)
{
@ -1026,7 +1053,20 @@ void Sys_mkdir (char *path)
_mkdir (path);
}
qboolean Sys_remove (char *path)
qboolean Sys_rmdir (const char *path)
{
if (WinNT)
{
wchar_t wide[MAX_OSPATH];
widen(wide, sizeof(wide), path);
return RemoveDirectoryW(wide);
}
else
return 0==_mkdir (path);
}
qboolean Sys_remove (const char *path)
{
if (WinNT)
{
@ -1055,7 +1095,7 @@ qboolean Sys_remove (char *path)
}
}
qboolean Sys_Rename (char *oldfname, char *newfname)
qboolean Sys_Rename (const char *oldfname, const char *newfname)
{
if (WinNT)
{
@ -4234,4 +4274,48 @@ void WIN_DestroyCursor(void *cursor)
{
DestroyIcon(cursor);
}
/*
static HRESULT STDMETHODCALLTYPE DD_QueryInterface(IDropTarget *This, REFIID riid, void **ppvObject) {return E_NOINTERFACE;}
static ULONG STDMETHODCALLTYPE DD_AddRef(IDropTarget *This) {return 1;}
static ULONG STDMETHODCALLTYPE DD_Release(IDropTarget *This) {return 1;}
static HRESULT STDMETHODCALLTYPE DD_DragEnter(IDropTarget *This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
*pdwEffect &= DROPEFFECT_COPY;
return S_OK;
}
static HRESULT STDMETHODCALLTYPE DD_DragOver(IDropTarget *This, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
*pdwEffect &= DROPEFFECT_COPY;
return S_OK;
}
static HRESULT STDMETHODCALLTYPE DD_DragLeave(IDropTarget *This)
{
return S_OK;
}
static HRESULT STDMETHODCALLTYPE DD_Drop(IDropTarget *This, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
*pdwEffect &= DROPEFFECT_COPY;
return S_OK;
}
static struct IDropTargetVtbl MyDropTargetVtbl =
{
DD_QueryInterface,
DD_AddRef,
DD_Release,
DD_DragEnter,
DD_DragOver,
DD_DragLeave,
DD_Drop
};
static IDropTarget MyDropTarget = {&MyDropTargetVtbl};*/
void WIN_WindowCreated(HWND window)
{
// OleInitialize(NULL);
// if (FAILED(RegisterDragDrop(window, &MyDropTarget)))
// Con_Printf("RegisterDragDrop failed\n");
DragAcceptFiles(window, TRUE);
}
#endif

View File

@ -125,14 +125,17 @@ qboolean Sys_RandomBytes(qbyte *string, int len)
}
/*filesystem stuff*/
void Sys_mkdir (char *path)
void Sys_mkdir (const char *path)
{
}
qboolean Sys_remove (char *path)
void Sys_rmdir (const char *path)
{
}
qboolean Sys_remove (const char *path)
{
return false; //false for failure
}
qboolean Sys_Rename (char *oldfname, char *newfname)
qboolean Sys_Rename (const char *oldfname, const char *newfname)
{
return false; //false for failure
}

View File

@ -122,6 +122,6 @@ void GLVID_Update (vrect_t *rects);
void GLVID_SwapBuffers(void);
enum uploadfmt;
char *GLVID_GetRGBInfo(int *truewidth, int *trueheight, enum uploadfmt *fmt);
char *GLVID_GetRGBInfo(int *bytestride, int *truewidth, int *trueheight, enum uploadfmt *fmt);
void GLVID_SetCaption(const char *caption);
#endif

View File

@ -158,10 +158,10 @@ static qboolean Headless_VID_ApplyGammaRamps (unsigned int gammarampsize, unsig
static void Headless_VID_SetWindowCaption (const char *msg)
{
}
static char *Headless_VID_GetRGBInfo (int *truevidwidth, int *truevidheight, enum uploadfmt *fmt)
static char *Headless_VID_GetRGBInfo (int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt)
{
*fmt = TF_INVALID;
*truevidwidth = *truevidheight = 0;
*bytestride = *truevidwidth = *truevidheight = 0;
return NULL;
}
static qboolean Headless_SCR_UpdateScreen (void)
@ -291,12 +291,31 @@ rendererinfo_t headlessrenderer =
static qboolean HeadlessVK_CreateSurface(void)
{
vk.surface = VK_NULL_HANDLE; //nothing to create, we're using offscreen rendering.
vk.allowsubmissionthread = false; //waste of threading
return true;
}
//static void HeadlessVK_Present(struct vkframe *theframe)
//{
static void HeadlessVK_Present(struct vkframe *theframe)
{
//VK_DoPresent(theframe);
//}
if (!theframe)
return;
vk
qglWaitVkSemaphoreNV(theframe->backbuf->presentsemaphore);
//tell the gl driver to copy it over now
qglDrawVkImageNV(theframe->backbuf->colour.image, theframe->backbuf->colour.sampler,
0, 0, vid.pixelwidth, vid.pixelheight, //xywh (window coords)
0, //z
0, 1, 1, 0); //stst (remember that gl textures are meant to be upside down)
//at this point the gl driver can signal the fence so our vk code can wake up and start drawing the next frame (if the gpu is slow)
qglSignalVkFenceNV(vk.acquirefences[vk.aquirelast%ACQUIRELIMIT]);
//and tell our code to expect it.
vk.acquirebufferidx[vk.aquirelast%ACQUIRELIMIT] = vk.aquirelast%vk.backbuf_count;
barrier
vk.aquirelast++;
}
static qboolean HeadlessVK_Init (rendererstate_t *info, unsigned char *palette)
{
extern cvar_t vid_conautoscale;

View File

@ -317,9 +317,9 @@ cshift_t cshift_lava = { {255,80,0}, 150 };
cshift_t cshift_server = { {130,80,50}, 0 };
cvar_t v_gamma = CVARFDC("gamma", "1.0", CVAR_ARCHIVE|CVAR_RENDERERCALLBACK, "Controls how bright the screen is. Setting this to anything but 1 without hardware gamma requires glsl support and can noticably harm your framerate.", V_Gamma_Callback);
cvar_t v_contrast = CVARFDC("contrast", "1.0", CVAR_ARCHIVE, "Scales colour values linearly to make your screen easier to see. Setting this to anything but 1 without hardware gamma will reduce your framerates a little.", V_Gamma_Callback);
cvar_t v_brightness = CVARFDC("brightness", "0.0", CVAR_ARCHIVE, "Brightness is how much 'white' to add to each and every pixel on the screen.", V_Gamma_Callback);
cvar_t v_gamma = CVARFCD("gamma", "1.0", CVAR_ARCHIVE|CVAR_RENDERERCALLBACK, V_Gamma_Callback, "Controls how bright the screen is. Setting this to anything but 1 without hardware gamma requires glsl support and can noticably harm your framerate.");
cvar_t v_contrast = CVARFCD("contrast", "1.0", CVAR_ARCHIVE, V_Gamma_Callback, "Scales colour values linearly to make your screen easier to see. Setting this to anything but 1 without hardware gamma will reduce your framerates a little.");
cvar_t v_brightness = CVARFCD("brightness", "0.0", CVAR_ARCHIVE, V_Gamma_Callback, "Brightness is how much 'white' to add to each and every pixel on the screen.");
qbyte gammatable[256]; // palette is sent through this

View File

@ -842,10 +842,6 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel) //actually, this should be in
Q_strncatz(wads, token, sizeof(wads)); //cache it for later (so that we don't play with any temp memory yet)
#endif
}
else if (!strcmp("skyname", key)) // for HalfLife maps
{
Q_strncpyz(cl.skyname, token, sizeof(cl.skyname));
}
else if (!strcmp("fog", key)) //q1 extension. FIXME: should be made temporary.
{
key[0] = 'f';
@ -889,6 +885,10 @@ void Mod_ParseInfoFromEntityLump(model_t *wmodel) //actually, this should be in
Cvar_LockFromServer(&r_telealpha, com_token);
Cvar_LockFromServer(&r_telestyle, "1");
}
else if (!strcmp("skyname", key)) // for HalfLife maps
{
Q_strncpyz(cl.skyname, token, sizeof(cl.skyname));
}
else if (!strcmp("sky", key)) // for Quake2 maps
{
Q_strncpyz(cl.skyname, token, sizeof(cl.skyname));

View File

@ -122,6 +122,7 @@ extern HCURSOR hArrowCursor, hCustomCursor;
void *WIN_CreateCursor(const char *filename, float hotx, float hoty, float scale);
qboolean WIN_SetCursor(void *cursor);
void WIN_DestroyCursor(void *cursor);
void WIN_WindowCreated(HWND window);
void INS_UpdateClipCursor (void);
void CenterWindow(HWND hWndCenter, int width, int height, BOOL lefttopjustify);

View File

@ -297,7 +297,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//#define VM_LUA //q1 lua gamecode interface
#define TCPCONNECT //a tcpconnect command, that allows the player to connect to tcp-encapsulated qw protocols.
#define IRCCONNECT //an ircconnect command, that allows the player to connect to irc-encapsulated qw protocols... yeah, really.
// #define IRCCONNECT //an ircconnect command, that allows the player to connect to irc-encapsulated qw protocols... yeah, really.
#define PLUGINS //qvm/dll plugins.
#define SUPPORT_ICE //Interactive Connectivity Establishment protocol, for peer-to-peer connections
@ -383,6 +383,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifdef NO_ZLIB //compile-time option.
#undef AVAIL_ZLIB
#undef AVAIL_PNGLIB
#undef AVAIL_XZDEC
#undef AVAIL_GZDEC
#endif

View File

@ -714,8 +714,13 @@ void Cmd_Exec_f (void)
if (cvar_watched)
Cbuf_InsertText (va("echo END %s", buf), level, true);
// don't execute anything if it was from server (either the stuffcmd/localcmd, or the file)
if (!strcmp(name, "default.cfg") && !(Cmd_FromGamecode() || untrusted))
Cbuf_InsertText ("\ncvar_lockdefaults 1\n", level, false);
if (!strcmp(name, "default.cfg"))
{
if (!(Cmd_FromGamecode() || untrusted))
Cbuf_InsertText ("\ncvar_lockdefaults 1\n", level, false);
if (fs_manifest->defaultoverrides)
Cbuf_InsertText (fs_manifest->defaultoverrides, level, false);
}
Cbuf_InsertText (s, level, true);
if (cvar_watched)
Cbuf_InsertText (va("echo BEGIN %s", buf), level, true);
@ -1246,15 +1251,16 @@ char *VARGS Cmd_Args (void)
return cmd_args;
}
void Cmd_Args_Set(const char *newargs)
void Cmd_Args_Set(const char *newargs, size_t len)
{
if (cmd_args_buf)
Z_Free(cmd_args_buf);
if (newargs)
{
cmd_args_buf = (char*)Z_Malloc (Q_strlen(newargs)+1);
Q_strcpy (cmd_args_buf, newargs);
cmd_args_buf = (char*)Z_Malloc (len+1);
memcpy(cmd_args_buf, newargs, len);
cmd_args_buf[len] = 0;
cmd_args = cmd_args_buf;
}
else
@ -1547,13 +1553,13 @@ Parses the given string into command line tokens, stopping at the \n
const char *Cmd_TokenizeString (const char *text, qboolean expandmacros, qboolean qctokenize)
{
int i;
const char *args = NULL;
// clear the args from the last string
for (i=0 ; i<cmd_argc ; i++)
Z_Free (cmd_argv[i]);
cmd_argc = 0;
Cmd_Args_Set(NULL);
while (1)
{
@ -1570,18 +1576,16 @@ const char *Cmd_TokenizeString (const char *text, qboolean expandmacros, qboolea
}
if (!*text)
return text;
break;
if (cmd_argc == 1)
{
Cmd_Args_Set(text);
}
args = text;
text = COM_StringParse (text, com_token, sizeof(com_token), expandmacros, qctokenize);
if (!text)
return text;
break;
if (!strcmp(com_token, "\n"))
return text;
break;
if (cmd_argc < MAX_ARGS)
{
@ -1590,19 +1594,30 @@ const char *Cmd_TokenizeString (const char *text, qboolean expandmacros, qboolea
cmd_argc++;
}
}
if (args)
{
const char *argsend = text?text:args+strlen(args);
while (argsend > args && (argsend[-1] == '\n' || argsend[-1] == '\r'))
argsend--;
Cmd_Args_Set(args, argsend-args);
}
else
Cmd_Args_Set(NULL, 0);
return text;
}
void Cmd_TokenizePunctation (char *text, char *punctuation)
{
int i;
char *args = NULL;
// clear the args from the last string
for (i=0 ; i<cmd_argc ; i++)
Z_Free (cmd_argv[i]);
cmd_argc = 0;
Cmd_Args_Set(NULL);
Cmd_Args_Set(NULL, 0);
while (1)
{
@ -1619,16 +1634,14 @@ void Cmd_TokenizePunctation (char *text, char *punctuation)
}
if (!*text)
return;
break;
if (cmd_argc == 1)
{
Cmd_Args_Set(text);
}
args = text;
text = COM_ParseToken (text, punctuation);
if (!text)
return;
break;
if (cmd_argc < MAX_ARGS)
{
@ -1637,6 +1650,14 @@ void Cmd_TokenizePunctation (char *text, char *punctuation)
cmd_argc++;
}
}
if (args)
{
const char *argsend = text?text:args+strlen(args);
while (argsend > args && (argsend[-1] == '\n' || argsend[-1] == '\r'))
argsend--;
Cmd_Args_Set(args, argsend-args);
}
}
@ -2095,7 +2116,7 @@ void Cmd_Apropos_f (void)
;
else
continue;
Con_Printf("command ^2%s^7: %s\n", cmd->name, cmd->description?cmd->description:"no description");
Con_Printf("command ^2%s^7: %s\n", cmd->name, cmd->description?cmd->description:"no description");
}
//FIXME: add aliases.
}

View File

@ -131,7 +131,7 @@ const char *Cmd_TokenizeString (const char *text, qboolean expandmacros, qboolea
void Cmd_ExecuteString (char *text, int restrictionlevel);
void Cmd_Args_Set(const char *newargs);
void Cmd_Args_Set(const char *newargs, size_t len);
#define RESTRICT_MAX_TOTAL 31
#define RESTRICT_MAX_USER 29

View File

@ -77,9 +77,6 @@ static BUILTIN(void, Sys_CloseLibrary, (dllhandle_t *hdl));
#define ARGNAMES ,version
static BUILTINR(rbeplugfuncs_t*, RBE_GetPluginFuncs, (int version));
#undef ARGNAMES
#define ARGNAMES ,name,defaultval,flags,description,groupname
static BUILTINR(cvar_t*, Cvar_GetNVFDG, (const char *name, const char *defaultval, unsigned int flags, const char *description, const char *groupname));
#undef ARGNAMES
static rbeplugfuncs_t *rbefuncs;
cvar_t r_meshpitch;
@ -2831,7 +2828,6 @@ static qintptr_t QDECL Plug_ODE_Shutdown(qintptr_t *args)
qintptr_t Plug_Init(qintptr_t *args)
{
CHECKBUILTIN(RBE_GetPluginFuncs);
CHECKBUILTIN(Cvar_GetNVFDG);
#ifndef ODE_STATIC
CHECKBUILTIN(Sys_LoadLibrary);
CHECKBUILTIN(Sys_CloseLibrary);

View File

@ -392,11 +392,18 @@ int Q_strncasecmp (const char *s1, const char *s2, int n)
int Q_strcasecmp (const char *s1, const char *s2)
{
return Q_strncasecmp (s1, s2, 99999);
return Q_strncasecmp (s1, s2, INT_MAX);
}
int QDECL Q_stricmp (const char *s1, const char *s2)
{
return Q_strncasecmp (s1, s2, 99999);
return Q_strncasecmp (s1, s2, INT_MAX);
}
int Q_strstopcasecmp(const char *s1start, const char *s1end, const char *s2)
{ //safer version of strncasecmp, where s1 is the one with the length, and must exactly match s2 (which is null terminated and probably an immediate.
//return value isn't suitable for sorting.
if (s1end - s1start != strlen(s2))
return -1;
return Q_strncasecmp (s1start, s2, s1end - s1start);
}
char *Q_strcasestr(const char *haystack, const char *needle)
@ -2042,7 +2049,7 @@ char *COM_FileExtension (const char *in, char *result, size_t sizeofresult)
int i;
const char *dot;
for (dot = in + strlen(in); dot >= in && *dot != '.'; dot--)
for (dot = in + strlen(in); dot >= in && *dot != '.' && *dot != '/' && *dot != '\\'; dot--)
;
if (dot < in)
{
@ -2058,6 +2065,23 @@ char *COM_FileExtension (const char *in, char *result, size_t sizeofresult)
return result;
}
//returns a pointer to the extension text, including the dot
//term is the end of the string (or null, to make things easy). if its a previous (non-empty) return value, then you can scan backwards to skip .gz or whatever extra postfixes.
const char *COM_GetFileExtension (const char *in, const char *term)
{
const char *dot;
if (!term)
term = in + strlen(in);
for (dot = term; dot >= in && *dot != '.' && *dot != '/' && *dot != '\\'; dot--)
;
if (dot < in)
return "";
in = dot;
return in;
}
//Quake 2's tank model has a borked skin (or two).
void COM_CleanUpPath(char *str)
{
@ -4962,7 +4986,7 @@ void COM_ErrorMe_f(void)
#ifdef LOADERTHREAD
static void QDECL COM_WorkerCount_Change(cvar_t *var, char *oldvalue);
cvar_t worker_flush = CVARD("worker_flush", "1", "If set, process the entire load queue, loading stuff faster but at the risk of stalling the main thread.");
cvar_t worker_count = CVARFDC("worker_count", "", CVAR_NOTFROMSERVER, "Specifies the number of worker threads to utilise.", COM_WorkerCount_Change);
cvar_t worker_count = CVARFCD("worker_count", "", CVAR_NOTFROMSERVER, COM_WorkerCount_Change, "Specifies the number of worker threads to utilise.");
cvar_t worker_sleeptime = CVARFD("worker_sleeptime", "0", CVAR_NOTFROMSERVER, "Causes workers to sleep for a period of time after each job.");
#define WORKERTHREADS 16 //max

View File

@ -339,6 +339,7 @@ void QDECL Q_strncpyz(char*d, const char*s, int n);
char *Q_strcasestr(const char *haystack, const char *needle);
int Q_strncasecmp (const char *s1, const char *s2, int n);
int Q_strcasecmp (const char *s1, const char *s2);
int Q_strstopcasecmp(const char *s1start, const char *s1end, const char *s2);
int Q_atoi (const char *str);
float Q_atof (const char *str);
void deleetstring(char *result, const char *leet);
@ -416,7 +417,8 @@ void COM_FileBase (const char *in, char *out, int outlen);
int QDECL COM_FileSize(const char *path);
void COM_DefaultExtension (char *path, const char *extension, int maxlen);
qboolean COM_RequireExtension(char *path, const char *extension, int maxlen);
char *COM_FileExtension (const char *in, char *result, size_t sizeofresult);
char *COM_FileExtension (const char *in, char *result, size_t sizeofresult); //copies the extension, without the dot
const char *COM_GetFileExtension (const char *in, const char *term); //returns the extension WITH the dot, allowing for scanning backwards.
void COM_CleanUpPath(char *str);
char *VARGS va(const char *format, ...) LIKEPRINTF(1);
@ -432,7 +434,8 @@ extern char com_configdir[MAX_OSPATH]; //dir to put cfg_save configs in
//extern char *com_basedir;
//qofs_Make is used to 'construct' a variable of qofs_t type. this is so the code can merge two 32bit ints on old systems and use a long long type internally without generating warnings about bit shifts when qofs_t is only 32bit instead.
#if 1//defined(__amd64__) || defined(_AMD64_) || __WORDSIZE == 64
//#if defined(__amd64__) || defined(_AMD64_) || __WORDSIZE == 64
#if !defined(FTE_TARGET_WEB) && !defined(NACL)
#if !defined(_MSC_VER) || _MSC_VER > 1200
#define FS_64BIT
#endif
@ -440,16 +443,23 @@ extern char com_configdir[MAX_OSPATH]; //dir to put cfg_save configs in
#ifdef FS_64BIT
typedef quint64_t qofs_t; //type to use for a file offset
#define qofs_Make(low,high) (low | (((qofs_t)(high))<<32))
#define qofs_Low(o) ((o)&0xffffffffu)
#define qofs_High(o) ((o)>>32)
#define qofs_Error(o) ((o) == ~(quint64_t)0u)
#define qofs_Low(ofs) ((ofs)&0xffffffffu)
#define qofs_High(ofs) ((ofs)>>32)
#define qofs_Error(ofs) ((ofs) == ~(quint64_t)0u)
#define PRIxQOFS PRIx64
#define PRIuQOFS PRIu64
#else
typedef quint32_t qofs_t; //type to use for a file offset
#define qofs_Make(low,high) (low)
#define qofs_Low(o) (o)
#define qofs_High(o) (0)
#define qofs_Error(o) ((o) == ~0ul)
#define qofs_Low(ofs) (ofs)
#define qofs_High(ofs) (0)
#define qofs_Error(ofs) ((ofs) == ~0ul)
#define PRIxQOFS "x"
#define PRIuQOFS "u"
#endif
#define qofs_ErrorValue() (~(qofs_t)0u)
typedef struct searchpathfuncs_s searchpathfuncs_t;
typedef struct searchpath_s
@ -491,6 +501,7 @@ struct vfsfile_s;
int FS_FLocateFile(const char *filename, unsigned int flags, flocation_t *loc);
struct vfsfile_s *FS_OpenReadLocation(flocation_t *location);
char *FS_WhichPackForLocation(flocation_t *loc, qboolean makereferenced);
qboolean FS_GetLocMTime(flocation_t *location, time_t *modtime);
char *FS_GetPackageDownloadFilename(flocation_t *loc);
qboolean FS_GetPackageDownloadable(const char *package);
@ -511,7 +522,13 @@ typedef struct vfsfile_s
qofs_t (QDECL *GetLen) (struct vfsfile_s *file); //could give some lag
qboolean (QDECL *Close) (struct vfsfile_s *file); //returns false if there was some error.
void (QDECL *Flush) (struct vfsfile_s *file);
qboolean seekingisabadplan;
enum
{
SS_SEEKABLE,
SS_SLOW, //probably readonly, its fine for an occasional seek, its just really. really. slow.
SS_PIPE, //read can be seeked, write appends only.
SS_UNSEEKABLE
} seekstyle;
#ifdef _DEBUG
char dbgname[MAX_QPATH];
@ -623,8 +640,10 @@ typedef struct
char *downloadsurl; //optional installable files (menu)
char *installupd; //which download/updated package to install.
char *protocolname; //the name used for purposes of dpmaster
char *defaultexec; //execed after cvars are reset, to give game-specific defaults.
char *defaultexec; //execed after cvars are reset, to give game-specific engine-defaults.
char *defaultoverrides; //execed after default.cfg, to give usable defaults even when the mod the user is running is shit.
char *eula; //when running as an installer, the user will be presented with this as a prompt
char *rtcbroker; //the broker to use for webrtc connections.
struct
{
qboolean base;
@ -642,6 +661,7 @@ typedef struct
int mirrornum; //the index we last tried to download from, so we still work even if mirrors are down.
} package[64];
} ftemanifest_t;
extern ftemanifest_t *fs_manifest; //currently active manifest.
void FS_Manifest_Free(ftemanifest_t *man);
ftemanifest_t *FS_Manifest_Parse(const char *fname, const char *data);
void PM_Shutdown(void);
@ -659,7 +679,6 @@ struct gamepacks
};
void COM_Gamedir (const char *dir, const struct gamepacks *packagespaths);
char *FS_GetGamedir(qboolean publicpathonly);
char *FS_GetBasedir(void);
char *FS_GetManifestArgs(void);
int FS_GetManifestArgv(char **argv, int maxargs);

View File

@ -422,7 +422,7 @@ showhelp:
//default values are meant to be constants.
//sometimes that just doesn't make sense.
//so provide a safe way to change it (MUST be initialised to NULL)
//so provide a safe way to change it (MUST be initialised to NULL so that it doesn't get freed)
void Cvar_SetEngineDefault(cvar_t *var, char *val)
{
qboolean wasdefault = (var->defaultstr == var->enginevalue);
@ -1021,11 +1021,22 @@ Cvar_SetValue
void Cvar_SetValue (cvar_t *var, float value)
{
char val[32];
char *e, *p;
// if (value == (int)value)
// sprintf (val, "%i",(int)value); //make it look nicer.
// else
sprintf (val, "%g",value);
sprintf (val, "%f",value);
p = strchr(val, '.');
if (p)
{
e = p + strlen(p);
while (e > p && e[-1] == '0')
e--;
if (e[-1] == '.')
e--;
*e = 0;
}
Cvar_Set (var, val);
}
void Cvar_ForceSetValue (cvar_t *var, float value)

View File

@ -86,21 +86,21 @@ typedef struct cvar_s
} cvar_t;
#ifdef MINIMAL
#define CVARAFDC(ConsoleName,Value,ConsoleName2,Flags,Description,Callback) {ConsoleName, NULL, NULL, Flags, 0, 0, 0, ConsoleName2, Callback, NULL, Value}
#define CVARAFCD(ConsoleName,Value,ConsoleName2,Flags,Callback,Description) {ConsoleName, NULL, NULL, Flags, 0, 0, 0, ConsoleName2, Callback, NULL, Value}
#else
#define CVARAFDC(ConsoleName,Value,ConsoleName2,Flags,Description,Callback) {ConsoleName, NULL, NULL, Flags, 0, 0, 0, ConsoleName2, Callback, Description, Value}
#define CVARAFCD(ConsoleName,Value,ConsoleName2,Flags,Callback,Description) {ConsoleName, NULL, NULL, Flags, 0, 0, 0, ConsoleName2, Callback, Description, Value}
#endif
#define CVARAFD(ConsoleName,Value,ConsoleName2,Flags,Description)CVARAFDC(ConsoleName, Value, ConsoleName2, Flags, Description, NULL)
#define CVARAFC(ConsoleName,Value,ConsoleName2,Flags,Callback) CVARAFDC(ConsoleName, Value, ConsoleName2, Flags, NULL, Callback)
#define CVARAF(ConsoleName,Value,ConsoleName2,Flags) CVARAFDC(ConsoleName, Value, ConsoleName2, Flags, NULL, NULL)
#define CVARFDC(ConsoleName,Value,Flags,Description,Callback) CVARAFDC(ConsoleName, Value, NULL, Flags, Description, Callback)
#define CVARFC(ConsoleName,Value,Flags,Callback) CVARAFDC(ConsoleName, Value, NULL, Flags, NULL, Callback)
#define CVARAD(ConsoleName,Value,ConsoleName2,Description) CVARAFDC(ConsoleName, Value, ConsoleName2, 0, Description, NULL)
#define CVARFD(ConsoleName,Value,Flags,Description) CVARAFDC(ConsoleName, Value, NULL, Flags, Description, NULL)
#define CVARAFD(ConsoleName,Value,ConsoleName2,Flags,Description)CVARAFCD(ConsoleName, Value, ConsoleName2, Flags, NULL, Description)
#define CVARAFC(ConsoleName,Value,ConsoleName2,Flags,Callback) CVARAFCD(ConsoleName, Value, ConsoleName2, Flags, Callback, NULL)
#define CVARAF(ConsoleName,Value,ConsoleName2,Flags) CVARAFCD(ConsoleName, Value, ConsoleName2, Flags, NULL, NULL)
#define CVARFCD(ConsoleName,Value,Flags,Callback,Description) CVARAFCD(ConsoleName, Value, NULL, Flags, Callback, Description)
#define CVARFC(ConsoleName,Value,Flags,Callback) CVARAFCD(ConsoleName, Value, NULL, Flags, Callback, NULL)
#define CVARAD(ConsoleName,Value,ConsoleName2,Description) CVARAFCD(ConsoleName, Value, ConsoleName2, 0, NULL, Description)
#define CVARFD(ConsoleName,Value,Flags,Description) CVARAFCD(ConsoleName, Value, NULL, Flags, NULL, Description)
#define CVARF(ConsoleName,Value,Flags) CVARFC(ConsoleName, Value, Flags, NULL)
#define CVARC(ConsoleName,Value,Callback) CVARFC(ConsoleName, Value, 0, Callback)
#define CVARCD(ConsoleName,Value,Callback,Description) CVARAFDC(ConsoleName, Value, NULL, 0, Description, Callback)
#define CVARD(ConsoleName,Value,Description) CVARAFDC(ConsoleName, Value, NULL, 0, Description, NULL)
#define CVARCD(ConsoleName,Value,Callback,Description) CVARAFCD(ConsoleName, Value, NULL, 0, Callback, Description)
#define CVARD(ConsoleName,Value,Description) CVARAFCD(ConsoleName, Value, NULL, 0, NULL, Description)
#define CVAR(ConsoleName,Value) CVARD(ConsoleName, Value, NULL)
#define CVARDP4(Flags,ConsoleName,Value,Description) CVARFD(ConsoleName, Value, Flags,Description)

View File

@ -23,8 +23,9 @@ static unsigned int fs_restarts;
void *fs_thread_mutex;
cvar_t com_fs_cache = CVARF("fs_cache", IFMINIMAL("2","1"), CVAR_ARCHIVE);
cvar_t fs_noreexec = CVARD("fs_noreexec", "0", "Disables automatic re-execing configs on gamedir switches.\nThis means your cvar defaults etc may be from the wrong mod, and cfg_save will leave that stuff corrupted!");
cvar_t cfg_reload_on_gamedir = CVAR("cfg_reload_on_gamedir", "1");
cvar_t fs_game = CVARFDC("game", "", CVAR_NOSAVE|CVAR_NORESET, "Provided for Q2 compat.", fs_game_callback);
cvar_t fs_game = CVARFCD("game", "", CVAR_NOSAVE|CVAR_NORESET, fs_game_callback, "Provided for Q2 compat.");
cvar_t fs_gamedir = CVARFD("fs_gamedir", "", CVAR_NOUNSAFEEXPAND|CVAR_NOSET|CVAR_NOSAVE, "Provided for Q2 compat.");
cvar_t fs_basedir = CVARFD("fs_basedir", "", CVAR_NOUNSAFEEXPAND|CVAR_NOSET|CVAR_NOSAVE, "Provided for Q2 compat.");
int active_fs_cachetype;
@ -210,6 +211,8 @@ void FS_Manifest_Free(ftemanifest_t *man)
Z_Free(man->protocolname);
Z_Free(man->eula);
Z_Free(man->defaultexec);
Z_Free(man->defaultoverrides);
Z_Free(man->rtcbroker);
for (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++)
{
Z_Free(man->gamepath[i].path);
@ -247,6 +250,10 @@ static ftemanifest_t *FS_Manifest_Clone(ftemanifest_t *oldm)
newm->eula = Z_StrDup(oldm->eula);
if (oldm->defaultexec)
newm->defaultexec = Z_StrDup(oldm->defaultexec);
if (oldm->defaultoverrides)
newm->defaultoverrides = Z_StrDup(oldm->defaultoverrides);
if (oldm->rtcbroker)
newm->rtcbroker = Z_StrDup(oldm->rtcbroker);
newm->disablehomedir = oldm->disablehomedir;
for (i = 0; i < sizeof(newm->gamepath) / sizeof(newm->gamepath[0]); i++)
@ -291,6 +298,10 @@ void FS_Manifest_Print(ftemanifest_t *man)
Con_Printf("protocolname %s\n", COM_QuotedString(man->protocolname, buffer, sizeof(buffer), false));
if (man->defaultexec)
Con_Printf("defaultexec %s\n", COM_QuotedString(man->defaultexec, buffer, sizeof(buffer), false));
if (man->defaultoverrides)
Con_Printf("%s", man->defaultoverrides);
if (man->rtcbroker)
Con_Printf("rtcbroker %s\n", COM_QuotedString(man->rtcbroker, buffer, sizeof(buffer), false));
for (i = 0; i < sizeof(man->gamepath) / sizeof(man->gamepath[0]); i++)
{
@ -531,6 +542,15 @@ static qboolean FS_Manifest_ParseTokens(ftemanifest_t *man)
Z_Free(man->defaultexec);
man->defaultexec = Z_StrDup(Cmd_Argv(1));
}
else if (!Q_strcasecmp(cmd, "bind") || !Q_strcasecmp(cmd, "set") || !Q_strcasecmp(cmd, "seta"))
{
Z_StrCat(&man->defaultoverrides, va("%s %s\n", Cmd_Argv(0), Cmd_Args()));
}
else if (!Q_strcasecmp(cmd, "rtcbroker"))
{
Z_Free(man->rtcbroker);
man->rtcbroker = Z_StrDup(Cmd_Argv(1));
}
else if (!Q_strcasecmp(cmd, "updateurl"))
{
Z_Free(man->updateurl);
@ -1618,7 +1638,7 @@ vfsfile_t *VFS_Filter(const char *filename, vfsfile_t *handle)
{
// char *ext;
if (!handle || handle->WriteBytes || handle->seekingisabadplan) //only on readonly files
if (!handle || handle->WriteBytes || handle->seekstyle == SS_SLOW || handle->seekstyle == SS_UNSEEKABLE) //only on readonly files for which we can undo any header read damage
return handle;
// ext = COM_FileExtension (filename);
#ifdef AVAIL_GZDEC
@ -1878,7 +1898,7 @@ vfsfile_t *FS_OpenVFS(const char *filename, const char *mode, enum fs_relative r
COM_CreatePath(fullname);
vfs = VFSOS_Open(fullname, mode);
}
if (vfs && (*mode == 'w' || *mode == 'a'))
if (vfs || !(*mode == 'w' || *mode == 'a'))
return vfs;
//fall through
case FS_PUBGAMEONLY:
@ -1945,6 +1965,14 @@ vfsfile_t *FS_OpenVFS(const char *filename, const char *mode, enum fs_relative r
return NULL;
}
qboolean FS_GetLocMTime(flocation_t *location, time_t *modtime)
{
*modtime = 0;
if (!location->search->handle->FileStat || !location->search->handle->FileStat(location->search->handle, location, modtime))
return false;
return true;
}
/*opens a vfsfile from an already discovered location*/
vfsfile_t *FS_OpenReadLocation(flocation_t *location)
{
@ -5021,6 +5049,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
qboolean reloadconfigs = false;
qboolean builtingame = false;
flocation_t loc;
qboolean allowvidrestart = true;
char *vidfile[] = {"gfx.wad", "gfx/conback.lmp", //misc stuff
"gfx/palette.lmp", "pics/colormap.pcx"}; //palettes
@ -5033,13 +5062,26 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
for (i = 0; i < countof(vidfile); i++)
{
FS_FLocateFile(vidfile[i], FSLF_IFFOUND, &loc); //q1
vidpath[i] = loc.search?loc.search->handle:NULL;
if (allowvidrestart)
{
FS_FLocateFile(vidfile[i], FSLF_IFFOUND, &loc); //q1
vidpath[i] = loc.search?loc.search->handle:NULL;
}
else
vidpath[i] = NULL;
}
if (allowreloadconfigs && fs_noreexec.ival)
allowreloadconfigs = false;
for (i = 0; i < countof(conffile); i++)
{
FS_FLocateFile(conffile[i], FSLF_IFFOUND, &loc); //q1
confpath[i] = loc.search?loc.search->handle:NULL;
if (allowreloadconfigs)
{
FS_FLocateFile(conffile[i], FSLF_IFFOUND, &loc); //q1
confpath[i] = loc.search?loc.search->handle:NULL;
}
else
confpath[i] = NULL;
}
#if defined(NACL) || defined(FTE_TARGET_WEB) || defined(ANDROID) || defined(WINRT)
@ -5153,7 +5195,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
}
}
if (!man->downloadsurl)
if (!man->downloadsurl && gamemode_info[i].downloadsurl)
{
Cmd_TokenizeString(va("downloadsurl \"%s\"", gamemode_info[i].downloadsurl), false, false);
FS_Manifest_ParseTokens(man);
@ -5262,7 +5304,7 @@ qboolean FS_ChangeGame(ftemanifest_t *man, qboolean allowreloadconfigs, qboolean
COM_CheckRegistered();
if (qrenderer != QR_NONE)
if (qrenderer != QR_NONE && allowvidrestart)
{
for (i = 0; i < countof(vidfile); i++)
{

View File

@ -47,6 +47,7 @@ struct searchpathfuncs_s
qboolean (QDECL *PollChanges)(searchpathfuncs_t *handle); //returns true if there were changes
qboolean (QDECL *FileStat)(searchpathfuncs_t *handle, flocation_t *loc, time_t *mtime);
qboolean (QDECL *RenameFile)(searchpathfuncs_t *handle, const char *oldname, const char *newname); //returns true on success, false if source doesn't exist, or if dest does.
qboolean (QDECL *RemoveFile)(searchpathfuncs_t *handle, const char *filename); //returns true on success, false if it wasn't found or is readonly.
qboolean (QDECL *MkDir)(searchpathfuncs_t *handle, const char *filename); //is this really needed?
@ -86,5 +87,9 @@ int FS_EnumerateKnownGames(qboolean (*callback)(void *usr, ftemanifest_t *man),
#define SPF_BASEPATH 128 //part of the basegames, and not the mod gamedir(s).
qboolean FS_LoadPackageFromFile(vfsfile_t *vfs, char *pname, char *localname, int *crc, unsigned int flags);
#ifdef AVAIL_XZDEC
vfsfile_t *FS_XZ_DecompressWriteFilter(vfsfile_t *infile);
#endif
#ifdef AVAIL_GZDEC
vfsfile_t *FS_GZ_WriteFilter(vfsfile_t *outfile, qboolean autoclosefile, qboolean compress);
#endif

View File

@ -93,7 +93,9 @@ vfsfile_t *FSSTDIO_OpenTemp(void)
#ifdef _WIN32
/*warning: annother app might manage to open the file before we can. if the file is not opened exclusively then we can end up with issues
on windows, fopen is typically exclusive anyway, but not on unix. but on unix, tmpfile is actually usable, so special-case the windows code*/
on windows, fopen is typically exclusive anyway, but not on unix. but on unix, tmpfile is actually usable, so special-case the windows code
we also have a special close function to ensure the file is deleted too
*/
char *fname = _tempnam(NULL, "ftemp");
f = fopen(fname, "w+b");
if (!f)
@ -321,6 +323,18 @@ static int QDECL FSSTDIO_EnumerateFiles (searchpathfuncs_t *handle, const char *
return Sys_EnumerateFiles(sp->rootpath, match, func, parm, handle);
}
#include <sys/stat.h>
static qboolean QDECL FSSTDIO_FileStat (searchpathfuncs_t *handle, flocation_t *loc, time_t *mtime)
{
struct stat s;
if (stat(loc->rawname, &s) != -1)
{
*mtime = s.st_mtime;
return true;
}
return false;
}
searchpathfuncs_t *QDECL FSSTDIO_OpenPath(vfsfile_t *mustbenull, const char *desc, const char *prefix)
{
@ -345,6 +359,7 @@ searchpathfuncs_t *QDECL FSSTDIO_OpenPath(vfsfile_t *mustbenull, const char *des
np->pub.EnumerateFiles = FSSTDIO_EnumerateFiles;
np->pub.OpenVFS = FSSTDIO_OpenVFS;
np->pub.PollChanges = FSSTDIO_PollChanges;
np->pub.FileStat = FSSTDIO_FileStat;
return &np->pub;
}

View File

@ -316,6 +316,7 @@ static vfsfile_t *QDECL VFSW32_OpenInternal(vfsw32path_t *handle, const char *qu
unsigned int fsize;
void *mmap;
qboolean didexist = true;
qboolean create;
vfsw32file_t *file;
qboolean read = !!strchr(mode, 'r');
@ -323,6 +324,7 @@ static vfsfile_t *QDECL VFSW32_OpenInternal(vfsw32path_t *handle, const char *qu
qboolean append = !!strchr(mode, 'a');
qboolean text = !!strchr(mode, 't');
write |= append;
create = write;
if (strchr(mode, '+'))
read = write = true;
@ -332,7 +334,9 @@ static vfsfile_t *QDECL VFSW32_OpenInternal(vfsw32path_t *handle, const char *qu
if (!WinNT)
{
//FILE_SHARE_DELETE is not supported in 9x, sorry.
if ((write && read) || append)
if (!create && write)
h = CreateFileA(osname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
else if ((write && read) || append)
h = CreateFileA(osname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
else if (write)
h = CreateFileA(osname, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
@ -356,6 +360,8 @@ static vfsfile_t *QDECL VFSW32_OpenInternal(vfsw32path_t *handle, const char *qu
if (h != INVALID_HANDLE_VALUE)
;
else if (!create && write)
h = CreateFileW(wide, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
else if ((write && read) || append)
h = CreateFileW(wide, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
else if (write)
@ -430,6 +436,25 @@ vfsfile_t *QDECL VFSW32_Open(const char *osname, const char *mode)
return VFSW32_OpenInternal(NULL, NULL, osname, mode);
}
#include <sys/stat.h>
static qboolean QDECL VFSW32_FileStat(searchpathfuncs_t *handle, flocation_t *loc, time_t *mtime)
{
int r;
struct _stat s;
if (WinNT)
{
wchar_t wide[MAX_OSPATH];
widen(wide, sizeof(wide), loc->rawname);
r = _wstat(wide, &s);
}
else
r = _stat(loc->rawname, &s);
if (r)
return false;
*mtime = s.st_mtime;
return true;
}
static vfsfile_t *QDECL VFSW32_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)
{
//path is already cleaned, as anything that gets a valid loc needs cleaning up first.
@ -641,6 +666,8 @@ searchpathfuncs_t *QDECL VFSW32_OpenPath(vfsfile_t *mustbenull, const char *desc
np->pub.OpenVFS = VFSW32_OpenVFS;
np->pub.PollChanges = VFSW32_PollChanges;
np->pub.FileStat = VFSW32_FileStat;
np->pub.RenameFile = VFSW32_RenameFile;
np->pub.RemoveFile = VFSW32_RemoveFile;
np->pub.MkDir = VFSW32_MkDir;

View File

@ -3112,7 +3112,7 @@ vfsfile_t *FS_XZ_DecompressWriteFilter(vfsfile_t *outfile)
n->vf.Tell = NULL;
n->vf.Close = FS_XZ_Dec_Close;
n->vf.WriteBytes = FS_XZ_Dec_Write;
n->vf.seekingisabadplan = true;
n->vf.seekstyle = SS_UNSEEKABLE;
return &n->vf;
}

View File

@ -484,7 +484,7 @@ vfsfile_t *FS_GZ_WriteFilter(vfsfile_t *outfile, qboolean autoclosefile, qboolea
n->vf.Tell = NULL;
n->vf.Close = FS_GZ_Dec_Close;
n->vf.WriteBytes = FS_GZ_Dec_Write;
n->vf.seekingisabadplan = true;
n->vf.seekstyle = SS_UNSEEKABLE;
if (n->compress)
{
@ -1049,6 +1049,13 @@ static qboolean QDECL VFSZIP_Close (struct vfsfile_s *file)
static qboolean FSZIP_ValidateLocalHeader(zipfile_t *zip, zpackfile_t *zfile, qofs_t *datastart, qofs_t *datasize);
static qboolean QDECL FSZIP_FileStat (searchpathfuncs_t *handle, flocation_t *loc, time_t *mtime)
{
zpackfile_t *pf = loc->fhandle;
*mtime = pf->mtime;
return true;
}
static vfsfile_t *QDECL FSZIP_OpenVFS(searchpathfuncs_t *handle, flocation_t *loc, const char *mode)
{
zipfile_t *zip = (void*)handle;
@ -1082,7 +1089,7 @@ static vfsfile_t *QDECL FSZIP_OpenVFS(searchpathfuncs_t *handle, flocation_t *lo
vfsz->funcs.Seek = VFSZIP_Seek;
vfsz->funcs.Tell = VFSZIP_Tell;
vfsz->funcs.WriteBytes = NULL;
vfsz->funcs.seekingisabadplan = true;
vfsz->funcs.seekstyle = SS_SLOW;
if (!FSZIP_ValidateLocalHeader(zip, pf, &vfsz->startpos, &datasize))
{
@ -1806,6 +1813,7 @@ searchpathfuncs_t *QDECL FSZIP_LoadArchive (vfsfile_t *packhandle, const char *d
zip->pub.FindFile = FSZIP_FLocate;
zip->pub.ReadFile = FSZIP_ReadFile;
zip->pub.EnumerateFiles = FSZIP_EnumerateFiles;
zip->pub.FileStat = FSZIP_FileStat;
zip->pub.GeneratePureCRC = FSZIP_GeneratePureCRC;
zip->pub.OpenVFS = FSZIP_OpenVFS;
return &zip->pub;

View File

@ -25,17 +25,39 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define HAVE_WEBSOCKCL
#endif
//FIXME: should split this into loopback/dgram/stream/irc
//FIXME: should split this into loopback/dgram/stream/dtls/tls/irc
//with the ipv4/v6/x as a separate parameter
typedef enum {NA_INVALID, NA_LOOPBACK, NA_IP, NA_IPV6, NA_IPX, NA_BROADCAST_IP, NA_BROADCAST_IP6, NA_BROADCAST_IPX, NA_TCP, NA_TCPV6, NA_TLSV4, NA_TLSV6, NA_IRC, NA_WEBSOCKET, NA_NATPMP} netadrtype_t;
typedef enum {
NA_INVALID,
NA_LOOPBACK,
/*NA_HYBRID,*/ //ipv6 hybrid socket that might accept ipv4 packets too.
NA_IP,
NA_IPV6,
NA_IPX,
#ifdef IRCCONNECT
NA_IRC/*remove!*/,
#endif
#ifdef HAVE_WEBSOCKCL
NA_WEBSOCKET,
#endif
} netadrtype_t;
typedef enum {
NP_DGRAM,
NP_DTLS, //connected via ICE/WebRTC
NP_STREAM,
NP_TLS,
NP_WS,
NP_WSS,
NP_IRC,
NP_NATPMP
} netproto_t;
typedef enum {NS_CLIENT, NS_SERVER} netsrc_t;
typedef enum {NQP_ERROR, NQP_DATAGRAM, NQP_RELIABLE} nqprot_t;
typedef struct
{
netadrtype_t type;
netproto_t prot;
union {
qbyte ip[4];
@ -105,6 +127,7 @@ int NET_LocalAddressForRemote(struct ftenet_connections_s *collection, netadr_
void NET_PrintAddresses(struct ftenet_connections_s *collection);
qboolean NET_AddressSmellsFunny(netadr_t *a);
qboolean NET_EnsureRoute(struct ftenet_connections_s *collection, char *routename, char *host, qboolean islisten);
void NET_PrintConnectionsStatus(struct ftenet_connections_s *collection);
enum addressscope_e
{
@ -115,16 +138,18 @@ enum addressscope_e
};
enum addressscope_e NET_ClassifyAddress(netadr_t *adr, char **outdesc);
qboolean NET_AddrIsReliable(netadr_t *adr); //hints that the protocol is reliable. if so, we don't need to wait for acks
qboolean NET_CompareAdr (netadr_t *a, netadr_t *b);
qboolean NET_CompareBaseAdr (netadr_t *a, netadr_t *b);
void NET_AdrToStringResolve (netadr_t *adr, void (*resolved)(void *ctx, void *data, size_t a, size_t b), void *ctx, size_t a, size_t b);
char *NET_AdrToString (char *s, int len, netadr_t *a);
char *NET_SockadrToString (char *s, int len, struct sockaddr_qstorage *a);
char *NET_BaseAdrToString (char *s, int len, netadr_t *a);
size_t NET_StringToSockaddr2 (const char *s, int defaultport, struct sockaddr_qstorage *sadr, int *addrfamily, int *addrsize, size_t addrcount);
#define NET_StringToSockaddr(s,p,a,f,z) (NET_StringToSockaddr2(s,p,a,f,z,1)>0)
size_t NET_StringToAdr2 (const char *s, int defaultport, netadr_t *a, size_t addrcount);
#define NET_StringToAdr(s,p,a) NET_StringToAdr2(s,p,a,1)
qboolean NET_PortToAdr (int adrfamily, const char *s, netadr_t *a);
qboolean NET_PortToAdr (netadrtype_t adrfamily, netproto_t adrprot, const char *s, netadr_t *a);
qboolean NET_IsClientLegal(netadr_t *adr);
qboolean NET_IsLoopBackAddress (netadr_t *adr);
@ -134,7 +159,7 @@ char *NET_AdrToStringMasked (char *s, int len, netadr_t *a, netadr_t *amask);
void NET_IntegerToMask (netadr_t *a, netadr_t *amask, int bits);
qboolean NET_CompareAdrMasked(netadr_t *a, netadr_t *b, netadr_t *mask);
qboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *name, const char *address, netadrtype_t addrtype, qboolean islisten);
qboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *name, const char *address, netadrtype_t addrtype, netproto_t addrprot, qboolean islisten);
//============================================================================
@ -230,7 +255,7 @@ qboolean Netchan_CanPacket (netchan_t *chan, int rate);
void Netchan_Block (netchan_t *chan, int bytes, int rate);
qboolean Netchan_CanReliable (netchan_t *chan, int rate);
#ifdef NQPROT
nqprot_t NQNetChan_Process(netchan_t *chan);
qboolean NQNetChan_Process(netchan_t *chan);
#endif
#ifdef HUFFNETWORK
@ -299,9 +324,9 @@ void Huff_EmitByte(int ch, qbyte *buffer, int *count);
#endif
int UDP_OpenSocket (int port, qboolean bcast);
int UDP6_OpenSocket (int port, qboolean bcast);
int IPX_OpenSocket (int port, qboolean bcast);
int UDP_OpenSocket (int port);
int UDP6_OpenSocket (int port);
int IPX_OpenSocket (int port);
int NetadrToSockadr (netadr_t *a, struct sockaddr_qstorage *s);
void SockadrToNetadr (struct sockaddr_qstorage *s, netadr_t *a);
qboolean NET_Sleep(float seconds, qboolean stdinissocket);

View File

@ -401,7 +401,7 @@ qboolean ServerPaused(void);
#endif
#ifdef NQPROT
nqprot_t NQNetChan_Process(netchan_t *chan)
qboolean NQNetChan_Process(netchan_t *chan)
{
int header;
int sequence;
@ -412,10 +412,10 @@ nqprot_t NQNetChan_Process(netchan_t *chan)
header = LongSwap(MSG_ReadLong());
if (net_message.cursize != (header & NETFLAG_LENGTH_MASK))
return NQP_ERROR; //size was wrong, couldn't have been ours.
return false; //size was wrong, couldn't have been ours.
if (header & NETFLAG_CTL)
return NQP_ERROR; //huh?
return false; //huh?
sequence = LongSwap(MSG_ReadLong());
@ -452,7 +452,7 @@ nqprot_t NQNetChan_Process(netchan_t *chan)
, sequence
, 0);
return NQP_ERROR; //don't try execing the 'payload'. I hate ack packets.
return false; //don't try execing the 'payload'. I hate ack packets.
}
if (header & NETFLAG_UNRELIABLE)
@ -461,7 +461,7 @@ nqprot_t NQNetChan_Process(netchan_t *chan)
{
if (showdrop.ival)
Con_Printf("Stale datagram recieved (%i<=%i)\n", sequence, chan->incoming_unreliable);
return NQP_ERROR;
return false;
}
drop = sequence - chan->incoming_unreliable - 1;
if (drop > 0)
@ -489,7 +489,7 @@ nqprot_t NQNetChan_Process(netchan_t *chan)
, chan->sock != NS_SERVER?"s2c":"c2s"
, chan->incoming_unreliable
, net_message.cursize);
return NQP_DATAGRAM;
return true;
}
if (header & NETFLAG_DATA)
{
@ -512,7 +512,7 @@ nqprot_t NQNetChan_Process(netchan_t *chan)
if (chan->in_fragment_length + net_message.cursize-8 >= sizeof(chan->in_fragment_buf))
{
chan->fatal_error = true;
return NQP_ERROR;
return false;
}
memcpy(chan->in_fragment_buf + chan->in_fragment_length, net_message.data+8, net_message.cursize-8);
@ -530,7 +530,7 @@ nqprot_t NQNetChan_Process(netchan_t *chan)
, chan->sock != NS_SERVER?"s2c":"c2s"
, sequence
, net_message.cursize);
return NQP_RELIABLE; //we can read it now
return true; //we can read it now
}
}
else
@ -539,10 +539,10 @@ nqprot_t NQNetChan_Process(netchan_t *chan)
Con_Printf("Stale reliable (%i)\n", sequence);
}
return NQP_ERROR;
return false;
}
return NQP_ERROR; //not supported.
return false; //not supported.
}
#endif
@ -575,7 +575,7 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate)
send.maxsize = MAX_NQMSGLEN + PACKET_HEADER;
send.cursize = 0;
if ((chan->remote_address.type == NA_TCP || chan->remote_address.type == NA_TCPV6 || chan->remote_address.type == NA_TLSV4 || chan->remote_address.type == NA_TLSV6) && chan->reliable_length)
if (NET_AddrIsReliable(&chan->remote_address) && chan->reliable_length)
{
//if over tcp, everything is assumed to be reliable. pretend it got acked.
chan->reliable_length = 0; //they got the entire message
@ -606,7 +606,7 @@ int Netchan_Transmit (netchan_t *chan, int length, qbyte *data, int rate)
{
MSG_WriteLong(&send, 0);
MSG_WriteLong(&send, LongSwap(chan->reliable_sequence));
if (i > MAX_NQDATAGRAM && chan->remote_address.type != NA_TCP)
if (i > MAX_NQDATAGRAM && !NET_AddrIsReliable(&chan->remote_address))
i = MAX_NQDATAGRAM;
SZ_Write (&send, chan->reliable_buf+chan->reliable_start, i);

View File

@ -76,6 +76,10 @@ struct icestate_s
char *conname; //internal id.
char *friendlyname; //who you're talking to.
unsigned int originid; //should be randomish
unsigned int originversion;//bumped each time something in the sdp changes.
char originaddress[16];
struct icecandidate_s *lc;
char *lpwd;
char *lufrag;
@ -277,21 +281,24 @@ struct icestate_s *QDECL ICE_Create(void *module, const char *conname, const cha
Sys_RandomBytes((void*)rnd, sizeof(rnd));
conname = va("fte%08x%08x", rnd[0], rnd[1]);
}
con = Z_Malloc(sizeof(*con));
con->conname = Z_StrDup(conname);
con->friendlyname = Z_StrDup(peername);
con->proto = proto;
con->rpwd = Z_StrDup("");
con->rufrag = Z_StrDup("");
Sys_RandomBytes((void*)&con->originid, sizeof(con->originid));
con->originversion = 1;
Q_strncpyz(con->originaddress, "127.0.0.1", sizeof(con->originaddress));
con->mode = mode;
if (!collection)
{
con->connections = collection = FTENET_CreateCollection(true);
FTENET_AddToCollection(collection, "UDP", "0", NA_IP, true);
FTENET_AddToCollection(collection, "natpmp", "natpmp://5351", NA_IP, true);
FTENET_AddToCollection(collection, "UDP", "0", NA_IP, NP_DGRAM, true);
FTENET_AddToCollection(collection, "natpmp", "natpmp://5351", NA_IP, NP_NATPMP, true);
}
con->next = icelist;
@ -317,7 +324,7 @@ struct icestate_s *QDECL ICE_Create(void *module, const char *conname, const cha
netadr_t addr[64];
struct ftenet_generic_connection_s *gcon[sizeof(addr)/sizeof(addr[0])];
int flags[sizeof(addr)/sizeof(addr[0])];
unsigned int flags[sizeof(addr)/sizeof(addr[0])];
m = NET_EnumerateAddresses(collection, gcon, flags, addr, sizeof(addr)/sizeof(addr[0]));
@ -325,29 +332,10 @@ struct icestate_s *QDECL ICE_Create(void *module, const char *conname, const cha
{
if (addr[i].type == NA_IP || addr[i].type == NA_IPV6)
{
ICE_AddLCandidateInfo(con, &addr[i], i, ICE_HOST);
/*
cand = Z_Malloc(sizeof(*cand));
cand->info.network = i;
cand->info.port = ntohs(adr.port);
adr.port = 0; //to make sure its not part of the string...
Q_strncpyz(cand->info.addr, NET_AdrToString(adrbuf, sizeof(adrbuf), &adr), sizeof(cand->info.addr));
cand->info.generation = 0;
cand->info.component = 1;
cand->info.foundation = 1;
cand->info.priority =
(1<<24)*(126) +
(1<<8)*((adr.type == NA_IP?32768:0)+net*256+(255-adrno)) +
(1<<0)*(256 - cand->info.component);
Sys_RandomBytes((void*)rnd, sizeof(rnd));
Q_strncpyz(cand->info.candidateid, va("x%08x%08x", rnd[0], rnd[1]), sizeof(cand->info.candidateid));
cand->dirty = true;
cand->next = con->lc;
con->lc = cand;
*/
// if (flags[i] & ADDR_REFLEX)
// ICE_AddLCandidateInfo(con, &addr[i], i, ICE_SRFLX); //FIXME: needs reladdr relport info
// else
ICE_AddLCandidateInfo(con, &addr[i], i, ICE_HOST);
}
}
}
@ -685,7 +673,6 @@ qboolean QDECL ICE_Set(struct icestate_s *con, const char *prop, const char *val
if (!NET_StringToAdr(con->stunserver, con->stunport, &con->pubstunserver))
return false;
}
/*
else if (!strcmp(prop, "sdp"))
{
const char *eol;
@ -694,6 +681,8 @@ qboolean QDECL ICE_Set(struct icestate_s *con, const char *prop, const char *val
eol = strchr(value, '\n');
if (!eol)
eol = value+strlen(value);
else
eol++;
if (!strncmp(value, "a=ice-pwd:", 10))
ICE_Set(con, "rpwd", value+10);
@ -705,7 +694,7 @@ qboolean QDECL ICE_Set(struct icestate_s *con, const char *prop, const char *val
int codec;
char *sl;
value += 9;
codec = strtoul(value, &value, 0);
codec = strtoul(value, (char**)&value, 0);
if (*value == ' ') value++;
COM_ParseOut(value, name, sizeof(name));
@ -720,10 +709,10 @@ qboolean QDECL ICE_Set(struct icestate_s *con, const char *prop, const char *val
memset(&n, 0, sizeof(n));
value += 12;
n.foundation = strtoul(value, &value, 0);
n.foundation = strtoul(value, (char**)&value, 0);
if(*value == ' ')value++;
n.component = strtoul(value, &value, 0);
n.component = strtoul(value, (char**)&value, 0);
if(*value == ' ')value++;
if (!strncmp(value, "UDP ", 4))
@ -735,14 +724,14 @@ qboolean QDECL ICE_Set(struct icestate_s *con, const char *prop, const char *val
break;
if(*value == ' ')value++;
n.priority = strtoul(value, &value, 0);
n.priority = strtoul(value, (char**)&value, 0);
if(*value == ' ')value++;
value = COM_ParseOut(value, n.addr, sizeof(n.addr));
if (!value) break;
if(*value == ' ')value++;
n.port = strtoul(value, &value, 0);
n.port = strtoul(value, (char**)&value, 0);
if(*value == ' ')value++;
if (strncmp(value, "typ ", 4)) break;
@ -773,7 +762,7 @@ qboolean QDECL ICE_Set(struct icestate_s *con, const char *prop, const char *val
else if (!strncmp(value, "rport ", 6))
{
value += 6;
n.relport = strtoul(value, &value, 0);
n.relport = strtoul(value, (char**)&value, 0);
}
else
{
@ -789,12 +778,40 @@ qboolean QDECL ICE_Set(struct icestate_s *con, const char *prop, const char *val
}
}
}
*/
else
return false;
return true;
}
qboolean QDECL ICE_Get(struct icestate_s *con, const char *prop, char *value, int valuelen)
static char *ICE_CandidateToSDP(struct icecandidate_s *can, char *value, size_t valuelen)
{
char *ctype = "?";
switch(can->info.type)
{
default:
case ICE_HOST: ctype = "host"; break;
case ICE_SRFLX: ctype = "srflx"; break;
case ICE_PRFLX: ctype = "prflx"; break;
case ICE_RELAY: ctype = "relay"; break;
}
Q_snprintfz(value, valuelen, "candidate:%i %i %s %i %s %i typ %s",
can->info.foundation,
can->info.component,
can->info.transport==0?"udp":"ERROR",
can->info.priority,
can->info.addr,
can->info.port,
ctype
);
Q_strncatz(value, va(" generation %i", can->info.generation), valuelen);
if (can->info.type != ICE_HOST)
{
Q_strncatz(value, va(" raddr %s", can->info.reladdr), valuelen);
Q_strncatz(value, va(" rport %i", can->info.relport), valuelen);
}
return value;
}
qboolean QDECL ICE_Get(struct icestate_s *con, const char *prop, char *value, size_t valuelen)
{
if (!strcmp(prop, "sid"))
Q_strncpyz(value, con->conname, valuelen);
@ -828,7 +845,6 @@ qboolean QDECL ICE_Get(struct icestate_s *con, const char *prop, char *value, in
}
}
}
/*
else if (!strcmp(prop, "sdp"))
{
struct icecandidate_s *can;
@ -848,13 +864,18 @@ qboolean QDECL ICE_Get(struct icestate_s *con, const char *prop, char *value, in
}
Q_strncpyz(value, "v=0\n", valuelen);
Q_strncatz(value, va("o=$NAME $? $? IN IP4 $ADR\n"), valuelen);
Q_strncatz(value, "s=\n", valuelen);
Q_strncatz(value, va("o=%s %u %u IN IP4 %s\n", "-", con->originid, con->originversion, con->originaddress), valuelen); //originator
Q_strncatz(value, va("s=%s\n", con->conname), valuelen);
Q_strncatz(value, va("c=IN %s %s\n", sender.type==NA_IPV6?"IP6":"IP4", NET_BaseAdrToString(tmpstr, sizeof(tmpstr), &sender)), valuelen);
Q_strncatz(value, "t=0 0\n", valuelen);
Q_strncatz(value, va("a=ice-pwd:%s\n", con->lpwd), valuelen);
Q_strncatz(value, va("a=ice-ufrag:%s\n", con->lufrag), valuelen);
if (con->proto == ICEP_QWSERVER || con->proto == ICEP_QWCLIENT)
{
Q_strncatz(value, "m=application 9 DTLS/SCTP 5000\n", valuelen);
}
for (i = 0; i < countof(con->codec); i++)
{
int codec = atoi(prop+5);
@ -876,39 +897,35 @@ qboolean QDECL ICE_Get(struct icestate_s *con, const char *prop, char *value, in
for (can = con->lc; can; can = can->next)
{
char *ctype = NULL;
char canline[256];
can->dirty = false; //doesn't matter now.
switch(can->info.type)
{
default:
case ICE_HOST: ctype = "host"; break;
case ICE_SRFLX: ctype = "srflx"; break;
case ICE_PRFLX: ctype = "prflx"; break;
case ICE_RELAY: ctype = "relay"; break;
}
Q_strncatz(value, va("a=candidate:%i %i %s %i %s %i typ %s",
can->info.foundation,
can->info.component,
can->info.transport==0?"UDP":"ERROR",
can->info.priority,
can->info.addr,
can->info.port,
ctype
), valuelen);
if (can->info.type != ICE_HOST)
{
Q_strncatz(value, va(" raddr %s", can->info.reladdr), valuelen);
Q_strncatz(value, va(" rport %i", can->info.relport), valuelen);
}
Q_strncatz(value, "a=\n", valuelen);
ICE_CandidateToSDP(can, canline, sizeof(canline));
Q_strncatz(value, canline, valuelen);
Q_strncatz(value, "\n", valuelen);
}
}
}
*/
else
return false;
return true;
}
qboolean QDECL ICE_GetLCandidateSDP(struct icestate_s *con, char *out, size_t outsize)
{
struct icecandidate_s *can;
for (can = con->lc; can; can = can->next)
{
if (can->dirty)
{
struct icecandinfo_s *c = &can->info;
can->dirty = false;
ICE_CandidateToSDP(can, out, outsize);
return true;
}
}
return false;
}
struct icecandinfo_s *QDECL ICE_GetLCandidateInfo(struct icestate_s *con)
{
struct icecandidate_s *can;
@ -1091,7 +1108,8 @@ icefuncs_t iceapi =
ICE_GetLCandidateInfo,
ICE_AddRCandidateInfo,
ICE_Close,
ICE_CloseModule
ICE_CloseModule,
ICE_GetLCandidateSDP
};
qboolean ICE_WasStun(netsrc_t netsrc)

View File

@ -680,7 +680,7 @@ vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server,
newf->funcs.WriteBytes = SSL_Write;
newf->funcs.Seek = SSL_Seek;
newf->funcs.Tell = SSL_Tell;
newf->funcs.seekingisabadplan = true;
newf->funcs.seekingisabadplan = SS_UNSEEKABLE;
Q_strncpyz(newf->certname, hostname, sizeof(newf->certname));

View File

@ -25,6 +25,13 @@ cvar_t *tls_ignorecertificateerrors;
#define USE_PROT_DGRAM_SERVER (SP_PROT_DTLS_SERVER)
#define USE_PROT_DGRAM_CLIENT (SP_PROT_DTLS_CLIENT)
#ifndef szOID_RSA_SHA512RSA
#define szOID_RSA_SHA512RSA "1.2.840.113549.1.1.13"
#endif
#ifndef SCH_CRED_SNI_CREDENTIAL
#define SCH_CRED_SNI_CREDENTIAL 0x00080000
#endif
//hungarian ensures we hit no macros.
static struct
{
@ -501,9 +508,8 @@ static DWORD VerifyServerCertificate(PCCERT_CONTEXT pServerCert, PWSTR pwszServe
crypt.pCertFreeCertificateChain(pChainContext);
}
return Status;
return Status;
}
static PCCERT_CONTEXT SSPI_GetServerCertificate(void)
{
static PCCERT_CONTEXT ret;
@ -516,8 +522,8 @@ static PCCERT_CONTEXT SSPI_GetServerCertificate(void)
if (ret)
return ret;
memset(&sigalg, 0, sizeof(sigalg));
sigalg.pszObjId = szOID_RSA_SHA1RSA;
memset(&sigalg, 0, sizeof(sigalg));
sigalg.pszObjId = szOID_RSA_SHA512RSA;
GetSystemTime(&expiredate);
expiredate.wYear += 2; //2 years hence. woo
@ -538,6 +544,20 @@ static PCCERT_CONTEXT SSPI_GetServerCertificate(void)
&expiredate,
NULL
);
if (!ret)
{ //try and downgrade the signature algo if it failed.
sigalg.pszObjId = szOID_RSA_SHA1RSA;
ret = crypt.pCertCreateSelfSignCertificate(
0,
&issuerblob,
0,
NULL,
&sigalg,
NULL,
&expiredate,
NULL
);
}
Z_Free(issuerblob.pbData);
return ret;
@ -578,12 +598,15 @@ static void SSPI_Handshake (sslfile_t *f)
SECURITY_STATUS ss;
TimeStamp Lifetime;
SecBufferDesc OutBuffDesc;
SecBuffer OutSecBuff;
SecBuffer OutSecBuff[2];
SecBufferDesc InBuffDesc;
SecBuffer InSecBuff[2];
SecBuffer InSecBuff[3];
ULONG ContextAttributes;
SCHANNEL_CRED SchannelCred;
char buf1[128];
char buf2[128];
if (f->outcrypt.avail)
{
//don't let things build up too much
@ -595,12 +618,16 @@ static void SSPI_Handshake (sslfile_t *f)
//FIXME: skip this if we've had no new data since last time
OutBuffDesc.ulVersion = SECBUFFER_VERSION;
OutBuffDesc.cBuffers = 1;
OutBuffDesc.pBuffers = &OutSecBuff;
OutBuffDesc.cBuffers = 2;
OutBuffDesc.pBuffers = OutSecBuff;
OutSecBuff.cbBuffer = f->outcrypt.datasize - f->outcrypt.avail;
OutSecBuff.BufferType = SECBUFFER_TOKEN;
OutSecBuff.pvBuffer = f->outcrypt.data + f->outcrypt.avail;
OutSecBuff[0].cbBuffer = f->outcrypt.datasize - f->outcrypt.avail;
OutSecBuff[0].BufferType = SECBUFFER_TOKEN;
OutSecBuff[0].pvBuffer = f->outcrypt.data + f->outcrypt.avail;
OutSecBuff[1].BufferType = 16;//SECBUFFER_TARGET_HOST;
OutSecBuff[1].pvBuffer = buf1;
OutSecBuff[1].cbBuffer = sizeof(buf1);
if (f->handshaking == HS_ERROR)
return; //gave up.
@ -612,7 +639,7 @@ static void SSPI_Handshake (sslfile_t *f)
memset(&SchannelCred, 0, sizeof(SchannelCred));
SchannelCred.dwVersion = SCHANNEL_CRED_VERSION;
SchannelCred.grbitEnabledProtocols = f->datagram?USE_PROT_DGRAM_CLIENT:USE_PROT_CLIENT;
SchannelCred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; /*don't use windows login info or anything*/
SchannelCred.dwFlags |= SCH_CRED_SNI_CREDENTIAL | SCH_CRED_NO_DEFAULT_CREDS; /*don't use windows login info or anything*/
ss = secur.pAcquireCredentialsHandleA (NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &SchannelCred, NULL, NULL, &f->cred, &Lifetime);
if (ss < 0)
@ -665,7 +692,7 @@ static void SSPI_Handshake (sslfile_t *f)
return;
InBuffDesc.ulVersion = SECBUFFER_VERSION;
InBuffDesc.cBuffers = 2;
InBuffDesc.cBuffers = 3;
InBuffDesc.pBuffers = InSecBuff;
InSecBuff[0].BufferType = SECBUFFER_TOKEN;
@ -676,6 +703,10 @@ static void SSPI_Handshake (sslfile_t *f)
InSecBuff[1].pvBuffer = NULL;
InSecBuff[1].cbBuffer = 0;
InSecBuff[2].BufferType = 16;//SECBUFFER_TARGET_HOST;
InSecBuff[2].pvBuffer = buf2;
InSecBuff[2].cbBuffer = sizeof(buf2);
ss = secur.pAcceptSecurityContext(&f->cred, (f->handshaking==HS_SERVER)?&f->sechnd:NULL, &InBuffDesc, ASC_REQ_ALLOCATE_MEMORY|ASC_REQ_STREAM|ASC_REQ_CONFIDENTIALITY, SECURITY_NATIVE_DREP, &f->sechnd, &OutBuffDesc, &ContextAttributes, &Lifetime);
if (ss == SEC_E_INCOMPLETE_MESSAGE)
@ -727,7 +758,7 @@ static void SSPI_Handshake (sslfile_t *f)
}
}
if (SSPI_CopyIntoBuffer(&f->outcrypt, OutSecBuff.pvBuffer, OutSecBuff.cbBuffer, true) < OutSecBuff.cbBuffer)
if (SSPI_CopyIntoBuffer(&f->outcrypt, OutSecBuff[0].pvBuffer, OutSecBuff[0].cbBuffer, true) < OutSecBuff[0].cbBuffer)
{
SSPI_Error(f, "crypt overflow\n");
return;
@ -855,12 +886,13 @@ static qboolean QDECL SSPI_Close (struct vfsfile_s *file)
}
#include <wchar.h>
vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server, qboolean datagram)
vfsfile_t *FS_OpenSSL(const char *servername, vfsfile_t *source, qboolean server, qboolean datagram)
{
sslfile_t *newf;
int i = 0;
int err;
unsigned int c;
const char *localname, *peername;
if (!source || !SSL_Inited())
{
@ -868,8 +900,16 @@ vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server,
VFS_CLOSE(source);
return NULL;
}
if (!hostname)
hostname = "";
if (server)
{
localname = servername;
peername = "";
}
else
{
localname = "";
peername = servername;
}
/*
if (server) //unsupported
@ -880,9 +920,9 @@ vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server,
*/
newf = Z_Malloc(sizeof(*newf));
while(*hostname)
while(*peername)
{
c = utf8_decode(&err, hostname, (void*)&hostname);
c = utf8_decode(&err, peername, (void*)&peername);
if (c > WCHAR_MAX)
err = true; //no 16bit surrogates. they're evil.
else if (i == sizeof(newf->wpeername)/sizeof(newf->wpeername[0]) - 1)
@ -897,6 +937,7 @@ vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server,
}
}
newf->wpeername[i] = 0;
newf->datagram = datagram;
newf->handshaking = server?HS_STARTSERVER:HS_STARTCLIENT;
newf->stream = source;
@ -907,7 +948,7 @@ vfsfile_t *FS_OpenSSL(const char *hostname, vfsfile_t *source, qboolean server,
newf->funcs.Seek = SSPI_Seek;
newf->funcs.Tell = SSPI_Tell;
newf->funcs.WriteBytes = SSPI_WriteBytes;
newf->funcs.seekingisabadplan = true;
newf->funcs.seekstyle = SS_UNSEEKABLE;
SSPI_ExpandBuffer(&newf->outraw, 8192);
SSPI_ExpandBuffer(&newf->outcrypt, 8192);

File diff suppressed because it is too large Load Diff

View File

@ -253,19 +253,21 @@ typedef struct
{
struct icestate_s *(QDECL *ICE_Create)(void *module, const char *conname, const char *peername, enum icemode_e mode, enum iceproto_e proto); //doesn't start pinging anything.
qboolean (QDECL *ICE_Set)(struct icestate_s *con, const char *prop, const char *value);
qboolean (QDECL *ICE_Get)(struct icestate_s *con, const char *prop, char *value, int valuesize);
qboolean (QDECL *ICE_Get)(struct icestate_s *con, const char *prop, char *value, size_t valuesize);
struct icecandinfo_s *(QDECL *ICE_GetLCandidateInfo)(struct icestate_s *con); //retrieves candidates that need reporting to the peer.
void (QDECL *ICE_AddRCandidateInfo)(struct icestate_s *con, struct icecandinfo_s *cand); //stuff that came from the peer.
void (QDECL *ICE_Close)(struct icestate_s *con); //bye then.
void (QDECL *ICE_CloseModule)(void *module); //closes all unclosed connections, with warning.
// struct icestate_s *(QDECL *ICE_Find)(void *module, const char *conname);
qboolean (QDECL *ICE_GetLCandidateSDP)(struct icestate_s *con, char *out, size_t valuesize); //retrieves candidates that need reporting to the peer.
} icefuncs_t;
extern icefuncs_t iceapi;
#endif
//address flags
#define ADDR_NATPMP (1u<<0)
#define ADDR_UPNPIGP (1u<<1)
#define ADDR_REFLEX (1u<<2) //as reported by some external server.
#define FTENET_ADDRTYPES 2
typedef struct ftenet_generic_connection_s {
@ -277,8 +279,9 @@ typedef struct ftenet_generic_connection_s {
neterr_t (*SendPacket)(struct ftenet_generic_connection_s *con, int length, const void *data, netadr_t *to);
void (*Close)(struct ftenet_generic_connection_s *con);
#ifdef HAVE_PACKET
int (*SetReceiveFDSet) (struct ftenet_generic_connection_s *con, fd_set *fdset); /*set for connections which have multiple sockets (ie: listening tcp connections)*/
int (*SetFDSets) (struct ftenet_generic_connection_s *con, fd_set *readfdset, fd_set *writefdset); /*set for connections which have multiple sockets (ie: listening tcp connections)*/
#endif
void (*PrintStatus)(struct ftenet_generic_connection_s *con);
netadrtype_t addrtype[FTENET_ADDRTYPES];
qboolean islisten;
@ -312,8 +315,8 @@ void QDECL ICE_AddLCandidateInfo(struct icestate_s *con, netadr_t *adr, int adrn
ftenet_connections_t *FTENET_CreateCollection(qboolean listen);
void FTENET_CloseCollection(ftenet_connections_t *col);
qboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *name, const char *address, netadrtype_t addrtype, qboolean islisten);
int NET_EnumerateAddresses(ftenet_connections_t *collection, struct ftenet_generic_connection_s **con, int *adrflags, netadr_t *addresses, int maxaddresses);
qboolean FTENET_AddToCollection(struct ftenet_connections_s *col, const char *name, const char *address, netadrtype_t addrtype, netproto_t addrprot, qboolean islisten);
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, qboolean datagram);
#ifdef HAVE_PACKET

View File

@ -1170,7 +1170,7 @@ qintptr_t VARGS Plug_FS_Open(void *offset, quintptr_t mask, const qintptr_t *arg
handle = Plug_NewStreamHandle(STREAM_WEB);
pluginstreamarray[handle].dl = HTTP_CL_Get(fname, NULL, Plug_DownloadComplete);
pluginstreamarray[handle].dl->user_num = handle;
pluginstreamarray[handle].dl->file = pluginstreamarray[handle].vfs = VFSPIPE_Open();
pluginstreamarray[handle].dl->file = pluginstreamarray[handle].vfs = VFSPIPE_Open(2, true);
pluginstreamarray[handle].dl->isquery = true;
#ifdef MULTITHREAD
DL_CreateThread(pluginstreamarray[handle].dl, NULL, NULL);
@ -1187,6 +1187,7 @@ qintptr_t VARGS Plug_FS_Open(void *offset, quintptr_t mask, const qintptr_t *arg
return -1;
handle = Plug_NewStreamHandle(STREAM_VFS);
pluginstreamarray[handle].vfs = f;
Q_strncpyz(pluginstreamarray[handle].file.filename, fname, sizeof(pluginstreamarray[handle].file.filename));
*ret = handle;
return VFS_GETLEN(pluginstreamarray[handle].vfs);
}
@ -1325,7 +1326,7 @@ void Plug_Net_Close_Internal(int handle)
case STREAM_VFS:
if (pluginstreamarray[handle].vfs)
{
if (!pluginstreamarray[handle].vfs->seekingisabadplan && pluginstreamarray[handle].vfs->WriteBytes)
if (*pluginstreamarray[handle].file.filename && pluginstreamarray[handle].vfs->WriteBytes)
{
VFS_CLOSE(pluginstreamarray[handle].vfs);
FS_FlushFSHashWritten(pluginstreamarray[handle].file.filename);
@ -1949,7 +1950,7 @@ qboolean Plug_ServerMessage(char *buffer, int messagelevel)
qboolean ret = true;
Cmd_TokenizeString(buffer, false, false);
Cmd_Args_Set(buffer);
Cmd_Args_Set(buffer, strlen(buffer));
for (currentplug = plugs; currentplug; currentplug = currentplug->next)
{
@ -1959,7 +1960,7 @@ qboolean Plug_ServerMessage(char *buffer, int messagelevel)
}
}
Cmd_Args_Set(NULL);
Cmd_Args_Set(NULL, 0);
return ret; // true to display message, false to supress
}
@ -1969,7 +1970,7 @@ qboolean Plug_ChatMessage(char *buffer, int talkernum, int tpflags)
qboolean ret = true;
Cmd_TokenizeString(buffer, false, false);
Cmd_Args_Set(buffer);
Cmd_Args_Set(buffer, strlen(buffer));
for (currentplug = plugs; currentplug; currentplug = currentplug->next)
{
@ -1979,7 +1980,7 @@ qboolean Plug_ChatMessage(char *buffer, int talkernum, int tpflags)
}
}
Cmd_Args_Set(NULL);
Cmd_Args_Set(NULL, 0);
return ret; // true to display message, false to supress
}
@ -1989,7 +1990,7 @@ qboolean Plug_CenterPrintMessage(char *buffer, int clientnum)
qboolean ret = true;
Cmd_TokenizeString(buffer, false, false);
Cmd_Args_Set(buffer);
Cmd_Args_Set(buffer, strlen(buffer));
for (currentplug = plugs; currentplug; currentplug = currentplug->next)
{
@ -1999,7 +2000,7 @@ qboolean Plug_CenterPrintMessage(char *buffer, int clientnum)
}
}
Cmd_Args_Set(NULL);
Cmd_Args_Set(NULL, 0);
return ret; // true to display message, false to supress
}

View File

@ -1147,7 +1147,9 @@ void PM_NudgePosition (void)
VectorCopy (pmove.origin, base);
for (i=0 ; i<3 ; i++)
base[i] = ((int)(base[i]*8)) * 0.125;
base[i] = MSG_FromCoord(MSG_ToCoord(base[i], 2), 2);
// VectorCopy (base, pmove.origin);
//if we're moving, allow that spot without snapping to any grid
// if (pmove.velocity[0] || pmove.velocity[1] || pmove.velocity[2])

View File

@ -24,9 +24,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// for the most part, we use stdio.
// if your system doesn't have stdio then urm... well.
//
void Sys_mkdir (char *path); //not all pre-unix systems have directories (including dos 1)
qboolean Sys_remove (char *path);
qboolean Sys_Rename (char *oldfname, char *newfname);
void Sys_mkdir (const char *path); //not all pre-unix systems have directories (including dos 1)
qboolean Sys_rmdir (const char *path);
qboolean Sys_remove (const char *path);
qboolean Sys_Rename (const char *oldfname, const char *newfname);
qboolean Sys_FindGameData(const char *poshname, const char *gamename, char *basepath, int basepathlen, qboolean allowprompts);
//

View File

@ -168,7 +168,16 @@ void Sys_DetachThread(void *thread)
void Sys_WaitOnThread(void *thread)
{
threadctx_t *ctx = thread;
#ifdef SERVERONLY
WaitForSingleObject(ctx->handle, INFINITE);
#else
while (WAIT_OBJECT_0+1 == MsgWaitForMultipleObjects(1, &ctx->handle, false, INFINITE, QS_SENDMESSAGE))
{
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
DispatchMessage (&msg);
}
#endif
CloseHandle(ctx->handle);
free(ctx);
}

View File

@ -212,6 +212,18 @@ void *Z_Malloc(int size)
}
#endif
void Z_StrCat(char **ptr, char *append)
{
size_t oldlen = *ptr?strlen(*ptr):0;
size_t newlen = strlen(append);
char *newptr = BZ_Malloc(oldlen+newlen+1);
memcpy(newptr, *ptr, oldlen);
memcpy(newptr+oldlen, append, newlen);
newptr[oldlen+newlen] = 0;
BZ_Free(*ptr);
*ptr = newptr;
}
void VARGS Z_TagFree(void *mem)
{
zone_t *zone = ((zone_t *)mem) - 1;

View File

@ -133,6 +133,8 @@ void ZG_FreeGroup(zonegroup_t *ctx);
#endif
#define Z_StrDup(s) strcpy(Z_Malloc(strlen(s)+1), s)
void Z_StrCat(char **ptr, char *append);
/*
void *Hunk_Alloc (int size); // returns 0 filled memory
void *Hunk_AllocName (int size, char *name);

View File

@ -10,6 +10,9 @@
<Platform
Name="Win32"
/>
<Platform
Name="x64"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
@ -136,6 +139,130 @@
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Debug|x64"
OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TargetEnvironment="3"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="../libs/speex,..\client,../libs/freetype2/include,../common,../server,../gl,../sw,../qclib,../libs,../libs/dxsdk7/include"
PreprocessorDefinitions="BOTLIB;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
SmallerTypeCheck="true"
RuntimeLibrary="0"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|x64"
OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TargetEnvironment="3"
/>
<Tool
Name="VCCLCompilerTool"
FavorSizeOrSpeed="0"
OmitFramePointers="true"
PreprocessorDefinitions="BOTLIB;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
RuntimeLibrary="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>

View File

@ -23,7 +23,7 @@
>
<Tool
Name="VCNMakeTool"
BuildCommandLine="cd $(InputDir)\.. &amp;&amp; vcify make droid-dbg ANDROID_HOME=C:/Games/tools/android-sdk ANDROID_NDK_ROOT=C:/Games/tools/android-ndk-r8e ANT=C:/Games/tools/apache-ant-1.8.2/bin/ant JAVATOOL=&quot;C:/Program\ Files/Java/jdk1.7.0_02/bin/&quot; -j8 DROID_ARCH=&quot;armeabi x86&quot;"
BuildCommandLine="cd $(InputDir)\.. &amp;&amp; vcify make droid-dbg ANDROID_HOME=C:/Games/tools/android-sdk ANDROID_NDK_ROOT=C:/Games/tools/android-ndk-r8e ANT=C:/Games/tools/apache-ant-1.8.2/bin/ant JAVATOOL=&quot;C:/Program\ Files/Java/jdk1.7.0_02/bin/&quot; -j8 DROID_ARCH=&quot;arm x86&quot;"
ReBuildCommandLine=""
CleanCommandLine="cd $(InputDir)\.. &amp;&amp; vcify make clean -j8"
Output=""

View File

@ -23,6 +23,9 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fs_mpq", "..\..\plugins\mpq\fs_mpq.vcproj", "{72269FEE-293D-40BC-A7AE-E429F4496869}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "httpserver", "..\http\httpserver.vcproj", "{E6BAD203-4704-4860-9C38-D4702E9CAD7D}"
ProjectSection(ProjectDependencies) = postProject
{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364} = {88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ftequake", "ftequake.vcproj", "{88BFEE0E-7BC0-43AD-9CCC-6B1A6E4C1364}"
ProjectSection(ProjectDependencies) = postProject
@ -110,7 +113,7 @@ Global
{0018E098-B12A-4E4D-9B22-6772DA287080}.Debug|x64.ActiveCfg = Release|Win32
{0018E098-B12A-4E4D-9B22-6772DA287080}.GLDebug|Win32.ActiveCfg = Debug|Win32
{0018E098-B12A-4E4D-9B22-6772DA287080}.GLDebug|Win32.Build.0 = Debug|Win32
{0018E098-B12A-4E4D-9B22-6772DA287080}.GLDebug|x64.ActiveCfg = Debug|Win32
{0018E098-B12A-4E4D-9B22-6772DA287080}.GLDebug|x64.ActiveCfg = Debug|x64
{0018E098-B12A-4E4D-9B22-6772DA287080}.GLRelease|Win32.ActiveCfg = Release|Win32
{0018E098-B12A-4E4D-9B22-6772DA287080}.GLRelease|Win32.Build.0 = Release|Win32
{0018E098-B12A-4E4D-9B22-6772DA287080}.GLRelease|x64.ActiveCfg = Release|Win32
@ -311,7 +314,8 @@ Global
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.Debug|x64.ActiveCfg = Release|Win32
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.GLDebug|Win32.ActiveCfg = Debug|Win32
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.GLDebug|Win32.Build.0 = Debug|Win32
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.GLDebug|x64.ActiveCfg = Debug|Win32
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.GLDebug|x64.ActiveCfg = Debug|x64
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.GLDebug|x64.Build.0 = Debug|x64
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.GLRelease|Win32.ActiveCfg = Release|Win32
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.GLRelease|Win32.Build.0 = Release|Win32
{32B12987-DF8C-4E40-B07C-B18586A4CA65}.GLRelease|x64.ActiveCfg = Release|Win32

View File

@ -769,7 +769,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="../libs/speex,..\client,../libs/freetype2/include,../common,../server,../gl,../sw,../qclib,../libs,../libs/dxsdk7/include,../d3d,../d3d9,../libs/dxsdk9/include"
PreprocessorDefinitions="_DEBUG;GLQUAKE;WIN32;_WINDOWS;MULTITHREAD;USE_MSVCRT_DEBUG"
PreprocessorDefinitions="_DEBUG;GLQUAKE;WIN32;_WINDOWS;BOTLIB_STATIC;MULTITHREAD;USE_MSVCRT_DEBUG"
BasicRuntimeChecks="3"
SmallerTypeCheck="true"
RuntimeLibrary="1"

View File

@ -153,7 +153,7 @@
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\client;../common;../server;../gl;../sw;../qclib;../libs;../libs/dxsdk7/include;../libs/freetype2/include;../libs/speex;../"
PreprocessorDefinitions="_DEBUG;GLQUAKE;WIN32;_WINDOWS;NPQTV;MULTITHREAD"
PreprocessorDefinitions="_DEBUG;GLQUAKE;WIN32;_WINDOWS;NPQTV;MULTITHREAD;NO_ZLIB"
RuntimeLibrary="1"
FloatingPointModel="2"
PrecompiledHeaderThrough="quakedef.h"

View File

@ -237,13 +237,14 @@ qboolean GLSCR_UpdateScreen (void)
}
char *GLVID_GetRGBInfo(int *truewidth, int *trueheight, enum uploadfmt *fmt)
char *GLVID_GetRGBInfo(int *bytestride, int *truewidth, int *trueheight, enum uploadfmt *fmt)
{ //returns a BZ_Malloced array
extern qboolean gammaworks;
int i, c;
qbyte *ret;
extern qboolean r2d_canhwgamma;
*bytestride = 0;
*truewidth = vid.fbpwidth;
*trueheight = vid.fbpheight;
@ -277,7 +278,8 @@ char *GLVID_GetRGBInfo(int *truewidth, int *trueheight, enum uploadfmt *fmt)
//total line byte length must be aligned to GL_PACK_ALIGNMENT. by reading rgba instead of rgb, we can ensure the line is a multiple of 4 bytes.
ret = BZ_Malloc((*truewidth)*(*trueheight)*4);
qglReadPixels (0, 0, (*truewidth), (*trueheight), GL_RGBA, GL_UNSIGNED_BYTE, ret);
qglReadPixels (0, 0, (*truewidth), (*trueheight), GL_RGBA, GL_UNSIGNED_BYTE, ret);
*bytestride = *truewidth*-3;
*fmt = TF_RGB24;
c = (*truewidth)*(*trueheight);
@ -296,6 +298,7 @@ char *GLVID_GetRGBInfo(int *truewidth, int *trueheight, enum uploadfmt *fmt)
*fmt = TF_BGRA32;
ret = BZ_Malloc((*truewidth)*(*trueheight)*4);
qglReadPixels (0, 0, (*truewidth), (*trueheight), GL_BGRA_EXT, GL_UNSIGNED_INT_8_8_8_8_REV, ret);
*bytestride = *truewidth*-4;
}
#endif
else
@ -303,6 +306,7 @@ char *GLVID_GetRGBInfo(int *truewidth, int *trueheight, enum uploadfmt *fmt)
*fmt = TF_RGB24;
ret = BZ_Malloc((*truewidth)*(*trueheight)*3);
qglReadPixels (0, 0, (*truewidth), (*trueheight), GL_RGB, GL_UNSIGNED_BYTE, ret);
*bytestride = *truewidth*-3;
}
if (gammaworks && r2d_canhwgamma)

View File

@ -52,7 +52,10 @@ static enum
MODE_EGL,
#endif
#ifdef VKQUAKE
MODE_VULKAN,
MODE_VULKAN, //proper vulkan
#ifdef USE_WGL
MODE_NVVULKAN, //vulkan accessed via nvidia's render-to-image-then-copy-to-gl-backbuffer-then-copy-that opengl extension.
#endif
#endif
} platform_rendermode;
@ -834,6 +837,94 @@ static qboolean Win32VK_AttachVulkan (rendererstate_t *info)
}
#endif
#if defined(VKQUAKE) && defined(USE_WGL)
#define GLuint64 quint64_t
#define GLchar char
static PFN_vkVoidFunction (WINAPI *qglGetVkProcAddrNV) (const GLchar *name);
static void (WINAPI *qglDrawVkImageNV) (GLuint64 vkImage, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1);
static void (WINAPI *qglWaitVkSemaphoreNV) (GLuint64 vkSemaphore);
static void (WINAPI *qglSignalVkSemaphoreNV) (GLuint64 vkSemaphore);
static void (WINAPI *qglSignalVkFenceNV) (GLuint64 vkFence);
static PFN_vkVoidFunction VKAPI_CALL nvvkGetInstanceProcAddr(VkInstance instance, const char* pName)
{
//nvidia do not make this easy.
PFN_vkVoidFunction fnc;
// qwglMakeCurrent(maindc, baseRC);
fnc = qglGetVkProcAddrNV(pName);
// qwglMakeCurrent(maindc, NULL);
return fnc;
}
static qboolean Win32NVVK_CreateSurface(void)
{
vk.surface = VK_NULL_HANDLE;
// vk.allowsubmissionthread = false; //must come on the main thread, because that's the one with the gl context.
//I seem to be getting crashes on vulkan's fences if I try giving ownership of the gl context to a different thread (instead of main).
return true;
}
static void Win32NVVK_Present(struct vkframe *theframe)
{
SendMessage(mainwindow, WM_USER+8, 0, (LPARAM)theframe);
}
static void Win32NVVK_DoPresent(struct vkframe *theframe)
{
VkFence fence;
RSpeedLocals();
if (!theframe)
return; //this is used to ensure some presentation thread has woken up. we're not threading this, hopefully the gl server will do any of that that's needed.
RSpeedRemark();
//this might be a submission thread, so make sure we're talking to the right opengl context...
// qwglMakeCurrent(maindc, baseRC);
//get the gl driver to wait for the vk driver to finish rendering the frame
qglWaitVkSemaphoreNV(theframe->backbuf->presentsemaphore);
//tell the gl driver to copy it over now
qglDrawVkImageNV(theframe->backbuf->colour.image, theframe->backbuf->colour.sampler,
0, 0, vid.pixelwidth, vid.pixelheight, //xywh (window coords)
0, //z
0, 1, 1, 0); //stst (remember that gl textures are meant to be upside down)
//and tell our code to expect it.
vk.acquirebufferidx[vk.aquirelast%ACQUIRELIMIT] = vk.aquirelast%vk.backbuf_count;
fence = vk.acquirefences[vk.aquirelast%ACQUIRELIMIT];
vk.aquirelast++;
//and actually signal it, so our code can wake up.
qglSignalVkFenceNV(fence);
//and the gl driver has its final image and should do something with it now.
qSwapBuffers(maindc);
// qwglMakeCurrent(maindc, NULL);
RSpeedEnd(RSPEED_PRESENT);
}
static qboolean WGL_CheckExtension(char *extname);
static qboolean Win32NVVK_AttachVulkan (rendererstate_t *info)
{ //make sure we can get a valid renderer.
if (!GL_CheckExtension("GL_NV_draw_vulkan_image"))
{
Con_Printf("GL_NV_draw_vulkan_image is not supported. Try using real vulkan instead.\n");
return false;
}
qglGetVkProcAddrNV = getglfunc("glGetVkProcAddrNV");
qglDrawVkImageNV = getglfunc("glDrawVkImageNV");
qglWaitVkSemaphoreNV = getglfunc("glWaitVkSemaphoreNV");
qglSignalVkSemaphoreNV = getglfunc("glSignalVkSemaphoreNV");
qglSignalVkFenceNV = getglfunc("glSignalVkFenceNV");
vkGetInstanceProcAddr = nvvkGetInstanceProcAddr;
// qwglMakeCurrent(maindc, NULL);
return VK_Init(info, NULL, Win32NVVK_CreateSurface, Win32NVVK_Present);
}
#endif
/*doesn't consider parent offsets*/
static RECT centerrect(unsigned int parentleft, unsigned int parenttop, unsigned int parentwidth, unsigned int parentheight, unsigned int cwidth, unsigned int cheight)
{
@ -1343,6 +1434,9 @@ static int GLVID_SetMode (rendererstate_t *info, unsigned char *palette)
{
#ifdef USE_WGL
case MODE_WGL:
#ifdef VKQUAKE
case MODE_NVVULKAN:
#endif
// Set either the fullscreen or windowed mode
qwglChoosePixelFormatARB = NULL;
qwglCreateContextAttribsARB = NULL;
@ -1383,6 +1477,13 @@ static int GLVID_SetMode (rendererstate_t *info, unsigned char *palette)
return false;
}
}
#ifdef VKQUAKE
if (platform_rendermode == MODE_NVVULKAN && stat)
{
stat = Win32NVVK_AttachVulkan(info);
}
#endif
break;
#endif
#ifdef USE_EGL
@ -1490,6 +1591,13 @@ void VID_UnSetMode (void)
switch(platform_rendermode)
{
#if defined(VKQUAKE) && defined(USE_WGL)
case MODE_NVVULKAN:
qwglMakeCurrent(maindc, baseRC);
VK_Shutdown();
ReleaseGL();
break;
#endif
#ifdef USE_WGL
case MODE_WGL:
ReleaseGL();
@ -1596,10 +1704,10 @@ static void VID_UpdateWindowStatus (HWND hWnd)
switch(platform_rendermode)
{
#ifdef VKQUAKE
case MODE_NVVULKAN:
case MODE_VULKAN:
if (vid.pixelwidth != window_width || vid.pixelheight != window_height)
vk.neednewswapchain = true;
break;
#endif
default:
vid.pixelwidth = window_width;
@ -2708,6 +2816,11 @@ static LONG WINAPI GLMainWndProc (
case WM_USER+7:
VK_DoPresent((struct vkframe*)lParam);
break;
#endif
#if defined(VKQUAKE) && defined(USE_WGL)
case WM_USER+8:
Win32NVVK_DoPresent((struct vkframe*)lParam);
break;
#endif
case WM_USER+4:
PostQuitMessage(0);
@ -2815,6 +2928,31 @@ static LONG WINAPI GLMainWndProc (
}
break;
case WM_DROPFILES:
{
HDROP p = (HDROP)wParam;
wchar_t fnamew[MAX_PATH];
char fname[MAX_PATH];
vfsfile_t *f;
int i, count = DragQueryFile(p, ~0, NULL, 0);
for(i = 0; i < count; i++)
{
if (WinNT)
{
DragQueryFileW(p, i, fnamew, countof(fnamew));
narrowen(fname, sizeof(fname), fnamew);
}
else
DragQueryFileA(p, i, fname, countof(fname));
f = FS_OpenVFS(fname, "rb", FS_SYSTEM);
if (f)
Host_RunFile(fname, strlen(fname), f);
}
DragFinish(p);
return 0; //An application should return zero if it processes this message.
}
break;
#ifdef HAVE_CDPLAYER
case MM_MCINOTIFY:
#ifdef WTHREAD
@ -2935,6 +3073,8 @@ qboolean Win32VID_Init (rendererstate_t *info, unsigned char *palette, int mode)
vid_initialized = true;
vid_initializing = false;
WIN_WindowCreated(mainwindow);
return true;
}
@ -3084,6 +3224,69 @@ rendererinfo_t vkrendererinfo =
"no more"
};
static qboolean NVVKVID_Init (rendererstate_t *info, unsigned char *palette)
{
return Win32VID_Init(info, palette, MODE_NVVULKAN);
}
rendererinfo_t nvvkrendererinfo =
{
"Vulkan (nvidia workaround)",
{
"nvvk",
},
QR_VULKAN,
VK_Draw_Init,
VK_Draw_Shutdown,
VK_UpdateFiltering,
VK_LoadTextureMips,
VK_DestroyTexture,
VK_R_Init,
VK_R_DeInit,
VK_R_RenderView,
NVVKVID_Init,
GLVID_DeInit,
GLVID_SwapBuffers,
GLVID_ApplyGammaRamps,
WIN_CreateCursor,
WIN_SetCursor,
WIN_DestroyCursor,
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"
};
#endif
#endif

View File

@ -10004,7 +10004,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND
"#define tcbase tcoffsetmap\n"
"#endif\n"
"#if defined(FLAT)\n"
"vec4 bases = vec3(FLAT, 1.0);\n"
"vec4 bases = vec4(FLAT, FLAT, FLAT, 1.0);\n"
"#else\n"
"vec4 bases = texture2D(s_diffuse, tcbase);\n"
"#ifdef VERTEXCOLOURS\n"

File diff suppressed because it is too large Load Diff

View File

@ -20,15 +20,18 @@ static struct dl_download *activedownloads;
#include <emscripten/emscripten.h>
#endif
vfsfile_t *FSWEB_OpenTempHandle(int f);
static void DL_Cancel(struct dl_download *dl)
{
//FIXME: clear out the callbacks somehow
dl->ctx = NULL;
}
static void DL_OnLoad(void *c, void *data, int datasize)
static void DL_OnLoad(void *c, int buf)
{
//also fires from 404s.
struct dl_download *dl = c;
vfsfile_t *tempfile = FSWEB_OpenTempHandle(buf);
//make sure the file is 'open'.
if (!dl->file)
@ -40,20 +43,38 @@ static void DL_OnLoad(void *c, void *data, int datasize)
}
else
{
//emscripten does not close the file. plus we seem to end up with infinite loops.
dl->file = FS_OpenTemp();
dl->file = tempfile;
tempfile = NULL;
}
}
if (dl->file)
{
VFS_WRITE(dl->file, data, datasize);
VFS_SEEK(dl->file, 0);
dl->status = DL_FINISHED;
if (tempfile)
{
qofs_t datasize = VFS_GETLEN(tempfile);
char *data = malloc(datasize); //grab a temp buffer so we can do the entire file at once...
if (!data)
dl->status = DL_FAILED;
else
{
VFS_READ(tempfile, data, datasize);
VFS_WRITE(dl->file, data, datasize);
if (dl->file->seekstyle < SS_PIPE)
VFS_SEEK(dl->file, 0);
free(data);
}
}
}
else
dl->status = DL_FAILED;
if (tempfile)
VFS_CLOSE(tempfile);
dl->replycode = 200;
#if !MYJS
dl->completed += datasize;
@ -1504,7 +1525,7 @@ void DL_Close(struct dl_download *dl)
if (dl->threadctx)
Sys_WaitOnThread(dl->threadctx);
#endif
if (dl->file && dl->file->Seek)
if (dl->file && dl->file->seekstyle < SS_PIPE)
VFS_SEEK(dl->file, 0);
if (dl->notifycomplete)
dl->notifycomplete(dl);
@ -1706,17 +1727,27 @@ typedef struct
char *data;
int maxlen;
int writepos;
int readpos;
unsigned int writepos;
unsigned int readpos;
void *mutex;
int refs;
qboolean terminate; //one end has closed, make the other report failures now that its no longer needed.
} vfspipe_t;
static qboolean QDECL VFSPIPE_Close(vfsfile_t *f)
{
int r;
vfspipe_t *p = (vfspipe_t*)f;
free(p->data);
Sys_DestroyMutex(p->mutex);
free(p);
Sys_LockMutex(p->mutex);
r = --p->refs;
p->terminate = true;
Sys_UnlockMutex(p->mutex);
if (!r)
{
free(p->data);
Sys_DestroyMutex(p->mutex);
free(p);
}
return true;
}
static qofs_t QDECL VFSPIPE_GetLen(vfsfile_t *f)
@ -1724,21 +1755,33 @@ static qofs_t QDECL VFSPIPE_GetLen(vfsfile_t *f)
vfspipe_t *p = (vfspipe_t*)f;
return p->writepos - p->readpos;
}
//static unsigned long QDECL VFSPIPE_Tell(vfsfile_t *f)
//{
// return 0;
//}
//static qboolean QDECL VFSPIPE_Seek(vfsfile_t *f, unsigned long offset)
//{
// Con_Printf("Seeking is a bad plan, mmkay?\n");
// return false;
//}
static qofs_t QDECL VFSPIPE_Tell(vfsfile_t *f)
{
vfspipe_t *p = (vfspipe_t*)f;
return p->readpos;
}
static qboolean QDECL VFSPIPE_Seek(vfsfile_t *f, qofs_t offset)
{
vfspipe_t *p = (vfspipe_t*)f;
p->readpos = offset;
return true;
}
static int QDECL VFSPIPE_ReadBytes(vfsfile_t *f, void *buffer, int len)
{
vfspipe_t *p = (vfspipe_t*)f;
Sys_LockMutex(p->mutex);
if (p->readpos > p->writepos)
len = 0;
if (len > p->writepos - p->readpos)
{
len = p->writepos - p->readpos;
if (!len && p->terminate)
{ //if we reached eof, we started with two refs and are down to 1 and we're reading, then its the writer side that disconnected.
//eof is now fatal rather than 'try again later'.
Sys_UnlockMutex(p->mutex);
return -1;
}
}
memcpy(buffer, p->data+p->readpos, len);
p->readpos += len;
Sys_UnlockMutex(p->mutex);
@ -1748,8 +1791,14 @@ static int QDECL VFSPIPE_WriteBytes(vfsfile_t *f, const void *buffer, int len)
{
vfspipe_t *p = (vfspipe_t*)f;
Sys_LockMutex(p->mutex);
if (p->readpos > 8192)
if (p->terminate)
{ //if we started with 2 refs, and we're down to one, and we're writing, then its the reader that closed. that means writing is redundant and we should signal an error.
Sys_UnlockMutex(p->mutex);
return -1;
}
if (p->readpos > 8192 && !p->funcs.Seek)
{ //don't grow infinitely if we're reading+writing at the same time
//if we're seekable, then we have to buffer the ENTIRE file.
memmove(p->data, p->data+p->readpos, p->writepos-p->readpos);
p->writepos -= p->readpos;
p->readpos = 0;
@ -1759,6 +1808,16 @@ static int QDECL VFSPIPE_WriteBytes(vfsfile_t *f, const void *buffer, int len)
p->maxlen = p->writepos + len;
if (p->maxlen < (p->writepos-p->readpos)*2) //over-allocate a little
p->maxlen = (p->writepos-p->readpos)*2;
if (p->maxlen > 0x8000000)
{
p->maxlen = 0x8000000;
len = p->maxlen - p->writepos;
if (!len)
{
Sys_UnlockMutex(p->mutex);
return -1; //try and get the caller to stop
}
}
p->data = realloc(p->data, p->maxlen);
}
memcpy(p->data+p->writepos, buffer, len);
@ -1767,10 +1826,12 @@ static int QDECL VFSPIPE_WriteBytes(vfsfile_t *f, const void *buffer, int len)
return len;
}
vfsfile_t *VFSPIPE_Open(void)
vfsfile_t *VFSPIPE_Open(int refs, qboolean seekable)
{
vfspipe_t *newf;
newf = malloc(sizeof(*newf));
newf->refs = refs;
newf->terminate = false;
newf->mutex = Sys_CreateMutex();
newf->data = NULL;
newf->maxlen = 0;
@ -1780,10 +1841,20 @@ vfsfile_t *VFSPIPE_Open(void)
newf->funcs.Flush = NULL;
newf->funcs.GetLen = VFSPIPE_GetLen;
newf->funcs.ReadBytes = VFSPIPE_ReadBytes;
newf->funcs.Seek = NULL;//VFSPIPE_Seek;
newf->funcs.Tell = NULL;//VFSPIPE_Tell;
newf->funcs.WriteBytes = VFSPIPE_WriteBytes;
newf->funcs.seekingisabadplan = true;
if (seekable)
{ //if this is set, then we allow changing the readpos at the expense of buffering the ENTIRE file. no more fifo.
newf->funcs.Seek = VFSPIPE_Seek;
newf->funcs.Tell = VFSPIPE_Tell;
newf->funcs.seekstyle = SS_PIPE;
}
else
{ //periodically reclaim read data to avoid memory wastage. this means that seeking can't work.
newf->funcs.Seek = NULL;
newf->funcs.Tell = NULL;
newf->funcs.seekstyle = SS_UNSEEKABLE;
}
return &newf->funcs;
}

View File

@ -129,17 +129,43 @@ typedef enum {HTTP_WAITINGFORREQUEST,HTTP_SENDING} http_mode_t;
qboolean HTTP_ServerInit(int port)
{
struct sockaddr_in address;
struct sockaddr_qstorage address;
unsigned long _true = true;
int i;
if ((httpserversocket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
memset(&address, 0, sizeof(address));
//check for interface binding option. this also forces ipv4, oh well.
if ((i = COM_CheckParm("-ip")) != 0 && i < com_argc)
{
((struct sockaddr_in*)&address)->sin_addr.s_addr = inet_addr(com_argv[i+1]);
Con_TPrintf("Binding to IP Interface Address of %s\n",
inet_ntoa(((struct sockaddr_in*)&address)->sin_addr));
((struct sockaddr_in*)&address)->sin_family = AF_INET;
if (port != PORT_ANY)
((struct sockaddr_in*)&address)->sin_port = htons((short)port);
}
else
{ //otherwise just use ipv6
((struct sockaddr_in6*)&address)->sin6_family = AF_INET6;
if (port != PORT_ANY)
((struct sockaddr_in6*)&address)->sin6_port = htons((short)port);
}
if ((httpserversocket = socket (((struct sockaddr*)&address)->sa_family, SOCK_STREAM, IPPROTO_TCP)) == -1)
{
IWebPrintf ("HTTP_ServerInit: socket: %s\n", strerror(neterrno()));
httpserverfailed = true;
return false;
}
if (((struct sockaddr_in6*)&address)->sin6_family == AF_INET6 && !memcmp(((struct sockaddr_in6*)&address)->sin6_addr.s6_addr, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16))
{ //in6addr_any, allow ipv4 too, if we can do hybrid sockets.
unsigned long v6only = false;
setsockopt(httpserversocket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, sizeof(v6only));
}
if (ioctlsocket (httpserversocket, FIONBIO, &_true) == -1)
{
IWebPrintf ("HTTP_ServerInit: ioctl FIONBIO: %s\n", strerror(neterrno()));
@ -147,22 +173,6 @@ qboolean HTTP_ServerInit(int port)
return false;
}
address.sin_family = AF_INET;
//check for interface binding option
if ((i = COM_CheckParm("-ip")) != 0 && i < com_argc)
{
address.sin_addr.s_addr = inet_addr(com_argv[i+1]);
Con_TPrintf("Binding to IP Interface Address of %s\n",
inet_ntoa(address.sin_addr));
}
else
address.sin_addr.s_addr = INADDR_ANY;
if (port == PORT_ANY)
address.sin_port = 0;
else
address.sin_port = htons((short)port);
if( bind (httpserversocket, (void *)&address, sizeof(address)) == -1)
{
closesocket(httpserversocket);
@ -697,23 +707,6 @@ notimplemented:
}
}
#ifdef WEBSVONLY
void VARGS Q_snprintfz (char *dest, size_t size, const char *fmt, ...)
{
va_list args;
va_start (args, fmt);
#ifdef _WIN32
#undef _vsnprintf
_vsnprintf (dest, size-1, fmt, args);
#else
vsnprintf (dest, size-1, fmt, args);
#endif
va_end (args);
//make sure its terminated.
dest[size-1] = 0;
}
#endif
qboolean HTTP_ServerPoll(qboolean httpserverwanted, int portnum) //loop while true
{
struct sockaddr_qstorage from;
@ -773,16 +766,7 @@ qboolean HTTP_ServerPoll(qboolean httpserverwanted, int portnum) //loop while tr
}
cl = IWebMalloc(sizeof(HTTP_active_connections_t));
#ifndef WEBSVONLY
{
netadr_t na;
SockadrToNetadr(&from, &na);
NET_AdrToString(cl->peername, sizeof(cl->peername), &na);
}
#else
Q_snprintfz(cl->peername, sizeof(cl->peername), "%s:%i", inet_ntoa(((struct sockaddr_in*)&from)->sin_addr), ntohs(((struct sockaddr_in*)&from)->sin_port));
#endif
NET_SockadrToString(cl->peername, sizeof(cl->peername), &from);
IWebPrintf("%s: New http connection\n", cl->peername);
cl->datasock = clientsock;

View File

@ -4,10 +4,9 @@
#ifdef WEBSERVER
#ifdef WEBSVONLY
#include "quakedef.h"
//When running standalone
#define Con_TPrintf IWebPrintf
void VARGS IWebDPrintf(char *fmt, ...) LIKEPRINTF(1);
#define IWebPrintf printf
#define com_gamedir "." //current dir.
@ -15,6 +14,16 @@
#define IWebMalloc(x) calloc(x, 1)
#define IWebRealloc(x, y) realloc(x, y)
#define IWebFree free
#else
//Inside FTE
#define IWebDPrintf Con_DPrintf
#define IWebPrintf Con_Printf
#define IWebMalloc Z_Malloc
#define IWebRealloc BZF_Realloc
#define IWebFree Z_Free
void VARGS IWebWarnPrintf(char *fmt, ...) LIKEPRINTF(1);
#endif
#define IWEBACC_READ 1
@ -30,13 +39,6 @@ qboolean SV_AllowDownload (const char *name);
typedef qboolean iwboolean;
//it's not allowed to error.
#ifndef WEBSVONLY
void VARGS IWebDPrintf(char *fmt, ...) LIKEPRINTF(1);
void VARGS IWebPrintf(char *fmt, ...) LIKEPRINTF(1);
void VARGS IWebWarnPrintf(char *fmt, ...) LIKEPRINTF(1);
#endif
typedef struct {
float gentime; //useful for generating a new file (if too old, removes reference)
int references; //freed if 0
@ -44,18 +46,11 @@ typedef struct {
int len;
} IWeb_FileGen_t;
#ifndef WEBSVONLY
void *IWebMalloc(int size);
void *IWebRealloc(void *old, int size);
void IWebFree(void *mem);
#define IWebFree Z_Free
#endif
int IWebAuthorize(char *name, char *password);
iwboolean IWebAllowUpLoad(char *fname, char *uname);
vfsfile_t *IWebGenerateFile(char *name, char *content, int contentlength);
int IWebAuthorize(const char *name, const char *password);
iwboolean IWebAllowUpLoad(const char *fname, const char *uname);
vfsfile_t *IWebGenerateFile(const char *name, const char *content, int contentlength);
int IWebGetSafeListeningPort(void);
//char *COM_ParseOut (const char *data, char *out, int outlen);
//struct searchpath_s;
@ -65,10 +60,6 @@ vfsfile_t *IWebGenerateFile(char *name, char *content, int contentlength);
char *Q_strcpyline(char *out, const char *in, int maxlen);
iwboolean FTP_StringToAdr (const char *s, qbyte ip[4], qbyte port[2]);
//server tick/control functions
iwboolean FTP_ServerRun(iwboolean ftpserverwanted, int port);
qboolean HTTP_ServerPoll(qboolean httpserverwanted, int port);
@ -145,7 +136,7 @@ struct dl_download
void (*notifycomplete) (struct dl_download *dl);
};
vfsfile_t *VFSPIPE_Open(void);
vfsfile_t *VFSPIPE_Open(int refs, qboolean seekable); //refs should be 1 or 2, to say how many times it must be closed before its actually closed, so both ends can close separately
void HTTP_CL_Think(void);
void HTTP_CL_Terminate(void); //kills all active downloads
unsigned int HTTP_CL_GetActiveDownloads(void);

View File

@ -7,6 +7,47 @@
#ifdef WEBSVONLY //we need some functions from quake
char *NET_SockadrToString(char *s, int slen, struct sockaddr_qstorage *addr)
{
switch(((struct sockaddr*)addr)->sa_family)
{
case AF_INET:
Q_snprintfz(s, slen, "%s:%u", inet_ntoa(((struct sockaddr_in*)addr)->sin_addr), ntohs(((struct sockaddr_in*)addr)->sin_port));
break;
case AF_INET6:
if (!memcmp(((struct sockaddr_in6*)addr)->sin6_addr.s6_bytes, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12))
{ //ipv4-mapped
Q_snprintfz(s, slen, "[::ffff:%u.%u.%u.%u]:%u",
((struct sockaddr_in6*)addr)->sin6_addr.s6_bytes[12],
((struct sockaddr_in6*)addr)->sin6_addr.s6_bytes[13],
((struct sockaddr_in6*)addr)->sin6_addr.s6_bytes[14],
((struct sockaddr_in6*)addr)->sin6_addr.s6_bytes[15],
ntohs(((struct sockaddr_in6*)addr)->sin6_port));
}
else
{
Q_snprintfz(s, slen, "[%x:%x:%x:%x:%x:%x:%x:%x]:%u",
ntohs(((struct sockaddr_in6*)addr)->sin6_addr.s6_words[0]),
ntohs(((struct sockaddr_in6*)addr)->sin6_addr.s6_words[1]),
ntohs(((struct sockaddr_in6*)addr)->sin6_addr.s6_words[2]),
ntohs(((struct sockaddr_in6*)addr)->sin6_addr.s6_words[3]),
ntohs(((struct sockaddr_in6*)addr)->sin6_addr.s6_words[4]),
ntohs(((struct sockaddr_in6*)addr)->sin6_addr.s6_words[5]),
ntohs(((struct sockaddr_in6*)addr)->sin6_addr.s6_words[6]),
ntohs(((struct sockaddr_in6*)addr)->sin6_addr.s6_words[7]),
ntohs(((struct sockaddr_in6*)addr)->sin6_port));
}
break;
default:
*s = 0;
break;
}
return s;
}
qboolean SV_AllowDownload (const char *name)
{
if (strstr(name, ".."))
@ -22,7 +63,7 @@ com_tokentype_t com_tokentype;
int com_argc;
const char **com_argv;
vfsfile_t *IWebGenerateFile(char *name, char *content, int contentlength)
vfsfile_t *IWebGenerateFile(const char *name, const char *content, int contentlength)
{
return NULL;
}
@ -47,6 +88,21 @@ void Q_strncpyz(char *d, const char *s, int n)
*d='\0';
}
void VARGS Q_snprintfz (char *dest, size_t size, const char *fmt, ...)
{
va_list args;
va_start (args, fmt);
#ifdef _WIN32
#undef _vsnprintf
_vsnprintf (dest, size-1, fmt, args);
#else
vsnprintf (dest, size-1, fmt, args);
#endif
va_end (args);
//make sure its terminated.
dest[size-1] = 0;
}
/*char *va(char *format, ...)
{
#define VA_BUFFERS 2 //power of two
@ -91,23 +147,82 @@ int COM_CheckParm(const char *parm)
char *authedusername;
char *autheduserpassword;
int lport_min, lport_max;
int anonaccess = IWEBACC_READ;
iwboolean verbose;
int main(int argc, char **argv)
{
int httpport = 80;
int ftpport = 21;
int arg = 1;
#ifdef _WIN32
WSADATA pointlesscrap;
WSAStartup(2, &pointlesscrap);
#endif
while (arg < argc)
{
char *a = argv[arg];
if (!a)
continue;
if (*a != '-')
break; //other stuff
while (*a == '-')
a++;
arg++;
if (!strcmp(a, "help"))
{
printf("%s -http 80 -ftp 21 -user steve -pass swordfish -ports 5000 6000\n", argv[0]);
printf("runs a simple http server\n");
printf(" -http <num> specifies the port to listen on for http\n");
printf(" -ftp <num> specifies the port to listen on for ftp\n");
printf(" -ports <lowest> <highest> specifies a port range for incoming ftp connections, to work around firewall rules\n");
printf(" -user <name> specifies the username that has full access. if not supplied noone can write.\n");
printf(" -pass <pass> specifies the password to go with that username\n");
printf(" -noanon will refuse to serve files to anyone but the authed user\n");
return;
}
else if (!strcmp(a, "port") || !strcmp(a, "p"))
{
httpport = atoi(argv[arg++]);
ftpport = 0;
}
else if (!strcmp(a, "http") || !strcmp(a, "h"))
httpport = atoi(argv[arg++]);
else if (!strcmp(a, "ftp") || !strcmp(a, "f"))
ftpport = atoi(argv[arg++]);
else if (!strcmp(a, "noanon"))
anonaccess = 0;
else if (!strcmp(a, "ports"))
{
lport_min = atoi(argv[arg++]);
lport_max = atoi(argv[arg++]);
if (lport_max < lport_min)
lport_max = lport_min;
}
else if (!strcmp(a, "verbose") || !strcmp(a, "v"))
verbose = true;
else if (!strcmp(a, "user"))
authedusername = argv[arg++];
else if (!strcmp(a, "pass"))
autheduserpassword = argv[arg++];
else
printf("Unknown argument: %s\n", a);
}
if (arg < argc && atoi(argv[arg]))
{
httpport = atoi(argv[arg++]);
ftpport = 0;
}
if (arg < argc)
authedusername = argv[arg++];
if (arg < argc)
autheduserpassword = argv[arg++];
printf("http port %i\n", httpport);
printf("ftp port %i\n", ftpport);
if (authedusername || autheduserpassword)
printf("Username = \"%s\"\nPassword = \"%s\"\n", authedusername, autheduserpassword);
else
@ -115,7 +230,8 @@ int main(int argc, char **argv)
while(1)
{
// FTP_ServerRun(1, 21);
if (ftpport)
FTP_ServerRun(1, ftpport);
if (httpport)
HTTP_ServerPoll(1, httpport);
#ifdef _WIN32
@ -126,6 +242,22 @@ int main(int argc, char **argv)
}
}
int IWebGetSafeListeningPort(void)
{
static int sequence;
return lport_min + (sequence++ % (lport_max+1-lport_min));
}
void VARGS IWebDPrintf(char *fmt, ...)
{
va_list args;
if (!verbose)
return;
va_start (args, fmt);
vprintf (fmt, args);
va_end (args);
}
#ifdef _WIN32
#ifdef _MSC_VER
#define ULL(x) x##ui64
@ -253,7 +385,7 @@ skipwhite:
return (char*)data;
}
#undef COM_ParseToken
/*#undef COM_ParseToken
char *COM_ParseToken (const char *data, const char *punctuation)
{
int c;
@ -335,7 +467,7 @@ skipwhite:
com_token[len] = 0;
return (char*)data;
}
}*/
/*
IWEBFILE *IWebFOpenRead(char *name) //fread(name, "rb");
@ -371,7 +503,7 @@ IWEBFILE *IWebFOpenRead(char *name) //fread(name, "rb");
#else
#ifndef CLIENTONLY
#ifdef WEBSERVER
cvar_t ftpserver = CVAR("sv_ftp", "0");
cvar_t ftpserver_port = CVAR("sv_ftp_port", "21");
cvar_t httpserver = CVAR("sv_http", "0");
@ -379,35 +511,31 @@ cvar_t httpserver_port = CVAR("sv_http_port", "80");
cvar_t sv_readlevel = CVAR("sv_readlevel", "0"); //default to allow anyone
cvar_t sv_writelevel = CVAR("sv_writelevel", "35"); //allowed to write to uploads/uname
cvar_t sv_fulllevel = CVAR("sv_fulllevel", "51"); //allowed to write anywhere, replace any file...
cvar_t sv_ftp_port_range = CVARD("sv_ftp_port_range", "0", "Specifies the port range for the server to create listening sockets for 'active' ftp connections, to work around NAT/firewall issues.\nMost FTP clients should use passive connections, but there's still some holdouts like windows.");
int IWebGetSafeListeningPort(void)
{
char *e;
int base, range;
static int sequence;
if (!*sv_ftp_port_range.string)
return 0; //lets the OS pick.
base = strtol(sv_ftp_port_range.string, &e, 0);
while(*e == ' ')
e++;
if (*e == '-')
e++;
while(*e == ' ')
e++;
range = strtol(e, NULL, 0);
if (range < base)
range = base;
return base + (sequence++ % (range+1-base));
}
#endif
//this file contains functions called from each side.
void VARGS IWebDPrintf(char *fmt, ...)
{
va_list argptr;
char msg[4096];
if (!developer.value)
return;
va_start (argptr,fmt);
vsnprintf (msg,sizeof(msg)-10, fmt,argptr); //catch any nasty bugs... (this is hopefully impossible)
va_end (argptr);
Con_Printf("%s", msg);
}
void VARGS IWebPrintf(char *fmt, ...)
{
va_list argptr;
char msg[4096];
va_start (argptr,fmt);
vsnprintf (msg,sizeof(msg)-10, fmt,argptr); //catch any nasty bugs... (this is hopefully impossible)
va_end (argptr);
Con_Printf("%s", msg);
}
void VARGS IWebWarnPrintf(char *fmt, ...)
{
va_list argptr;
@ -429,6 +557,7 @@ void IWebInit(void)
Cvar_Register(&ftpserver, "Internet Server Access");
Cvar_Register(&ftpserver_port, "Internet Server Access");
Cvar_Register(&sv_ftp_port_range, "Internet Server Access");
Cvar_Register(&httpserver, "Internet Server Access");
Cvar_Register(&httpserver_port, "Internet Server Access");
@ -467,39 +596,81 @@ void IWebShutdown(void)
}
#endif
#ifndef WEBSVONLY
//replacement for Z_Malloc. It simply allocates up to a reserve ammount.
void *IWebMalloc(int size)
#ifdef WEBSVONLY
void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize)
{
void *mem = BZF_Malloc(size);
memset(mem, 0, size);
return mem;
return NULL;
}
qboolean FS_Remove(const char *fname, enum fs_relative relativeto)
{
return false;
}
qboolean FS_NativePath(const char *fname, enum fs_relative relativeto, char *out, int outlen)
{
Q_strncpyz(out, fname, outlen);
if (*out == '/' || strstr(out, ".."))
{
*out = 0;
return false;
}
return strlen(fname) == strlen(out);
}
void FS_FlushFSHashWritten(const char *fname) {}
void FS_FlushFSHashRemoved(const char *fname) {}
qboolean FS_Rename(const char *oldf, const char *newf, enum fs_relative relativeto)
{
return rename(oldf, newf) != -1;
}
#ifdef _WIN32
#include <direct.h>
void FS_CreatePath(const char *pname, enum fs_relative relativeto)
{
_mkdir(pname);
}
qboolean Sys_rmdir (const char *path)
{
return _rmdir(path) != -1;
}
#else
#include <unistd.h>
void FS_CreatePath(const char *pname, enum fs_relative relativeto)
{
mkdir(pname);
}
qboolean Sys_rmdir (const char *path)
{
return rmdir(path) != -1;
}
#endif
void *IWebRealloc(void *old, int size)
{
return BZF_Realloc(old, size);
}
#endif
int IWebAuthorize(char *name, char *password)
int IWebAuthorize(const char *name, const char *password)
{
#ifdef WEBSVONLY
if (authedusername)
{
if (!strcmp(name, authedusername))
{
if (!strcmp(password, autheduserpassword))
return IWEBACC_FULL;
return IWEBACC_READ;
}
}
//if they tried giving some other username, don't give them any access (prevents them from reading actual user files).
if (*name && stricmp(name, "anonymous"))
return 0;
return anonaccess;
#else
#ifndef CLIENTONLY
int id = Rank_GetPlayerID(NULL, name, atoi(password), false, true);
rankinfo_t info;
if (!id)
{
if (!sv_readlevel.value)
if (!sv_readlevel.value && !*name || !stricmp(name, "anonymous"))
return IWEBACC_READ; //read only anywhere
return 0;
}
@ -517,7 +688,7 @@ int IWebAuthorize(char *name, char *password)
#endif
}
iwboolean IWebAllowUpLoad(char *fname, char *uname) //called for partial write access
iwboolean IWebAllowUpLoad(const char *fname, const char *uname) //called for partial write access
{
if (strstr(fname, ".."))
return false;
@ -530,50 +701,6 @@ iwboolean IWebAllowUpLoad(char *fname, char *uname) //called for partial write a
return false;
}
iwboolean FTP_StringToAdr (const char *s, qbyte ip[4], qbyte port[2])
{
s = COM_ParseToken(s, NULL);
ip[0] = atoi(com_token);
s = COM_ParseToken(s, NULL);
if (*com_token != ',')
return false;
s = COM_ParseToken(s, NULL);
ip[1] = atoi(com_token);
s = COM_ParseToken(s, NULL);
if (*com_token != ',')
return false;
s = COM_ParseToken(s, NULL);
ip[2] = atoi(com_token);
s = COM_ParseToken(s, NULL);
if (*com_token != ',')
return false;
s = COM_ParseToken(s, NULL);
ip[3] = atoi(com_token);
s = COM_ParseToken(s, NULL);
if (*com_token != ',')
return false;
s = COM_ParseToken(s, NULL);
port[0] = atoi(com_token);
s = COM_ParseToken(s, NULL);
if (*com_token != ',')
return false;
s = COM_ParseToken(s, NULL);
port[1] = atoi(com_token);
return true;
}
char *Q_strcpyline(char *out, const char *in, int maxlen)
{
char *w = out;

View File

@ -212,7 +212,7 @@ static void IWeb_GenerateRankingsFileCallback(const rankinfo_t *ri)
IWeb_Generate("</TR>");
}
static void IWeb_GenerateRankingsFile (char *parms, char *content, int contentlength)
static void IWeb_GenerateRankingsFile (const char *parms, const char *content, int contentlength)
{
IWeb_Generate("<HTML><HEAD></HEAD><BODY>");
@ -243,7 +243,7 @@ static void IWeb_GenerateRankingsFile (char *parms, char *content, int contentle
IWeb_Generate("</BODY></HTML>");
}
static void IWeb_GenerateIndexFile (char *parms, char *content, int contentlength)
static void IWeb_GenerateIndexFile (const char *parms, const char *content, int contentlength)
{
extern cvar_t rcon_password;
char *s, *o;
@ -338,7 +338,7 @@ static void IWeb_GenerateIndexFile (char *parms, char *content, int contentlengt
typedef struct {
char *name;
void (*GenerationFunction) (char *parms, char *content, int contentlength);
void (*GenerationFunction) (const char *parms, const char *content, int contentlength);
float lastgenerationtime;
float oldbysecs;
IWeb_FileGen_t *buffer;
@ -440,10 +440,10 @@ static vfsfile_t *VFSGen_Create(IWeb_FileGen_t *gen)
return (vfsfile_t*)ret;
}
vfsfile_t *IWebGenerateFile(char *name, char *content, int contentlength)
vfsfile_t *IWebGenerateFile(const char *name, const char *content, int contentlength)
{
int fnum;
char *parms;
const char *parms;
int len;
if (!sv.state)

View File

@ -292,7 +292,7 @@ vfsfile_t *FSPPAPI_OpenTemp(void)
return &r->funcs;
}
qboolean Sys_remove (char *path)
qboolean Sys_remove (const char *path)
{
mfile_t *f;
for (f = mfiles; f; f = f->next)
@ -308,7 +308,7 @@ qboolean Sys_remove (char *path)
}
return false;
}
qboolean Sys_Rename (char *oldfname, char *newfname)
qboolean Sys_Rename (const char *oldfname, const char *newfname)
{
mfile_t *f;
for (f = mfiles; f; f = f->next)
@ -322,9 +322,13 @@ qboolean Sys_Rename (char *oldfname, char *newfname)
return false;
}
//no concept of directories.
void Sys_mkdir (char *path)
void Sys_mkdir (const char *path)
{
}
qboolean Sys_rmdir (const char *path)
{
return false;
}
vfsfile_t *VFSPPAPI_Open(const char *osname, const char *mode)
{

View File

@ -9305,6 +9305,7 @@ static void QCBUILTIN PF_runclientphys(pubprogfuncs_t *prinst, struct globalvars
extern cvar_t sv_gravity;
edict_t *ent = G_EDICT(prinst, OFS_PARM0);
edict_t *touched;
client_t *client;
if (!ent || ent->readonly)
{
@ -9317,7 +9318,11 @@ static void QCBUILTIN PF_runclientphys(pubprogfuncs_t *prinst, struct globalvars
else
pmove.sequence = 0;
pmove.pm_type = SV_PMTypeForClient((host_client && host_client->edict == ent)?host_client:NULL, ent);
if (ent->entnum >= 1 && ent->entnum <= sv.allocated_client_slots)
client = &svs.clients[ent->entnum-1];
else
client = NULL;
pmove.pm_type = SV_PMTypeForClient(client, ent);
pmove.jump_msec = 0;

View File

@ -1508,11 +1508,11 @@ static void QVM_uri_query_callback(struct dl_download *dl)
char *buffer = malloc(len+1);
buffer[len] = 0;
VFS_READ(dl->file, buffer, len);
Cmd_Args_Set(buffer);
Cmd_Args_Set(buffer, strlen(buffer));
free(buffer);
}
else
Cmd_Args_Set(NULL);
Cmd_Args_Set(NULL, 0);
VM_Call(q1qvm, cb_entry, cb_context, dl->replycode, 0, 0, 0);
}

View File

@ -1226,6 +1226,7 @@ void SV_Savegame (const char *savename, qboolean mapchange)
FS_Remove(savefilename, FS_GAMEONLY);
if (cls.state == ca_active && qrenderer > QR_NONE && qrenderer != QR_VULKAN/*FIXME*/)
{
int stride;
int width;
int height;
void *rgbbuffer;
@ -1252,11 +1253,11 @@ void SV_Savegame (const char *savename, qboolean mapchange)
if (okay)
{
enum uploadfmt fmt;
rgbbuffer = VID_GetRGBInfo(&width, &height, &fmt);
rgbbuffer = VID_GetRGBInfo(&stride, &width, &height, &fmt);
if (rgbbuffer)
{
// extern cvar_t scr_sshot_type;
SCR_ScreenShot(savefilename, FS_GAMEONLY, &rgbbuffer, 1, width, height, fmt);
SCR_ScreenShot(savefilename, FS_GAMEONLY, &rgbbuffer, 1, stride, width, height, fmt);
BZ_Free(rgbbuffer);

View File

@ -319,8 +319,6 @@ typedef struct
float move_msecs; //
int packetsizein; //amount of data received for this frame
int packetsizeout; //amount of data that was sent in the frame
vec3_t playerpositions[MAX_CLIENTS]; //where each player was in this frame, for antilag
qboolean playerpresent[MAX_CLIENTS]; //whether the player was actually present
packet_entities_t entities; //package containing entity states that were sent in this frame, for deltaing
struct resendinfo_s
{
@ -330,6 +328,19 @@ typedef struct
} *resend;
unsigned short resendstats[32];//the number of each entity that was sent in this frame
unsigned int numresendstats; //the bits of each entity that were sent in this frame
//antilag
//these are to recalculate the player's origin without old knockbacks nor teleporters, to give more accurate weapon start positions (post-command).
vec3_t pmorigin;
vec3_t pmvelocity;
int pmtype;
unsigned int pmjumpheld:1;
unsigned int pmonladder:1;
float pmwaterjumptime;
usercmd_t cmd;
//these are old positions of players, to give more accurate victim positions
vec3_t playerpositions[MAX_CLIENTS]; //where each player was in this frame, for antilag
qboolean playerpresent[MAX_CLIENTS]; //whether the player was actually present
} client_frame_t;
#ifdef Q2SERVER
@ -779,7 +790,6 @@ typedef struct
int forceFrame;
struct mvddest_s *dest;
struct mvdpendingdest_s *pendingdest;
} demo_t;
@ -1333,6 +1343,11 @@ typedef struct { //stats info
qbyte trustlevel;
char pad2;
char pad3;
#if NUM_RANK_SPAWN_PARMS>32
quint64_t created;
quint64_t lastseen;
#endif
} rankstats_t;
typedef struct { //name, identity and order.
@ -1417,7 +1432,7 @@ void SV_ChatThink(client_t *client);
/*
//
// sv_mvd.c
//
@ -1442,7 +1457,7 @@ typedef struct mvdpendingdest_s {
int outsize;
struct mvdpendingdest_s *nextdest;
} mvdpendingdest_t;
} mvdpendingdest_t;*/
typedef struct mvddest_s {
qboolean error; //disables writers, quit ASAP.
@ -1450,11 +1465,6 @@ typedef struct mvddest_s {
enum {DEST_NONE, DEST_FILE, DEST_BUFFEREDFILE, DEST_THREADEDFILE, DEST_STREAM} desttype;
#ifdef _WIN32
quintptr_t socket; //gah
#else
int socket;
#endif
vfsfile_t *file;
char filename[MAX_QPATH]; //demos/foo.mvd
@ -1500,6 +1510,7 @@ extern cvar_t sv_demoMaxSize;
extern cvar_t sv_demoMaxDirSize;
char *SV_Demo_CurrentOutput(void);
void SV_Demo_PrintOutputs(void);
void SV_MVDInit(void);
char *SV_MVDNum(char *buffer, int bufferlen, int num);
const char *SV_MVDLastNum(unsigned int num);
@ -1509,6 +1520,14 @@ qboolean SV_ReadMVD (void);
void SV_FlushDemoSignon (void);
void DestFlush(qboolean compleate);
typedef struct
{
qboolean hasauthed;
qboolean isreverse;
char challenge[32];
} qtvpendingstate_t;
int SV_MVD_GotQTVRequest(vfsfile_t *clientstream, char *headerstart, char *headerend, qtvpendingstate_t *p);
// savegame.c
void SV_LegacySavegame_f(void);
void SV_Savegame_f (void);

View File

@ -1846,8 +1846,9 @@ static void SV_Status_f (void)
Con_Printf("gamedir : %s\n", FS_GetGamedir(true));
if (sv.csqcdebug)
Con_Printf("csqc debug : true\n");
if (sv.mvdrecording)
Con_Printf("recording : %s\n", SV_Demo_CurrentOutput());
SV_Demo_PrintOutputs();
NET_PrintConnectionsStatus(svs.sockets);
// min fps lat drp
if (columns < 80)
@ -1855,7 +1856,7 @@ static void SV_Status_f (void)
// most remote clients are 40 columns
// 0123456789012345678901234567890123456789
Con_Printf ("name userid frags\n");
Con_Printf (" address rate ping drop\n");
Con_Printf (" address rate ping drop\n");
Con_Printf (" ---------------- ---- ---- -----\n");
for (i=0,cl=svs.clients ; i<svs.allocated_client_slots ; i++,cl++)
{
@ -1930,7 +1931,7 @@ static void SV_Status_f (void)
}
if (cl->protocol != SCP_QUAKEWORLD || cl->spectator || !(cl->fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS))
columns |= 1<<9;
if (cl->netchan.remote_address.type == NA_IPV6 || cl->netchan.remote_address.type == NA_TCPV6 || cl->netchan.remote_address.type == NA_TLSV6)
if (cl->netchan.remote_address.type == NA_IPV6)
columns = (columns & ~(1<<2)) | (1<<10);
}

View File

@ -836,7 +836,7 @@ void SSV_UpdateAddresses(void)
char buf[256];
netadr_t addr[64];
struct ftenet_generic_connection_s *con[sizeof(addr)/sizeof(addr[0])];
int flags[sizeof(addr)/sizeof(addr[0])];
unsigned int flags[sizeof(addr)/sizeof(addr[0])];
int count;
sizebuf_t send;
qbyte send_buf[MAX_QWMSGLEN];

View File

@ -485,6 +485,9 @@ void SV_DropClient (client_t *drop)
for (j=0 ; j<NUM_RANK_SPAWN_PARMS ; j++)
if (pr_global_ptrs->spawnparamglobals[j])
rs.parm[j] = *pr_global_ptrs->spawnparamglobals[j];
#if NUM_RANK_SPAWN_PARMS>32
rs.lastseen = time(NULL);
#endif
Rank_SetPlayerStats(drop->rankid, &rs);
}
}
@ -3681,13 +3684,13 @@ qboolean SVNQ_ConnectionlessPacket(void)
flags = MSG_ReadByte();
passwd = MSG_ReadLong();
if (!strncmp(MSG_ReadString(), "getchallenge", 12) && (sv_listen_qw.ival || sv_listen_dp.ival))
if (SV_ChallengeRecent())
return true;
else if (!strncmp(MSG_ReadString(), "getchallenge", 12) && (sv_listen_qw.ival || sv_listen_dp.ival))
{
/*dual-stack client, supporting either DP or QW protocols*/
SVC_GetChallenge (true);
}
else if (SV_ChallengeRecent())
return true;
else
{
if (progstype == PROG_H2)
@ -4575,11 +4578,6 @@ float SV_Frame (void)
SVM_Think(PORT_QWMASTER);
}
{
void SV_MVDStream_Poll(void);
SV_MVDStream_Poll();
}
#ifdef PLUGINS
if (isDedicated)
Plug_Tick();

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,10 @@
#include "quakedef.h"
#ifndef CLIENTONLY
//FIXME: this is shitty old code.
//possible improvements: using a hash table for player names for faster logons
//threading logins
//using a real database...
#ifdef SVRANKING
@ -13,8 +17,14 @@ typedef struct {
} rankfileheader_t;
//endian
#define NOENDIAN
#ifdef NOENDIAN
#define swaplong(l) l
#define swapfloat(f) f
#else
#define swaplong LittleLong
#define swapfloat LittleFloat
#endif
rankfileheader_t rankfileheader;
vfsfile_t *rankfile;
@ -31,7 +41,9 @@ char rank_cvargroup[] = "server rankings";
static void READ_PLAYERSTATS(int x, rankstats_t *os)
{
#ifndef NOENDIAN
int i;
#endif
size_t result;
VFS_SEEK(rankfile, sizeof(rankfileheader_t)+sizeof(rankheader_t)+((x-1)*sizeof(rankinfo_t)));
@ -40,6 +52,7 @@ static void READ_PLAYERSTATS(int x, rankstats_t *os)
if (result != sizeof(rankstats_t))
Con_Printf("READ_PLAYERSTATS() fread: expected %lu, result was %u\n",(long unsigned int)sizeof(rankstats_t),(unsigned int)result);
#ifndef NOENDIAN
os->kills = swaplong(os->kills);
os->deaths = swaplong(os->deaths);
for (i = 0; i < NUM_RANK_SPAWN_PARMS; i++)
@ -49,15 +62,17 @@ static void READ_PLAYERSTATS(int x, rankstats_t *os)
// os->trustlevel = (os->trustlevel);
// os->pad2 = (os->pad2);
// os->pad3 = (os->pad3);
#endif
}
static void WRITE_PLAYERSTATS(int x, rankstats_t *os)
{
#ifdef NOENDIAN
VFS_SEEK(rankfile, sizeof(rankfileheader_t)+sizeof(rankheader_t)+((x-1)*sizeof(rankinfo_t)));
VFS_WRITE(rankfile, os, sizeof(rankstats_t));
#else
rankstats_t ns;
int i;
VFS_SEEK(rankfile, sizeof(rankfileheader_t)+sizeof(rankheader_t)+((x-1)*sizeof(rankinfo_t)));
ns.kills = swaplong(os->kills);
ns.deaths = swaplong(os->deaths);
for (i = 0; i < NUM_RANK_SPAWN_PARMS; i++)
@ -68,7 +83,9 @@ static void WRITE_PLAYERSTATS(int x, rankstats_t *os)
ns.pad2 = (os->pad2);
ns.pad3 = (os->pad3);
VFS_SEEK(rankfile, sizeof(rankfileheader_t)+sizeof(rankheader_t)+((x-1)*sizeof(rankinfo_t)));
VFS_WRITE(rankfile, &ns, sizeof(rankstats_t));
#endif
}
static void READ_PLAYERHEADER(int x, rankheader_t *oh)
@ -82,26 +99,31 @@ static void READ_PLAYERHEADER(int x, rankheader_t *oh)
if (result != sizeof(rankheader_t))
Con_Printf("READ_PLAYERHEADER() fread: expected %lu, result was %u\n",(long unsigned int)sizeof(rankheader_t),(unsigned int)result);
#ifndef NOENDIAN
oh->prev = swaplong(oh->prev); //score is held for convineance.
oh->next = swaplong(oh->next);
// strcpy(oh->name, oh->name);
oh->pwd = swaplong(oh->pwd);
oh->score = swapfloat(oh->score);
#endif
}
static void WRITE_PLAYERHEADER(int x, rankheader_t *oh)
{
rankheader_t nh;
#ifdef NOENDIAN
VFS_SEEK(rankfile, sizeof(rankfileheader_t)+((x-1)*sizeof(rankinfo_t)));
VFS_WRITE(rankfile, &oh, sizeof(rankheader_t));
#else
rankheader_t nh;
nh.prev = swaplong(oh->prev); //score is held for convineance.
nh.next = swaplong(oh->next);
Q_strncpyz(nh.name, oh->name, sizeof(nh.name));
nh.pwd = swaplong(oh->pwd);
nh.score = swapfloat(oh->score);
VFS_SEEK(rankfile, sizeof(rankfileheader_t)+((x-1)*sizeof(rankinfo_t)));
VFS_WRITE(rankfile, &nh, sizeof(rankheader_t));
#endif
}
static void READ_PLAYERINFO(int x, rankinfo_t *inf)
@ -469,6 +491,7 @@ void Rank_AddUser_f (void)
char *name = Cmd_Argv(1);
int pwd = atoi(Cmd_Argv(2));
int userlevel = atoi(Cmd_Argv(3));
char fixed[80];
if (Cmd_Argc() < 2)
{
@ -492,7 +515,8 @@ void Rank_AddUser_f (void)
return;
}
SV_FixupName(name, name, sizeof(name));
SV_FixupName(name, fixed, sizeof(fixed));
name = fixed;
if (!Rank_OpenRankings())
{
@ -548,6 +572,9 @@ void Rank_AddUser_f (void)
memset(&rs, 0, sizeof(rs));
rs.trustlevel = userlevel;
#if NUM_RANK_SPAWN_PARMS>32
rs.created = rs.lastseen = time(NULL);
#endif
WRITE_PLAYERSTATS(id, &rs);
Rank_SetPlayerStats(id, &rs);
@ -560,6 +587,7 @@ void Rank_SetPass_f (void)
rankheader_t rh;
char *name = Cmd_Argv(1);
int newpass = atoi(Cmd_Argv(2));
char fixed[80];
int id;
@ -575,7 +603,8 @@ void Rank_SetPass_f (void)
return;
}
SV_FixupName(name, name, sizeof(name));
SV_FixupName(name, fixed, sizeof(fixed));
name = fixed;
id = rankfileheader.leader;
while(id)
@ -595,6 +624,7 @@ void Rank_SetPass_f (void)
int Rank_GetPass (char *name)
{
rankheader_t rh;
char fixed[80];
int id;
@ -604,7 +634,8 @@ int Rank_GetPass (char *name)
return 0;
}
SV_FixupName(name, name, sizeof(name));
SV_FixupName(name, fixed, sizeof(fixed));
name = fixed;
id = rankfileheader.leader;
while(id)
@ -651,14 +682,11 @@ int Rank_Enumerate (unsigned int first, unsigned int last, void (*callback) (con
void Rank_RankingList_f (void)
{
#if 1
Con_Printf("Fixme\n");
#else
rankinfo_t ri;
int id;
int num;
FILE *outfile;
vfsfile_t *outfile;
if (!Rank_OpenRankings())
{
@ -666,9 +694,14 @@ void Rank_RankingList_f (void)
return;
}
outfile = fopen("list.txt", "wb");
outfile = FS_OpenVFS("list.txt", "wb", FS_GAMEONLY);
if (!outfile)
{
Con_Printf("Couldn't open list.txt\n");
return;
}
fprintf(outfile, "%5s: %32s, %5s %5s\r\n", "", "Name", "Kills", "Deaths");
VFS_PRINTF(outfile, "%5s: %32s, %5s %5s\r\n", "", "Name", "Kills", "Deaths");
id = rankfileheader.leader; //start at the leaders
num=1;
@ -676,17 +709,16 @@ void Rank_RankingList_f (void)
{
READ_PLAYERINFO(id, &ri);
fprintf(outfile, "%5i: %32s, %5i %5i\r\n", num, ri.h.name, ri.s.kills, ri.s.deaths);
VFS_PRINTF(outfile, "%5i: %32s, %5i %5i\r\n", num, ri.h.name, ri.s.kills, ri.s.deaths);
num++;
id = ri.h.next;
}
fclose(outfile);
#endif
VFS_CLOSE(outfile);
}
void Rank_Remove_f (void)
void Rank_RemoveID_f (void)
{
rankinfo_t ri;
int id;
@ -770,7 +802,7 @@ void Rank_Find_f (void)
rankinfo_t ri;
int id;
char *match = Cmd_Argv(1);
char *match = Q_strlwr(Cmd_Argv(1));
if (!Rank_OpenRankings())
{
@ -784,7 +816,7 @@ void Rank_Find_f (void)
{
READ_PLAYERINFO(id, &ri);
if (strstr(ri.h.name, match))
if (wildcmp(match, Q_strlwr(ri.h.name)))
{
Con_Printf("%i %s\n", id, ri.h.name);
}
@ -889,7 +921,7 @@ void Rank_RegisterCommands(void)
Cmd_AddCommand("ranklist", Rank_RankingList_f);
Cmd_AddCommand("ranktopten", Rank_ListTop10_f);
Cmd_AddCommand("rankfind", Rank_Find_f);
Cmd_AddCommand("rankremove", Rank_Remove_f);
Cmd_AddCommand("rankremove", Rank_RemoveID_f);
Cmd_AddCommand("rankrefresh", Rank_Refresh_f);
Cmd_AddCommand("rankrconlevel", Rank_RCon_f);

View File

@ -1171,7 +1171,7 @@ void SV_MulticastCB(vec3_t origin, multicast_t to, int dimension_mask, void (*ca
break;
if (to == MULTICAST_PHS_R || to == MULTICAST_PHS)
{
{ //phs is always 'visible' within 1024qu
vec3_t delta;
VectorSubtract(origin, split->edict->v->origin, delta);
if (DotProduct(delta, delta) <= 1024*1024)
@ -1294,7 +1294,7 @@ struct startsoundcontext_s
float attenuation;
float ratemul;
unsigned int chflags;
unsigned int timeofs;
int timeofs;
};
static void SV_SoundMulticast(client_t *client, sizebuf_t *msg, void *vctx)
{
@ -1305,7 +1305,7 @@ static void SV_SoundMulticast(client_t *client, sizebuf_t *msg, void *vctx)
if (ctx->ent >= client->max_net_ents)
return;
field_mask |= (ctx->chflags & (CF_NOSPACIALISE|CF_NOREVERB|CF_FOLLOW)) << 8;
field_mask |= (ctx->chflags & CF_NETWORKED) << 8;
if (ctx->volume != DEFAULT_SOUND_PACKET_VOLUME)
field_mask |= NQSND_VOLUME;
if (ctx->attenuation != DEFAULT_SOUND_PACKET_ATTENUATION)
@ -3422,8 +3422,6 @@ void SV_SendMVDMessage(void)
// extern cvar_t sv_demoMaxSize;
sizebuf_t *dmsg;
SV_MVD_RunPendingConnections();
if (!sv.mvdrecording)
return;

View File

@ -92,20 +92,28 @@ Sys_mkdir
============
*/
void Sys_mkdir (char *path)
void Sys_mkdir (const char *path)
{
if (mkdir (path, 0777) != -1)
if (mkdir (path, 0755) != -1)
return;
// if (errno != EEXIST)
// Sys_Error ("mkdir %s: %s",path, strerror(errno));
}
qboolean Sys_rmdir (const char *path)
{
if (rmdir (path) == 0)
return true;
if (errno == ENOENT)
return true;
return false;
}
qboolean Sys_remove (char *path)
qboolean Sys_remove (const char *path)
{
return system(va("rm \"%s\"", path));
}
qboolean Sys_Rename (char *oldfname, char *newfname)
qboolean Sys_Rename (const char *oldfname, const char *newfname)
{
return !rename(oldfname, newfname);
}

View File

@ -25,6 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <winsock.h>
#include <conio.h>
#include <direct.h>
#ifdef MULTITHREAD
#include <process.h>
@ -483,19 +484,22 @@ char *narrowen(char *out, size_t outlen, wchar_t *wide);
Sys_mkdir
================
*/
int _mkdir(const char *path);;
void Sys_mkdir (char *path)
void Sys_mkdir (const char *path)
{
_mkdir(path);
}
qboolean Sys_rmdir (const char *path)
{
return 0==_rmdir(path);
}
qboolean Sys_remove (char *path)
qboolean Sys_remove (const char *path)
{
remove(path);
return true;
}
qboolean Sys_Rename (char *oldfname, char *newfname)
qboolean Sys_Rename (const char *oldfname, const char *newfname)
{
return !rename(oldfname, newfname);
}

View File

@ -47,7 +47,7 @@ cvar_t sv_spectalk = CVAR("sv_spectalk", "1");
cvar_t sv_mapcheck = CVAR("sv_mapcheck", "1");
cvar_t sv_fullredirect = CVARD("sv_fullredirect", "", "This is the ip:port to redirect players to when the server is full");
cvar_t sv_antilag = CVARFD("sv_antilag", "", CVAR_SERVERINFO, "Attempt to backdate impacts to compensate for lag. 0=completely off. 1=mod-controlled. 2=forced, which might break certain uses of traceline.");
cvar_t sv_antilag = CVARFD("sv_antilag", "", CVAR_SERVERINFO, "Attempt to backdate impacts to compensate for lag via the MOVE_ANTILAG feature.\n0=completely off.\n1=mod-controlled (default).\n2=forced, which might break certain uses of traceline.\n3=Also attempt to recalculate trace start positions to avoid lagged knockbacks.");
cvar_t sv_antilag_frac = CVARF("sv_antilag_frac", "", CVAR_SERVERINFO);
#ifndef NEWSPEEDCHEATPROT
cvar_t sv_cheatpc = CVARD("sv_cheatpc", "125", "If the client tried to claim more than this percentage of time within any speed-cheat period, the client will be deemed to have cheated.");
@ -69,7 +69,9 @@ cvar_t sv_nqplayerphysics = CVARAD("sv_nqplayerphysics", "0", "sv_nomsec", "Disa
cvar_t sv_edgefriction = CVARAF("sv_edgefriction", "2",
"edgefriction", 0);
#ifndef NOLEGACY
cvar_t sv_brokenmovetypes = CVARD("sv_brokenmovetypes", "0", "Emulate vanilla quakeworld by forcing MOVETYPE_WALK on all players. Shouldn't be used for any games other than QuakeWorld.");
#endif
cvar_t sv_chatfilter = CVAR("sv_chatfilter", "0");
@ -3328,7 +3330,7 @@ void SV_BeginDownload_f(void)
#ifdef PEXT_CHUNKEDDOWNLOADS
if (host_client->fteprotocolextensions & PEXT_CHUNKEDDOWNLOADS)
{
if (host_client->download->seekingisabadplan)
if (host_client->download->seekstyle != SS_SEEKABLE)
{ //if seeking is a bad plan (for whatever reason - usually because of zip files)
//create a temp file instead
int i, len;
@ -6432,6 +6434,7 @@ int SV_PMTypeForClient (client_t *cl, edict_t *ent)
}
#endif
#ifndef NOLEGACY
if (sv_brokenmovetypes.value) //this is to mimic standard qw servers, which don't support movetypes other than MOVETYPE_FLY.
{ //it prevents bugs from being visible in unsuspecting mods.
if (cl && cl->spectator)
@ -6445,6 +6448,7 @@ int SV_PMTypeForClient (client_t *cl, edict_t *ent)
return PM_DEAD;
return PM_NORMAL;
}
#endif
switch((int)ent->v->movetype)
{
@ -8172,7 +8176,9 @@ void SV_UserInit (void)
Cvar_Register (&votepercent, sv_votinggroup);
Cvar_Register (&votetime, sv_votinggroup);
#ifndef NOLEGACY
Cvar_Register (&sv_brokenmovetypes, "Backwards compatability");
#endif
Cvar_Register (&sv_edgefriction, "netquake compatability");
}

View File

@ -1863,6 +1863,59 @@ boxmaxs[0] = boxmaxs[1] = boxmaxs[2] = 9999;
#endif
}
#if !defined(CLIENTONLY)
qboolean SV_AntiKnockBack(world_t *w, client_t *client)
{
int seq = client->netchan.incoming_acknowledged; //our outgoing sequence that was last acked (in qw, this matches the last known-good input frame)
client_frame_t *frame;
edict_t *ent = client->edict;
if (client->protocol != SCP_QUAKEWORLD || !client->frameunion.frames || !ent)
return false; //FIXME: support nq protocols too
//reload player state from the journal (the input frame should already have been applied)
frame = &client->frameunion.frames[seq&UPDATE_MASK];
VectorCopy(frame->pmorigin, pmove.origin);
VectorCopy(frame->pmvelocity, pmove.velocity);
pmove.pm_type = frame->pmtype;
pmove.jump_held = frame->pmjumpheld;
pmove.waterjumptime = frame->pmwaterjumptime;
pmove.onladder = frame->pmonladder;
//stuff not regenerated properly, shouldn't really be changing much or not very significant.
pmove.world = w;
VectorCopy(ent->v->mins, pmove.player_mins);
VectorCopy(ent->v->maxs, pmove.player_maxs);
pmove.capsule = (ent->xv->geomtype == GEOMTYPE_CAPSULE);
if (ent->xv->gravitydir[2] || ent->xv->gravitydir[1] || ent->xv->gravitydir[0])
VectorCopy(ent->xv->gravitydir, pmove.gravitydir);
else
VectorCopy(w->g.defaultgravitydir, pmove.gravitydir);
//FIXME
VectorCopy(ent->v->oldorigin, pmove.safeorigin);
pmove.safeorigin_known = false;
pmove.jump_msec = 0;
VectorClear(pmove.basevelocity);
//and apply each more recent frame
while (++seq <= client->netchan.incoming_sequence)
{
if (frame->sequence != seq)
continue; //FIXME: lost
pmove.sequence = seq;
pmove.cmd = frame->cmd;
// pmove.angles;
// pmove.numphysent/physents;
PM_PlayerMove(sv.gamespeed);
}
return true;
}
#endif
/*
==================
SV_Move
@ -1911,6 +1964,22 @@ trace_t World_Move (world_t *w, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t e
}
#endif
#if !defined(CLIENTONLY)
//figure out where the firing player was, and re-run their input frames to calculate their position without any velocity/knockback changes.
//then update the start position to compensate.
if ((clip.type & MOVE_LAGGED) && w == &sv.world && passedict->entnum && passedict->entnum <= sv.allocated_client_slots && sv_antilag.ival==3)
{
vec3_t nudge;
if (SV_AntiKnockBack(w, &svs.clients[passedict->entnum-1]))
{
VectorSubtract(pmove.origin, passedict->v->origin, nudge);
VectorAdd(start, nudge, start);
VectorAdd(end, nudge, end);
}
}
#endif
if (passedict->xv->hitcontentsmaski)
clip.hitcontentsmask = passedict->xv->hitcontentsmaski;
/*#ifndef NOLEGACY

View File

@ -240,7 +240,7 @@ void main ()
#define tcbase tcoffsetmap
#endif
#if defined(FLAT)
vec4 bases = vec3(FLAT, 1.0);
vec4 bases = vec4(FLAT, FLAT, FLAT, 1.0);
#else
vec4 bases = texture2D(s_diffuse, tcbase);
#ifdef VERTEXCOLOURS

View File

@ -155,7 +155,8 @@ static void VK_DestroySwapChain(void)
Sys_WaitOnThread(vk.submitthread);
vk.submitthread = NULL;
}
vk.dopresent(NULL);
if (vk.dopresent)
vk.dopresent(NULL);
while (vk.aquirenext < vk.aquirelast)
{
VkWarnAssert(vkWaitForFences(vk.device, 1, &vk.acquirefences[vk.aquirenext%ACQUIRELIMIT], VK_FALSE, UINT64_MAX));
@ -199,7 +200,8 @@ static void VK_DestroySwapChain(void)
VK_DestroyVkTexture(&vk.backbufs[i].depth);
}
vk.dopresent(NULL);
if (vk.dopresent)
vk.dopresent(NULL);
while (vk.aquirenext < vk.aquirelast)
{
VkWarnAssert(vkWaitForFences(vk.device, 1, &vk.acquirefences[vk.aquirenext%ACQUIRELIMIT], VK_FALSE, UINT64_MAX));
@ -249,13 +251,78 @@ static qboolean VK_CreateSwapChain(void)
uint32_t i, curpri;
VkSwapchainKHR newvkswapchain;
VkImage *images;
VkImage *memories;
VkImageView attachments[2];
VkFramebufferCreateInfo fb_info = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};
vk.dopresent(NULL); //make sure they're all pushed through.
if (!vk.surface)
return true;
if (vk.headless)
{
if (vk.swapchain || vk.backbuf_count)
VK_DestroySwapChain();
vk.backbufformat = vid_srgb.ival?VK_FORMAT_B8G8R8A8_SRGB:VK_FORMAT_B8G8R8A8_UNORM;
vk.backbuf_count = 4;
swapinfo.imageExtent.width = vid.pixelwidth;
swapinfo.imageExtent.height = vid.pixelheight;
images = malloc(sizeof(VkImage)*vk.backbuf_count);
memset(images, 0, sizeof(VkImage)*vk.backbuf_count);
memories = malloc(sizeof(VkDeviceMemory)*vk.backbuf_count);
memset(memories, 0, sizeof(VkDeviceMemory)*vk.backbuf_count);
vk.aquirelast = vk.aquirenext = 0;
for (i = 0; i < ACQUIRELIMIT; i++)
{
VkFenceCreateInfo fci = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
fci.flags = VK_FENCE_CREATE_SIGNALED_BIT;
VkAssert(vkCreateFence(vk.device,&fci,vkallocationcb,&vk.acquirefences[i]));
vk.acquirebufferidx[vk.aquirelast%ACQUIRELIMIT] = vk.aquirelast%vk.backbuf_count;
vk.aquirelast++;
}
for (i = 0; i < vk.backbuf_count; i++)
{
VkMemoryRequirements mem_reqs;
VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
VkImageCreateInfo ici = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
ici.flags = 0;
ici.imageType = VK_IMAGE_TYPE_2D;
ici.format = vk.backbufformat;
ici.extent.width = vid.pixelwidth;
ici.extent.height = vid.pixelheight;
ici.extent.depth = 1;
ici.mipLevels = 1;
ici.arrayLayers = 1;
ici.samples = VK_SAMPLE_COUNT_1_BIT;
ici.tiling = VK_IMAGE_TILING_OPTIMAL;
ici.usage = VK_IMAGE_USAGE_SAMPLED_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT|VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
ici.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
ici.queueFamilyIndexCount = 0;
ici.pQueueFamilyIndices = NULL;
ici.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkAssert(vkCreateImage(vk.device, &ici, vkallocationcb, &images[i]));
vkGetImageMemoryRequirements(vk.device, images[i], &mem_reqs);
memAllocInfo.allocationSize = mem_reqs.size;
memAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT|VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
if (memAllocInfo.memoryTypeIndex == ~0)
memAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
if (memAllocInfo.memoryTypeIndex == ~0)
memAllocInfo.memoryTypeIndex = vk_find_memory_try(mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
if (memAllocInfo.memoryTypeIndex == ~0)
memAllocInfo.memoryTypeIndex = vk_find_memory_require(mem_reqs.memoryTypeBits, 0);
VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &memories[i]));
VkAssert(vkBindImageMemory(vk.device, images[i], memories[i], 0));
}
}
else
{
VkAssert(vkGetPhysicalDeviceSurfaceFormatsKHR(vk.gpu, vk.surface, &fmtcount, NULL));
@ -267,129 +334,130 @@ static qboolean VK_CreateSwapChain(void)
VkAssert(vkGetPhysicalDeviceSurfacePresentModesKHR(vk.gpu, vk.surface, &presentmodes, presentmode));
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vk.gpu, vk.surface, &surfcaps);
}
swapinfo.surface = vk.surface;
swapinfo.minImageCount = surfcaps.minImageCount+vk.triplebuffer;
if (swapinfo.minImageCount > surfcaps.maxImageCount)
swapinfo.minImageCount = surfcaps.maxImageCount;
if (swapinfo.minImageCount < surfcaps.minImageCount)
swapinfo.minImageCount = surfcaps.minImageCount;
swapinfo.imageExtent.width = surfcaps.currentExtent.width;
swapinfo.imageExtent.height = surfcaps.currentExtent.height;
swapinfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
swapinfo.preTransform = surfcaps.currentTransform;//VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
if (surfcaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)
swapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
else if (surfcaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
swapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
else if (surfcaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
swapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
else
swapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; //erk?
swapinfo.imageArrayLayers = /*(r_stereo_method.ival==1)?2:*/1;
swapinfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapinfo.queueFamilyIndexCount = 0;
swapinfo.pQueueFamilyIndices = NULL;
swapinfo.oldSwapchain = vk.swapchain;
swapinfo.clipped = vid_isfullscreen?VK_FALSE:VK_TRUE; //allow fragment shaders to be skipped on parts that are obscured by another window. screenshots might get weird, so use proper captures if required/automagic.
swapinfo.surface = vk.surface;
swapinfo.minImageCount = surfcaps.minImageCount+vk.triplebuffer;
if (swapinfo.minImageCount > surfcaps.maxImageCount)
swapinfo.minImageCount = surfcaps.maxImageCount;
if (swapinfo.minImageCount < surfcaps.minImageCount)
swapinfo.minImageCount = surfcaps.minImageCount;
swapinfo.imageExtent.width = surfcaps.currentExtent.width;
swapinfo.imageExtent.height = surfcaps.currentExtent.height;
swapinfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
swapinfo.preTransform = surfcaps.currentTransform;//VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
if (surfcaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)
swapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
else if (surfcaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
swapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
else if (surfcaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
swapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
else
swapinfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; //erk?
swapinfo.imageArrayLayers = /*(r_stereo_method.ival==1)?2:*/1;
swapinfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapinfo.queueFamilyIndexCount = 0;
swapinfo.pQueueFamilyIndices = NULL;
swapinfo.oldSwapchain = vk.swapchain;
swapinfo.clipped = vid_isfullscreen?VK_FALSE:VK_TRUE; //allow fragment shaders to be skipped on parts that are obscured by another window. screenshots might get weird, so use proper captures if required/automagic.
swapinfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; //supposed to be guarenteed support.
for (i = 0, curpri = 0; i < presentmodes; i++)
{
uint32_t priority = 0;
switch(presentmode[i])
swapinfo.presentMode = VK_PRESENT_MODE_FIFO_KHR; //supposed to be guarenteed support.
for (i = 0, curpri = 0; i < presentmodes; i++)
{
default://ignore it.
break;
case VK_PRESENT_MODE_IMMEDIATE_KHR:
priority = (vk.vsync?0:2) + 2; //for most quake players, latency trumps tearing.
break;
case VK_PRESENT_MODE_MAILBOX_KHR:
priority = (vk.vsync?0:2) + 1;
break;
case VK_PRESENT_MODE_FIFO_KHR:
priority = (vk.vsync?2:0) + 1;
break;
case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
priority = (vk.vsync?2:0) + 2; //strict vsync results in weird juddering if rtlights etc caues framerates to drop below the refreshrate
break;
uint32_t priority = 0;
switch(presentmode[i])
{
default://ignore it.
break;
case VK_PRESENT_MODE_IMMEDIATE_KHR:
priority = (vk.vsync?0:2) + 2; //for most quake players, latency trumps tearing.
break;
case VK_PRESENT_MODE_MAILBOX_KHR:
priority = (vk.vsync?0:2) + 1;
break;
case VK_PRESENT_MODE_FIFO_KHR:
priority = (vk.vsync?2:0) + 1;
break;
case VK_PRESENT_MODE_FIFO_RELAXED_KHR:
priority = (vk.vsync?2:0) + 2; //strict vsync results in weird juddering if rtlights etc caues framerates to drop below the refreshrate
break;
}
if (priority > curpri)
{
curpri = priority;
swapinfo.presentMode = presentmode[i];
}
}
if (priority > curpri)
swapinfo.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
swapinfo.imageFormat = vid_srgb.ival?VK_FORMAT_B8G8R8A8_SRGB:VK_FORMAT_B8G8R8A8_UNORM;
for (i = 0, curpri = 0; i < fmtcount; i++)
{
curpri = priority;
swapinfo.presentMode = presentmode[i];
uint32_t priority = 0;
switch(surffmts[i].format)
{
case VK_FORMAT_B8G8R8A8_UNORM:
case VK_FORMAT_R8G8B8A8_UNORM:
priority = 4+!vid_srgb.ival;
break;
case VK_FORMAT_B8G8R8A8_SRGB:
case VK_FORMAT_R8G8B8A8_SRGB:
priority = 4+!!vid_srgb.ival;
break;
case VK_FORMAT_R16G16B16A16_SFLOAT: //16bit per-channel formats
case VK_FORMAT_R16G16B16A16_SNORM:
priority = 3;
break;
case VK_FORMAT_R32G32B32A32_SFLOAT: //32bit per-channel formats
priority = 2;
break;
default: //16 bit formats (565).
priority = 1;
break;
}
if (priority > curpri)
{
curpri = priority;
swapinfo.imageColorSpace = surffmts[i].colorSpace;
swapinfo.imageFormat = surffmts[i].format;
}
}
}
swapinfo.imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
swapinfo.imageFormat = vid_srgb.ival?VK_FORMAT_B8G8R8A8_SRGB:VK_FORMAT_B8G8R8A8_UNORM;
for (i = 0, curpri = 0; i < fmtcount; i++)
{
uint32_t priority = 0;
switch(surffmts[i].format)
if (vk.backbufformat != swapinfo.imageFormat)
{
case VK_FORMAT_B8G8R8A8_UNORM:
case VK_FORMAT_R8G8B8A8_UNORM:
priority = 4+!vid_srgb.ival;
break;
case VK_FORMAT_B8G8R8A8_SRGB:
case VK_FORMAT_R8G8B8A8_SRGB:
priority = 4+!!vid_srgb.ival;
break;
case VK_FORMAT_R16G16B16A16_SFLOAT: //16bit per-channel formats
case VK_FORMAT_R16G16B16A16_SNORM:
priority = 3;
break;
case VK_FORMAT_R32G32B32A32_SFLOAT: //32bit per-channel formats
priority = 2;
break;
default: //16 bit formats (565).
priority = 1;
break;
VK_DestroyRenderPass();
reloadshaders = true;
}
if (priority > curpri)
vk.backbufformat = swapinfo.imageFormat;
free(presentmode);
free(surffmts);
VkAssert(vkCreateSwapchainKHR(vk.device, &swapinfo, vkallocationcb, &newvkswapchain));
if (!newvkswapchain)
return false;
if (vk.swapchain)
{
curpri = priority;
swapinfo.imageColorSpace = surffmts[i].colorSpace;
swapinfo.imageFormat = surffmts[i].format;
VK_DestroySwapChain();
}
}
vk.swapchain = newvkswapchain;
if (vk.backbufformat != swapinfo.imageFormat)
{
VK_DestroyRenderPass();
reloadshaders = true;
}
vk.backbufformat = swapinfo.imageFormat;
VkAssert(vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &vk.backbuf_count, NULL));
images = malloc(sizeof(VkImage)*vk.backbuf_count);
memories = NULL;
VkAssert(vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &vk.backbuf_count, images));
free(presentmode);
free(surffmts);
VkAssert(vkCreateSwapchainKHR(vk.device, &swapinfo, vkallocationcb, &newvkswapchain));
if (!newvkswapchain)
return false;
if (vk.swapchain)
{
VK_DestroySwapChain();
}
vk.swapchain = newvkswapchain;
VkAssert(vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &vk.backbuf_count, NULL));
images = malloc(sizeof(VkImage)*vk.backbuf_count);
VkAssert(vkGetSwapchainImagesKHR(vk.device, vk.swapchain, &vk.backbuf_count, images));
vk.aquirelast = vk.aquirenext = 0;
for (i = 0; i < ACQUIRELIMIT; i++)
{
VkFenceCreateInfo fci = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
VkAssert(vkCreateFence(vk.device,&fci,vkallocationcb,&vk.acquirefences[i]));
}
/*-1 to hide any weird thread issues*/
while (vk.aquirelast < ACQUIRELIMIT-1 && vk.aquirelast < vk.backbuf_count && vk.aquirelast <= vk.backbuf_count-surfcaps.minImageCount)
{
VkAssert(vkAcquireNextImageKHR(vk.device, vk.swapchain, UINT64_MAX, VK_NULL_HANDLE, vk.acquirefences[vk.aquirelast%ACQUIRELIMIT], &vk.acquirebufferidx[vk.aquirelast%ACQUIRELIMIT]));
vk.aquirelast++;
vk.aquirelast = vk.aquirenext = 0;
for (i = 0; i < ACQUIRELIMIT; i++)
{
VkFenceCreateInfo fci = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
VkAssert(vkCreateFence(vk.device,&fci,vkallocationcb,&vk.acquirefences[i]));
}
/*-1 to hide any weird thread issues*/
while (vk.aquirelast < ACQUIRELIMIT-1 && vk.aquirelast < vk.backbuf_count && vk.aquirelast <= vk.backbuf_count-surfcaps.minImageCount)
{
VkAssert(vkAcquireNextImageKHR(vk.device, vk.swapchain, UINT64_MAX, VK_NULL_HANDLE, vk.acquirefences[vk.aquirelast%ACQUIRELIMIT], &vk.acquirebufferidx[vk.aquirelast%ACQUIRELIMIT]));
vk.aquirelast++;
}
}
VK_CreateRenderPass();
@ -415,7 +483,7 @@ static qboolean VK_CreateSwapChain(void)
for (i = 0; i < vk.backbuf_count; i++)
{
VkImageViewCreateInfo ivci = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
ivci.format = swapinfo.imageFormat;
ivci.format = vk.backbufformat;
ivci.components.r = VK_COMPONENT_SWIZZLE_R;
ivci.components.g = VK_COMPONENT_SWIZZLE_G;
ivci.components.b = VK_COMPONENT_SWIZZLE_B;
@ -429,6 +497,10 @@ static qboolean VK_CreateSwapChain(void)
ivci.flags = 0;
ivci.image = images[i];
vk.backbufs[i].colour.image = images[i];
if (memories)
vk.backbufs[i].colour.memory = memories[i];
vk.backbufs[i].colour.width = swapinfo.imageExtent.width;
vk.backbufs[i].colour.height = swapinfo.imageExtent.height;
VkAssert(vkCreateImageView(vk.device, &ivci, vkallocationcb, &vk.backbufs[i].colour.view));
vk.backbufs[i].firstuse = true;
@ -492,6 +564,7 @@ static qboolean VK_CreateSwapChain(void)
}
}
free(images);
free(memories);
vid.pixelwidth = swapinfo.imageExtent.width;
vid.pixelheight = swapinfo.imageExtent.height;
@ -1873,14 +1946,100 @@ void VK_R_RenderView (void)
vk.sourcecolour = r_nulltex;
}
char *VKVID_GetRGBInfo (int *truevidwidth, int *truevidheight, enum uploadfmt *fmt)
typedef struct
{
struct vk_fencework w;
uint32_t imageformat;
uint32_t imagestride;
uint32_t imagewidth;
uint32_t imageheight;
VkBuffer buffer;
size_t memsize;
VkDeviceMemory memory;
void (*gotrgbdata) (void *rgbdata, intptr_t bytestride, size_t width, size_t height, enum uploadfmt fmt);
} vkscreencapture_t;
static void VKVID_CopiedRGBData (void*ctx)
{ //some fence got hit, we did our copy, data is now cpu-visible, cache-willing.
vkscreencapture_t *capt = ctx;
void *imgdata;
VkAssert(vkMapMemory(vk.device, capt->memory, 0, capt->memsize, 0, &imgdata));
capt->gotrgbdata(imgdata, capt->imagestride, capt->imagewidth, capt->imageheight, capt->imageformat);
vkUnmapMemory(vk.device, capt->memory);
vkDestroyBuffer(vk.device, capt->buffer, vkallocationcb);
vkFreeMemory(vk.device, capt->memory, vkallocationcb);
}
void VKVID_QueueGetRGBData (void (*gotrgbdata) (void *rgbdata, intptr_t bytestride, size_t width, size_t height, enum uploadfmt fmt))
{
//should be half way through rendering
vkscreencapture_t *capt;
VkBufferImageCopy icpy;
VkImageSubresource subres = {0};
VkMemoryRequirements mem_reqs;
VkMemoryAllocateInfo memAllocInfo = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
VkBufferCreateInfo bci = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO};
if (!VK_SCR_GrabBackBuffer())
return;
if (!vk.frame->backbuf->colour.width || !vk.frame->backbuf->colour.height)
return; //erm, some kind of error?
capt = VK_AtFrameEnd(VKVID_CopiedRGBData, sizeof(*capt));
capt->gotrgbdata = gotrgbdata;
capt->imageformat = TF_BGRA32;
capt->imagestride = vk.frame->backbuf->colour.width*4; //vulkan is top-down, so this should be positive.
capt->imagewidth = vk.frame->backbuf->colour.width;
capt->imageheight = vk.frame->backbuf->colour.height;
bci.flags = 0;
bci.size = capt->memsize = capt->imagewidth*capt->imageheight*4;
bci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
bci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
bci.queueFamilyIndexCount = 0;
bci.pQueueFamilyIndices = NULL;
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);
VkAssert(vkAllocateMemory(vk.device, &memAllocInfo, vkallocationcb, &capt->memory));
VkAssert(vkBindBufferMemory(vk.device, capt->buffer, capt->memory, 0));
set_image_layout(vk.frame->cbuf, vk.frame->backbuf->colour.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT);
icpy.bufferOffset = 0;
icpy.bufferRowLength = 0; //packed
icpy.bufferImageHeight = 0; //packed
icpy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
icpy.imageSubresource.mipLevel = 0;
icpy.imageSubresource.baseArrayLayer = 0;
icpy.imageSubresource.layerCount = 1;
icpy.imageOffset.x = 0;
icpy.imageOffset.y = 0;
icpy.imageOffset.z = 0;
icpy.imageExtent.width = capt->imagewidth;
icpy.imageExtent.height = capt->imageheight;
icpy.imageExtent.depth = 1;
vkCmdCopyImageToBuffer(vk.frame->cbuf, vk.frame->backbuf->colour.image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, capt->buffer, 1, &icpy);
set_image_layout(vk.frame->cbuf, vk.frame->backbuf->colour.image, VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
}
char *VKVID_GetRGBInfo (int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt)
{
//with vulkan, we need to create a staging image to write into, submit a copy, wait for completion, map the copy, copy that out, free the staging.
//its enough to make you pitty anyone that writes opengl drivers.
if (VK_SCR_GrabBackBuffer())
{
void *imgdata, *outdata;
uint32_t y;
struct vk_fencework *fence = VK_FencedBegin(NULL, 0);
VkImageCopy icpy;
VkImage tempimage;
@ -1944,8 +2103,9 @@ char *VKVID_GetRGBInfo (int *truevidwidth, int *truevidheight, enum uploadfmt
VK_FencedSync(fence);
outdata = BZ_Malloc(4*vid.pixelwidth*vid.pixelheight);
outdata = BZ_Malloc(4*vid.pixelwidth*vid.pixelheight); //FIXME: this sucks.
*fmt = PTI_BGRA8;
*bytestride = vid.pixelwidth*4;
*truevidwidth = vid.pixelwidth;
*truevidheight = vid.pixelheight;
@ -1954,8 +2114,7 @@ char *VKVID_GetRGBInfo (int *truevidwidth, int *truevidheight, enum uploadfmt
subres.arrayLayer = 0;
vkGetImageSubresourceLayout(vk.device, tempimage, &subres, &layout);
VkAssert(vkMapMemory(vk.device, tempmemory, 0, mem_reqs.size, 0, &imgdata));
for (y = 0; y < vid.pixelheight; y++)
memcpy((char*)outdata + (vid.pixelheight-1-y)*vid.pixelwidth*4, (char*)imgdata + layout.offset + y*layout.rowPitch, vid.pixelwidth*4);
memcpy(outdata, imgdata, 4*vid.pixelwidth*vid.pixelheight);
vkUnmapMemory(vk.device, tempmemory);
vkDestroyImage(vk.device, tempimage, vkallocationcb);
@ -2121,13 +2280,6 @@ qboolean VK_SCR_GrabBackBuffer(void)
VK_FencedCheck();
if (!vk.surface)
{
// if (Media_Capturing() != 2)
// Cmd_ExecuteString("quit force\n", RESTRICT_LOCAL);
return false; //headless...
}
if (!vk.unusedframes)
{
struct vkframe *newframe = Z_Malloc(sizeof(*vk.frame));
@ -2139,6 +2291,9 @@ qboolean VK_SCR_GrabBackBuffer(void)
while (vk.aquirenext == vk.aquirelast)
{ //we're still waiting for the render thread to increment acquirelast.
Sys_Sleep(0); //o.O
#ifdef _WIN32
Sys_SendKeyEvents();
#endif
}
//wait for the queued acquire to actually finish
@ -2332,12 +2487,13 @@ void VK_DebugFramerate(void)
qboolean VK_SCR_UpdateScreen (void)
{
uint32_t fblayout;
VkCommandBuffer bufs[1];
VK_FencedCheck();
//a few cvars need some extra work if they're changed
if (vk_submissionthread.modified || vid_vsync.modified || vid_triplebuffer.modified || vid_srgb.modified)
if ((vk.allowsubmissionthread && vk_submissionthread.modified) || vid_vsync.modified || vid_triplebuffer.modified || vid_srgb.modified)
{
vid_vsync.modified = false;
vid_triplebuffer.modified = false;
@ -2371,7 +2527,7 @@ qboolean VK_SCR_UpdateScreen (void)
VK_CreateSwapChain();
vk.neednewswapchain = false;
if (vk_submissionthread.ival || !*vk_submissionthread.string)
if (vk.allowsubmissionthread && (vk_submissionthread.ival || !*vk_submissionthread.string))
{
vk.submitthread = Sys_CreateThread("vksubmission", VK_Submit_Thread, NULL, THREADP_HIGHEST, 0);
}
@ -2390,12 +2546,49 @@ qboolean VK_SCR_UpdateScreen (void)
vkCmdEndRenderPass(vk.frame->cbuf);
fblayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
/*if (0)
{
vkscreencapture_t *capt = VK_AtFrameEnd(atframeend, sizeof(vkscreencapture_t));
VkImageMemoryBarrier imgbarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
VkBufferImageCopy region;
imgbarrier.pNext = NULL;
imgbarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imgbarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
imgbarrier.oldLayout = fblayout;
imgbarrier.newLayout = fblayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
imgbarrier.image = vk.frame->backbuf->colour.image;
imgbarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imgbarrier.subresourceRange.baseMipLevel = 0;
imgbarrier.subresourceRange.levelCount = 1;
imgbarrier.subresourceRange.baseArrayLayer = 0;
imgbarrier.subresourceRange.layerCount = 1;
imgbarrier.srcQueueFamilyIndex = vk.queuefam[0];
imgbarrier.dstQueueFamilyIndex = vk.queuefam[0];
vkCmdPipelineBarrier(vk.frame->cbuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &imgbarrier);
region.bufferOffset = 0;
region.bufferRowLength = 0; //tightly packed
region.bufferImageHeight = 0; //tightly packed
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset.x = 0;
region.imageOffset.y = 0;
region.imageOffset.z = 0;
region.imageExtent.width = capt->imagewidth = vk.frame->backbuf->colour.width;
region.imageExtent.height = capt->imageheight = vk.frame->backbuf->colour.height;
region.imageExtent.depth = 1;
vkCmdCopyImageToBuffer(vk.frame->cbuf, vk.frame->backbuf->colour.image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, 1, &region);
}*/
{
VkImageMemoryBarrier imgbarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
imgbarrier.pNext = NULL;
imgbarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imgbarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT|VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
imgbarrier.dstAccessMask = 0;
imgbarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
imgbarrier.oldLayout = fblayout;
imgbarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
imgbarrier.image = vk.frame->backbuf->colour.image;
imgbarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
@ -2758,14 +2951,15 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat
const char *extensions[8];
qboolean nvglsl = false;
uint32_t extensions_count = 0;
qboolean headless = false;
vk.allowsubmissionthread = true;
vk.headless = false;
if (sysextname)
{
extensions[extensions_count++] = sysextname;
extensions[extensions_count++] = VK_KHR_SURFACE_EXTENSION_NAME;
}
else
headless = true;
vk.headless = true;
if (vk_debug.ival)
extensions[extensions_count++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME;
@ -2896,7 +3090,7 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat
vkGetPhysicalDeviceProperties(devs[i], &props);
vkGetPhysicalDeviceQueueFamilyProperties(devs[i], &queue_count, NULL);
if (!headless)
if (!vk.headless)
{
for (j = 0; j < queue_count; j++)
{
@ -3045,7 +3239,7 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat
for (i = 0; i < queue_count; i++)
{
VkBool32 supportsPresent = false;
if (headless)
if (vk.headless)
supportsPresent = true; //won't be used anyway.
else
VkAssert(vkGetPhysicalDeviceSurfaceSupportKHR(vk.gpu, i, vk.surface, &supportsPresent));
@ -3124,7 +3318,8 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat
else
{
queueinf[0].queueCount = 1;
vk.dopresent = VK_DoPresent; //can't split submit+present onto different queues, so do these on a single thread.
if (!vk.headless)
vk.dopresent = VK_DoPresent; //can't split submit+present onto different queues, so do these on a single thread.
Con_DPrintf("Using single queue\n");
}
}
@ -3242,7 +3437,7 @@ qboolean VK_Init(rendererstate_t *info, const char *sysextname, qboolean (*creat
{
vk.neednewswapchain = false;
if (vk_submissionthread.ival || !*vk_submissionthread.string)
if (vk.allowsubmissionthread && (vk_submissionthread.ival || !*vk_submissionthread.string))
{
vk.submitthread = Sys_CreateThread("vksubmission", VK_Submit_Thread, NULL, THREADP_HIGHEST, 0);
}

View File

@ -83,6 +83,7 @@
VKFunc(CmdClearDepthStencilImage) \
VKFunc(CmdCopyImage) \
VKFunc(CmdCopyBuffer) \
VKFunc(CmdCopyImageToBuffer) \
VKFunc(CmdBlitImage) \
VKFunc(CmdPipelineBarrier) \
VKFunc(CmdSetEvent) \
@ -231,6 +232,8 @@ extern struct vulkaninfo_s
{
unsigned short triplebuffer;
qboolean vsync;
qboolean headless;
qboolean allowsubmissionthread;
VkInstance instance;
VkDevice device;
@ -435,8 +438,11 @@ void VK_R_Init (void);
void VK_R_DeInit (void);
void VK_R_RenderView (void);
char *VKVID_GetRGBInfo (int *truevidwidth, int *truevidheight, enum uploadfmt *fmt);
char *VKVID_GetRGBInfo (int *bytestride, int *truevidwidth, int *truevidheight, enum uploadfmt *fmt);
qboolean VK_SCR_UpdateScreen (void);
void VKBE_RenderToTextureUpdate2d(qboolean destchanged);
//improved rgb get that calls the callback when the data is actually available. used for video capture.
void VKVID_QueueGetRGBData (void (*gotrgbdata) (void *rgbdata, qintptr_t bytestride, size_t width, size_t height, enum uploadfmt fmt));

View File

@ -1,5 +1,5 @@
//emscripten's download mechanism lacks usable progress indicators.
void emscriptenfte_async_wget_data2(const char *url, void *ctx, void (*onload)(void*ctx,void*buf,int sz), void (*onerror)(void*ctx,int code), void (*onprogress)(void*ctx,int prog,int total));
void emscriptenfte_async_wget_data2(const char *url, void *ctx, void (*onload)(void*ctx,int buf), void (*onerror)(void*ctx,int code), void (*onprogress)(void*ctx,int prog,int total));
//changes the page away from quake (oh noes!) or downloads something.
void emscriptenfte_window_location(const char *url);
@ -18,11 +18,15 @@ int emscriptenfte_buf_read(int handle, int offset, void *data, int len);//read d
int emscriptenfte_buf_write(int handle, int offset, const void *data, int len);//write data. no access checks.
//websocket is implemented in javascript because there is no usable C api (emscripten's javascript implementation is shite and has fatal errors).
int emscriptenfte_ws_connect(const char *url); //open a websocket connect to a specific host
void emscriptenfte_ws_close(int sockid); //close it again
int emscriptenfte_ws_connect(const char *url, const char *wsprotocol); //open a websocket connection to a specific host
void emscriptenfte_ws_close(int sockid); //close it again
int emscriptenfte_ws_cansend(int sockid, int extra, int maxpending); //returns false if we're blocking for some reason. avoids overflowing. everything is otherwise reliable.
int emscriptenfte_ws_send(int sockid, const void *data, int len); //send data to the peer. queues data. never dropped.
int emscriptenfte_ws_recv(int sockid, void *data, int len); //receive data from the peer.
int emscriptenfte_ws_send(int sockid, const void *data, int len); //send data to the peer. queues data. never dropped.
int emscriptenfte_ws_recv(int sockid, void *data, int len); //receive data from the peer.
int emscriptenfte_rtc_create(int clientside, void *ctxp, int ctxi, void(*cb)(void *ctxp, int ctxi, int type, const char *data)); //open a webrtc connection to a specific broker url
void emscriptenfte_rtc_offer(int sock, const char *offer, const char *sdptype);//sets the remote sdp.
void emscriptenfte_rtc_candidate(int sock, const char *offer); //adds a remote candidate.
//misc stuff for printf replacements
void emscriptenfte_alert(const char *msg);

View File

@ -56,8 +56,9 @@ mergeInto(LibraryManager.library,
emscriptenfte_buf_createfromarraybuf__deps : ['emscriptenfte_handle_alloc'],
emscriptenfte_buf_createfromarraybuf : function(buf)
{
buf = new Uint8Array(buf);
var len = buf.length;
var b = {h:-1, r:1, l:len,m:len,d:new Uint8Array(buf), n:null};
var b = {h:-1, r:1, l:len,m:len,d:buf, n:null};
b.h = _emscriptenfte_handle_alloc(b);
return b.h;
},
@ -69,6 +70,7 @@ mergeInto(LibraryManager.library,
pointerislocked:0,
pointerwantlock:0,
linebuffer:'',
localstorefailure:false,
w: -1,
h: -1,
donecb:0,
@ -400,6 +402,8 @@ mergeInto(LibraryManager.library,
emscriptenfte_setupcanvas__deps: ['$FTEC', '$Browser', 'emscriptenfte_buf_createfromarraybuf'],
emscriptenfte_setupcanvas : function(nw,nh,evresize,evmouse,evmbutton,evkey,evfile,evjbutton,evjaxis,evwantfullscreen)
{
try
{
FTEC.evcb.resize = evresize;
FTEC.evcb.mouse = evmouse;
FTEC.evcb.button = evmbutton;
@ -499,6 +503,10 @@ mergeInto(LibraryManager.library,
}
_emscriptenfte_updatepointerlock(false, false);
} catch(e)
{
console.log(e);
}
return 1;
},
@ -576,22 +584,35 @@ mergeInto(LibraryManager.library,
var r = -1;
if (f == null)
{
if (window.localStorage && createifneeded != 2)
if (!FTEC.localstorefailure)
{
var str = window.localStorage.getItem(name);
if (str != null)
try
{
// console.log('read file '+name+': ' + str);
if (localStorage && createifneeded != 2)
{
var str = localStorage.getItem(name);
if (str != null)
{
// console.log('read file '+name+': ' + str);
var len = str.length;
var buf = new Uint8Array(len);
for (var i = 0; i < len; i++)
buf[i] = str.charCodeAt(i);
var len = str.length;
var buf = new Uint8Array(len);
for (var i = 0; i < len; i++)
buf[i] = str.charCodeAt(i);
var b = {h:-1, r:2, l:len,m:len,d:buf, n:name};
r = b.h = _emscriptenfte_handle_alloc(b);
FTEH.f[name] = b;
return b.h;
var b = {h:-1, r:2, l:len,m:len,d:buf, n:name};
r = b.h = _emscriptenfte_handle_alloc(b);
FTEH.f[name] = b;
return b.h;
}
}
}
catch(e)
{
console.log('exception while trying to read local storage for ' + name);
console.log(e);
console.log('disabling further attempts to access local storage');
FTEC.localstorefailure = true;
}
}
@ -655,16 +676,24 @@ mergeInto(LibraryManager.library,
return;
var data = b.d;
var len = b.l;
if (window.localStorage)
try
{
var foo = "";
//use a divide and conquer implementation instead for speed?
for (var i = 0; i < len; i++)
foo += String.fromCharCode(data[i]);
window.localStorage.setItem(b.n, foo);
if (localStorage)
{
var foo = "";
//use a divide and conquer implementation instead for speed?
for (var i = 0; i < len; i++)
foo += String.fromCharCode(data[i]);
localStorage.setItem(b.n, foo);
}
else
console.log('local storage not supported');
}
catch (e)
{
console.log('exception while trying to save ' + b.n);
console.log(e);
}
else
console.log('local storage not supported');
},
emscriptenfte_buf_release : function(handle)
{
@ -722,13 +751,19 @@ mergeInto(LibraryManager.library,
},
emscriptenfte_ws_connect__deps: ['emscriptenfte_handle_alloc'],
emscriptenfte_ws_connect : function(url)
emscriptenfte_ws_connect : function(brokerurl, protocolname)
{
var _url = Pointer_stringify(url);
var _url = Pointer_stringify(brokerurl);
var _protocol = Pointer_stringify(protocolname);
var s = {ws:null, inq:[], err:0, con:0};
s.ws = new WebSocket(_url, 'binary');
try {
s.ws = new WebSocket(_url, _protocol);
} catch(err) { console.log(err); }
if (s.ws === undefined)
return -1;
if (s.ws == null)
return -1;
s.ws.binaryType = 'arraybuffer';
s.ws.onerror = function(event) {s.con = 0; s.err = 1;};
s.ws.onclose = function(event) {s.con = 0; s.err = 1;};
s.ws.onopen = function(event) {s.con = 1;};
@ -745,8 +780,26 @@ mergeInto(LibraryManager.library,
var s = FTEH.h[sockid];
if (s === undefined)
return -1;
s.ws.close();
s.ws = null; //make sure to avoid circular references
s.callcb = null;
if (s.ws != null)
{
s.ws.close();
s.ws = null; //make sure to avoid circular references
}
if (s.pc != null)
{
s.pc.close();
s.pc = null; //make sure to avoid circular references
}
if (s.broker != null)
{
s.broker.close();
s.broker = null; //make sure to avoid circular references
}
delete FTEH.h[sockid]; //socked is no longer accessible.
return 0;
},
@ -763,9 +816,9 @@ mergeInto(LibraryManager.library,
var s = FTEH.h[sockid];
if (s === undefined)
return -1;
if (s.con == 0 || len < 1 || len > 125)
if (s.con == 0)
return 0; //not connected yet
s.ws.send(HEAPU8.subarray(data, data+len).buffer);
s.ws.send(HEAPU8.subarray(data, data+len));
return len;
},
emscriptenfte_ws_recv : function(sockid, data, len)
@ -786,7 +839,189 @@ mergeInto(LibraryManager.library,
return 0;
},
emscriptenfte_rtc_create__deps: ['emscriptenfte_handle_alloc'],
emscriptenfte_rtc_create : function(clientside, ctxp, ctxi, callback)
{
var pcconfig = {
iceServers:
[{
url: 'stun:stun.l.google.com:19302'
}]
};
var dcconfig = {ordered: false, maxRetransmits: 0, reliable:false};
console.log("emscriptenfte_rtc_create");
var s = {pc:null, ws:null, inq:[], err:0, con:0, isclient:clientside, callcb:
function(evtype,stringdata)
{ //private helper
console.log("emscriptenfte_rtc_create callback: " + evtype);
var stringlen = (stringdata.length*3)+1;
var dataptr = _malloc(stringlen);
stringToUTF8(stringdata, dataptr, stringlen);
Runtime.dynCall('viiii', callback, [ctxp,ctxi,evtype,dataptr]);
_free(dataptr);
}
};
if (RTCPeerConnection === undefined)
{ //IE or something.
console.log("RTCPeerConnection undefined");
return -1;
}
s.pc = new RTCPeerConnection(pcconfig);
if (s.pc === undefined)
{
console.log("webrtc failed to create RTCPeerConnection");
return -1;
}
//create the dataconnection
s.ws = s.pc.createDataChannel('quake', dcconfig);
s.ws.binaryType = 'arraybuffer';
s.ws.onclose = function(event)
{
console.log("webrtc datachannel closed:")
console.log(event);
s.con = 0;
s.err = 1;
};
s.ws.onopen = function(event)
{
console.log("webrtc datachannel opened:");
console.log(event);
s.con = 1;
};
s.ws.onmessage = function(event)
{
//console.log("webrtc datachannel message:");
//console.log(event);
assert(typeof event.data !== 'string' && event.data.byteLength);
s.inq.push(new Uint8Array(event.data));
};
s.pc.onicecandidate = function(e)
{
console.log("onicecandidate: ");
console.log(e);
var desc;
if (1)
desc = JSON.stringify(e.candidate);
else
desc = e.candidate.candidate;
s.callcb(4, desc);
};
s.pc.oniceconnectionstatechange = function(e)
{
console.log("oniceconnectionstatechange: ");
console.log(e);
};
s.pc.onaddstream = function(e)
{
console.log("onaddstream: ");
console.log(e);
};
s.pc.ondatachannel = function(e)
{
console.log("ondatachannel: ");
console.log(e);
s.recvchan = e.channel;
s.recvchan.binaryType = 'arraybuffer';
s.recvchan.onmessage = s.ws.onmessage;
};
s.pc.onnegotiationneeded = function(e)
{
console.log("onnegotiationneeded: ");
console.log(e);
};
if (clientside)
{
s.pc.createOffer().then(
function(desc)
{
s.pc.setLocalDescription(desc);
console.log("gotlocaldescription: ");
console.log(desc);
if (1)
desc = JSON.stringify(desc);
else
desc = desc.sdp;
s.callcb(3, desc);
},
function(event)
{
console.log("createOffer error:");
console.log(event);
s.err = 1;
}
);
}
return _emscriptenfte_handle_alloc(s);
},
emscriptenfte_rtc_offer : function(sockid, offer, offertype)
{
var s = FTEH.h[sockid];
offer = Pointer_stringify(offer);
offertype = Pointer_stringify(offertype);
if (s === undefined)
return -1;
if (1)
desc = JSON.parse(offer);
else
desc = {sdp:offer, type:offertype};
s.pc.setRemoteDescription(desc);
if (!s.isclient)
{ //server must give a response.
s.pc.createAnswer().then(
function(desc)
{
s.pc.setLocalDescription(desc);
console.log("gotlocaldescription: ");
console.log(desc);
if (1)
desc = JSON.stringify(desc);
else
desc = desc.sdp;
s.callcb(3, desc);
},
function(event)
{
console.log("createAnswer error:" + event.toString());
s.err = 1;
}
);
}
},
emscriptenfte_rtc_candidate : function(sockid, offer)
{
var s = FTEH.h[sockid];
offer = Pointer_stringify(offer);
if (s === undefined)
return -1;
var desc;
if (1)
desc = JSON.parse(offer);
else
desc = {candidate:offer, sdpMid:null, sdpMLineIndex:0};
console.log("addIceCandidate:");
console.log(desc);
s.pc.addIceCandidate(desc);
},
emscriptenfte_async_wget_data2 : function(url, ctx, onload, onerror, onprogress)
{
@ -810,11 +1045,8 @@ mergeInto(LibraryManager.library,
console.log("onload: " + _url + " status " + http.status);
if (http.status == 200)
{
var bar = new Uint8Array(http.response);
var buf = _malloc(bar.length);
HEAPU8.set(bar, buf);
if (onload)
Runtime.dynCall('viii', onload, [ctx, buf, bar.length]);
Runtime.dynCall('vii', onload, [ctx, _emscriptenfte_buf_createfromarraybuf(http.response)]);
}
else
{

Some files were not shown because too many files have changed in this diff Show More