diff --git a/engine/Makefile b/engine/Makefile index ad45f2ebe..ff642af9b 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -1624,11 +1624,6 @@ help: @-echo "vc - Attempts to use msvc8+ to compile. Note: uses profile guided optimisations. You must build+run the relevent profile target before a release target will compile properly. Debug doesn't care." @-echo "android, npfte, nacl targets explicitly cross compile, and should generally not be given an FTE_TARGET." -install: - -cp debug/*.* /opt/quake/ - -cp release/*.* /opt/quake/ - -cp profile/*.* /opt/quake - clean: -rm -f -r $(RELEASE_DIR) -rm -f -r $(DEBUG_DIR) @@ -1847,3 +1842,16 @@ else test -f libvorbis-$(VORBISVER).tar.gz || wget http://downloads.xiph.org/releases/vorbis/libvorbis-$(VORBISVER).tar.gz -test -f libs-$(ARCH)/libvorbisfile.a || (cd libs-$(ARCH) && tar -xvzf ../libvorbis-$(VORBISVER).tar.gz && cd libvorbis-$(VORBISVER) && $(TOOLOVERRIDES) ./configure PKG_CONFIG= $(CONFIGARGS) --disable-oggtest --with-ogg-libraries=.. --with-ogg-includes=$(NATIVE_ABSBASE_DIR)/libs-$(ARCH)/libogg-$(OGGVER)/include && $(TOOLOVERRIDES) $(MAKE) && cp lib/.libs/libvorbis.a ../ && cp lib/.libs/libvorbisfile.a ../ ) endif + + +prefix ?= /usr/local +exec_prefix ?= $(prefix) +bindir ?= $(exec_prefix)/bin +sbindir ?= $(exec_prefix)/sbin +INSTALL ?= install +INSTALL_PROGRAM ?= $(INSTALL) +INSTALL_DATA ?= ${INSTALL} -m 644 +install: sv-rel gl-rel mingl-rel qcc-rel + $(INSTALL_PROGRAM) $(RELEASE_DIR)fteqw $(DESTDIR)$(bindir)/fteqw + $(INSTALL_PROGRAM) $(RELEASE_DIR)fteqwsv $(DESTDIR)$(bindir)/fteqwsv + $(INSTALL_PROGRAM) $(RELEASE_DIR)fteqcc $(DESTDIR)$(bindir)/fteqcc \ No newline at end of file diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index 1da1fb4a2..a9d5ac65b 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -1101,10 +1101,10 @@ float CL_FilterTime (double time, float wantfps, qboolean ignoreserver) //now re fps = bound (6.7, wantfps, fpscap); //we actually cap ourselves to 150msecs (1000/7 = 142) } - if (time < 1000 / fps) + if (time < ceil(1000 / fps)) return 0; - return time - (1000 / fps); + return time - ceil(1000 / fps); } qboolean allowindepphys; diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 22aa395b1..a2003c451 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -333,6 +333,9 @@ CL_Quit_f */ void CL_Quit_f (void) { + if (!host_initialized) + return; + if (forcesaveprompt && strcmp(Cmd_Argv(1), "force")) { forcesaveprompt = false; @@ -4586,7 +4589,7 @@ double Host_Frame (double time) #endif Key_Dest_Has(kdm_menu) || Key_Dest_Has(kdm_editor) || - cl.paused; + cl.paused || !ActiveApp; // TODO: check if minimized or unfocused //read packets early and always, so we don't have stuff waiting for reception quite so often. @@ -5238,6 +5241,8 @@ void Host_Shutdown(void) } host_initialized = false; + HTTP_CL_Terminate(); + #ifdef PLUGINS Plug_Shutdown(false); #endif diff --git a/engine/client/cl_screen.c b/engine/client/cl_screen.c index a536bfc5d..4cbfd2723 100644 --- a/engine/client/cl_screen.c +++ b/engine/client/cl_screen.c @@ -1959,12 +1959,12 @@ typedef struct _TargaHeader { #if defined(AVAIL_JPEGLIB) && !defined(NO_JPEG) -qboolean screenshotJPEG(char *filename, int compression, qbyte *screendata, int screenwidth, int screenheight); +qboolean screenshotJPEG(char *filename, enum fs_relative fsroot, int compression, qbyte *screendata, int screenwidth, int screenheight); #endif #ifdef AVAIL_PNGLIB -int Image_WritePNG (char *filename, int compression, qbyte *pixels, int width, int height); +int Image_WritePNG (char *filename, enum fs_relative fsroot, int compression, qbyte *pixels, int width, int height); #endif -void WriteBMPFile(char *filename, qbyte *in, int width, int height); +void WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, int width, int height); /* Find closest color in the palette for named color @@ -1999,9 +1999,9 @@ int MipColor(int r, int g, int b) return best; } -qboolean SCR_ScreenShot (char *filename, void *rgb_buffer, int width, int height) +qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void *rgb_buffer, int width, int height) { - int i, c, temp; + int i, c, temp; #if defined(AVAIL_PNGLIB) || defined(AVAIL_JPEGLIB) extern cvar_t scr_sshot_compression; #endif @@ -2016,14 +2016,14 @@ qboolean SCR_ScreenShot (char *filename, void *rgb_buffer, int width, int height #ifdef AVAIL_PNGLIB if (!Q_strcasecmp(ext, "png")) { - return Image_WritePNG(filename, scr_sshot_compression.value, rgb_buffer, width, height); + return Image_WritePNG(filename, fsroot, scr_sshot_compression.value, rgb_buffer, width, height); } else #endif #ifdef AVAIL_JPEGLIB if (!Q_strcasecmp(ext, "jpeg") || !Q_strcasecmp(ext, "jpg")) { - return screenshotJPEG(filename, scr_sshot_compression.value, rgb_buffer, width, height); + return screenshotJPEG(filename, fsroot, scr_sshot_compression.value, rgb_buffer, width, height); } else #endif @@ -2049,13 +2049,13 @@ qboolean SCR_ScreenShot (char *filename, void *rgb_buffer, int width, int height } } - WritePCXfile (filename, newbuf, width, height, width, host_basepal, false); + WritePCXfile (filename, fsroot, newbuf, width, height, width, host_basepal, false); } else if (!Q_strcasecmp(ext, "tga")) //tga { vfsfile_t *vfs; - FS_CreatePath(filename, FS_GAMEONLY); - vfs = FS_OpenVFS(filename, "wb", FS_GAMEONLY); + FS_CreatePath(filename, fsroot); + vfs = FS_OpenVFS(filename, "wb", fsroot); if (vfs) { unsigned char header[18]; @@ -2144,7 +2144,7 @@ void SCR_ScreenShot_f (void) rgbbuffer = VID_GetRGBInfo(0, &width, &height); if (rgbbuffer) { - if (SCR_ScreenShot(pcxname, rgbbuffer, width, height)) + if (SCR_ScreenShot(pcxname, FS_GAMEONLY, rgbbuffer, width, height)) { Con_Printf ("Wrote %s\n", sysname); BZ_Free(rgbbuffer); @@ -2217,7 +2217,7 @@ void SCR_ScreenShot_Mega_f(void) rgbbuffer = VID_GetRGBInfo(0, &width, &height); if (rgbbuffer) { - if (SCR_ScreenShot(filename, rgbbuffer, width, height)) + if (SCR_ScreenShot(filename, FS_GAMEONLY, rgbbuffer, width, height)) { char sysname[1024]; FS_NativePath(filename, FS_GAMEONLY, sysname, sizeof(sysname)); @@ -2385,7 +2385,7 @@ qboolean SCR_RSShot (void) Q_strncpyz(st, name.string, sizeof(st)); SCR_DrawStringToSnap (st, newbuf, w - strlen(st)*8, h - 21, w); - WritePCXfile ("snap.pcx", newbuf, w, h, w, host_basepal, true); + WritePCXfile ("snap.pcx", FS_GAMEONLY, newbuf, w, h, w, host_basepal, true); BZ_Free(newbuf); diff --git a/engine/client/image.c b/engine/client/image.c index 6e54f834d..7d9e42ffa 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -977,7 +977,7 @@ error: #ifndef NPFTE -int Image_WritePNG (char *filename, int compression, qbyte *pixels, int width, int height) +int Image_WritePNG (char *filename, enum fs_relative fsroot, int compression, qbyte *pixels, int width, int height) { char name[MAX_OSPATH]; int i; @@ -987,7 +987,7 @@ int Image_WritePNG (char *filename, int compression, qbyte *pixels, int width, i png_byte **row_pointers; struct pngerr errctx; - if (!FS_NativePath(filename, FS_GAMEONLY, name, sizeof(name))) + if (!FS_NativePath(filename, fsroot, name, sizeof(name))) return false; if (!LibPNG_Init()) @@ -1533,7 +1533,7 @@ METHODDEF(void) jpeg_error_exit (j_common_ptr cinfo) { longjmp(((jpeg_error_mgr_wrapper *) cinfo->err)->setjmp_buffer, 1); } -qboolean screenshotJPEG(char *filename, int compression, qbyte *screendata, int screenwidth, int screenheight) //input is rgb NOT rgba +qboolean screenshotJPEG(char *filename, enum fs_relative fsroot, int compression, qbyte *screendata, int screenwidth, int screenheight) //input is rgb NOT rgba { qbyte *buffer; vfsfile_t *outfile; @@ -1544,10 +1544,10 @@ qboolean screenshotJPEG(char *filename, int compression, qbyte *screendata, int if (!LIBJPEG_LOADED()) return false; - if (!(outfile = FS_OpenVFS(filename, "wb", FS_GAMEONLY))) + if (!(outfile = FS_OpenVFS(filename, "wb", fsroot))) { - FS_CreatePath (filename, FS_GAMEONLY); - if (!(outfile = FS_OpenVFS(filename, "wb", FS_GAMEONLY))) + FS_CreatePath (filename, fsroot); + if (!(outfile = FS_OpenVFS(filename, "wb", fsroot))) { Con_Printf("Error opening %s\n", filename); return false; @@ -1632,7 +1632,7 @@ qboolean screenshotJPEG(char *filename, int compression, qbyte *screendata, int WritePCXfile ============== */ -void WritePCXfile (const char *filename, qbyte *data, int width, int height, +void WritePCXfile (const char *filename, enum fs_relative fsroot, qbyte *data, int width, int height, int rowbytes, qbyte *palette, qboolean upload) //data is 8bit. { int i, j, length; @@ -1695,7 +1695,7 @@ void WritePCXfile (const char *filename, qbyte *data, int width, int height, if (upload) CL_StartUpload((void *)pcx, length); else - COM_WriteFile (filename, pcx, length); + COM_WriteFile (filename, fsroot, pcx, length); } #endif diff --git a/engine/client/keys.c b/engine/client/keys.c index 9110bd397..f9f7b7cba 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -30,7 +30,6 @@ void Editor_Key(int key, int unicode); void Key_ConsoleInsert(char *instext); void Key_ClearTyping (void); -#define KEY_MODIFIERSTATES 8 unsigned char *key_lines[CON_EDIT_LINES_MASK+1]; int key_linepos; int shift_down=false; @@ -1561,11 +1560,11 @@ int Key_StringToKeynum (const char *str, int *modifier) if (!underscore || !underscore[1]) break; //nothing afterwards or no underscore. if (!strnicmp(str, "shift_", 6)) - *modifier |= 1; + *modifier |= KEY_MODIFIER_SHIFT; else if (!strnicmp(str, "alt_", 4)) - *modifier |= 2; + *modifier |= KEY_MODIFIER_ALT; else if (!strnicmp(str, "ctrl_", 5)) - *modifier |= 4; + *modifier |= KEY_MODIFIER_CTRL; else break; str = underscore+1; //next char. @@ -1616,7 +1615,7 @@ char *Key_KeynumToString (int keynum) if (keynum == -1) return ""; - if (keynum > 32 && keynum < 127) + if (keynum > 32 && keynum < 127 && keynum != '\'' && keynum != '\"') { // printable ascii tinystr[0] = keynum; tinystr[1] = 0; @@ -1853,11 +1852,11 @@ void Key_WriteBindings (vfsfile_t *f) if (strcmp(binding, base) || (m==0 && keybindings[i][0]) || bindcmdlevel[i][m] != bindcmdlevel[i][0]) { *prefix = '\0'; - if (m & 4) + if (m & KEY_MODIFIER_CTRL) strcat(prefix, "CTRL_"); - if (m & 2) + if (m & KEY_MODIFIER_ALT) strcat(prefix, "ALT_"); - if (m & 1) + if (m & KEY_MODIFIER_SHIFT) strcat(prefix, "SHIFT_"); s = va("%s%s", prefix, Key_KeynumToString(i)); diff --git a/engine/client/keys.h b/engine/client/keys.h index e240a268a..99b2e8592 100644 --- a/engine/client/keys.h +++ b/engine/client/keys.h @@ -166,6 +166,11 @@ K_PRINTSCREEN = 248, K_MAX = 256 }; +#define KEY_MODIFIER_SHIFT (1<<0) +#define KEY_MODIFIER_ALT (1<<1) +#define KEY_MODIFIER_CTRL (1<<2) +#define KEY_MODIFIERSTATES (1<<3) + #define K_SHIFT K_LSHIFT #define K_CTRL K_LCTRL #define K_ALT K_LALT diff --git a/engine/client/m_download.c b/engine/client/m_download.c index 7e5092320..05de7347e 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -6,7 +6,7 @@ #ifdef DOWNLOADMENU -#define ROOTDOWNLOADABLESSOURCE "http://fteqw.com/downloadables.txt" +#define ROOTDOWNLOADABLESSOURCE "http://fte.triptohell.info/downloadables.txt" #define INSTALLEDFILES "installed.lst" //the file that resides in the quakedir (saying what's installed). #define DPF_HAVEAVERSION 1 //any old version diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index 51e79b3bd..bc65a03e4 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -2530,6 +2530,7 @@ void *currentcapture_ctx; struct capture_raw_ctx { int frames; + enum fs_relative fsroot; char videonameprefix[MAX_QPATH]; char videonameextension[16]; vfsfile_t *audio; @@ -2544,7 +2545,12 @@ static void *QDECL capture_raw_begin (char *streamname, int videorate, int width else Q_strncpyz(ctx->videonameextension, "tga", sizeof(ctx->videonameextension)); - Q_strncpyz(ctx->videonameprefix, streamname, sizeof(ctx->videonameprefix)); + if (!FS_NativePath(va("%s", streamname), FS_GAMEONLY, ctx->videonameprefix, sizeof(ctx->videonameprefix))) + { + Z_Free(ctx); + return NULL; + } + ctx->fsroot = FS_SYSTEM; ctx->audio = NULL; if (*sndkhz) { @@ -2558,8 +2564,8 @@ static void *QDECL capture_raw_begin (char *streamname, int videorate, int width if (*sndchannels < 1) *sndchannels = 1; Q_snprintfz(filename, sizeof(filename), "%s/audio_%ichan_%ikhz_%ib.raw", ctx->videonameprefix, *sndchannels, *sndkhz/1000, *sndbits); - FS_CreatePath(filename, FS_GAMEONLY); - ctx->audio = FS_OpenVFS(filename, "wb", FS_GAMEONLY); + FS_CreatePath(filename, ctx->fsroot); + ctx->audio = FS_OpenVFS(filename, "wb", ctx->fsroot); } if (!ctx->audio) { @@ -2575,7 +2581,7 @@ static void QDECL capture_raw_video (void *vctx, void *data, int frame, int widt 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, data, width, height); + SCR_ScreenShot(filename, ctx->fsroot, data, width, height); } static void QDECL capture_raw_audio (void *vctx, void *data, int bytes) { diff --git a/engine/client/menu.c b/engine/client/menu.c index 35eb6b78d..25bf86567 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -105,31 +105,51 @@ void M_DrawTextBox (int x, int y, int width, int lines) M_DrawScalePic (cx, cy+8, 8, 8, p); } -int M_FindKeysForBind (char *command, int *keylist, int total) +int M_FindKeysForBind (char *command, int *keylist, int *keymods, int total) { int count; - int j; - int l; + int j, m; + int l, p; char *b; - for (count = 0; count < total; count++) - keylist[count] = -1; l = strlen(command); count = 0; for (j=0 ; j<256 ; j++) { - b = keybindings[j][0]; - if (!b) - continue; - if (!strncmp (b, command, l) && (!b[l] || b[l] == ' ' || b[l] == ';')) + for (m = 0; m < KEY_MODIFIERSTATES; m++) { - keylist[count] = j; - count++; - if (count == total) - break; + b = keybindings[j][m]; + if (!b) + continue; + if (!strncmp (b, command, l) && (!b[l] || b[l] == ' ' || b[l] == ';')) + { + //if ctrl_a and ctrl_shift_a do the same thing, don't report ctrl_shift_a because its redundant. + for (p = 0; p < m; p++) + { + if (p&~m) //ignore shift_a if we're checking ctrl_a + continue; + if (!strcmp(keybindings[j][p], b)) + break; //break+continue + } + if (p != m) + continue; + + keylist[count] = j; + if (keymods) + keymods[count] = j; + count++; + if (count == total) + return count; + } } } + for (j = count; j < total; j++) + { + keylist[j] = -1; + if (keymods) + keymods[j] = 0; + } return count; } void M_FindKeysForCommand (int pnum, const char *command, int *twokeys) @@ -160,7 +180,7 @@ void M_FindKeysForCommand (int pnum, const char *command, int *twokeys) prefix[3] = 0; } } - M_FindKeysForBind(va("%s%s", prefix, command), twokeys, 2); + M_FindKeysForBind(va("%s%s", prefix, command), twokeys, NULL, 2); } #ifndef NOBUILTINMENUS diff --git a/engine/client/menu.h b/engine/client/menu.h index c29c5f454..1d9166187 100644 --- a/engine/client/menu.h +++ b/engine/client/menu.h @@ -468,6 +468,7 @@ void M_Keyup (int key, int unicode); void M_Draw (int uimenu); #endif void M_FindKeysForCommand (int pnum, const char *command, int *twokeys); +int M_FindKeysForBind (char *command, int *keylist, int *keymods, int total); void M_Media_Draw (void); void M_Media_Key (int key); diff --git a/engine/client/p_script.c b/engine/client/p_script.c index c05eb8775..cf846f07e 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -1170,7 +1170,7 @@ static void P_ParticleEffect_f(void) ptype->alpha = atof(value); else if (!strcmp(var, "alphachange")) { - Con_DPrintf("alphachange is deprechiated, use alphadelta\n"); + Con_DPrintf("alphachange is deprecated, use alphadelta\n"); ptype->alphachange = atof(value); } else if (!strcmp(var, "alphadelta")) @@ -1195,7 +1195,7 @@ static void P_ParticleEffect_f(void) } else if (!strcmp(var, "diesubrand")) { - Con_DPrintf("diesubrand is deprechiated, use die with two arguments\n"); + Con_DPrintf("diesubrand is deprecated, use die with two arguments\n"); ptype->randdie = atof(value); } @@ -1550,7 +1550,7 @@ static void P_ParticleEffect_f(void) } else if (!strcmp(var, "isbeam")) { - Con_DPrintf("isbeam is deprechiated, use type beam\n"); + Con_DPrintf("isbeam is deprecated, use type beam\n"); ptype->looks.type = PT_BEAM; } else if (!strcmp(var, "spawntime")) @@ -1589,22 +1589,22 @@ static void P_ParticleEffect_f(void) // old names else if (!strcmp(var, "areaspread")) { - Con_DPrintf("areaspread is deprechiated, use spawnorg\n"); + Con_DPrintf("areaspread is deprecated, use spawnorg\n"); ptype->areaspread = atof(value); } else if (!strcmp(var, "areaspreadvert")) { - Con_DPrintf("areaspreadvert is deprechiated, use spawnorg\n"); + Con_DPrintf("areaspreadvert is deprecated, use spawnorg\n"); ptype->areaspreadvert = atof(value); } else if (!strcmp(var, "offsetspread")) { - Con_DPrintf("offsetspread is deprechiated, use spawnvel\n"); + Con_DPrintf("offsetspread is deprecated, use spawnvel\n"); ptype->spawnvel = atof(value); } else if (!strcmp(var, "offsetspreadvert")) { - Con_DPrintf("offsetspreadvert is deprechiated, use spawnvel\n"); + Con_DPrintf("offsetspreadvert is deprecated, use spawnvel\n"); ptype->spawnvelvert = atof(value); } @@ -1640,7 +1640,7 @@ static void P_ParticleEffect_f(void) ptype->rampmode = RAMP_NONE; else if (!strcmp(value, "absolute")) { - Con_DPrintf("'rampmode absolute' is deprechiated, use 'rampmode nearest'\n"); + Con_DPrintf("'rampmode absolute' is deprecated, use 'rampmode nearest'\n"); ptype->rampmode = RAMP_NEAREST; } else if (!strcmp(value, "nearest")) @@ -2902,7 +2902,7 @@ static void P_ExportBuiltinSet_f(void) file = *partset_list[i].data; if (file) { - COM_WriteFile(va("particles/%s.cfg", efname), file, strlen(file)); + COM_WriteFile(va("particles/%s.cfg", efname), FS_GAMEONLY, file, strlen(file)); Con_Printf("Written particles/%s.cfg\n", efname); } else diff --git a/engine/client/pr_clcmd.c b/engine/client/pr_clcmd.c index 5eddfcfd2..505ae8a41 100644 --- a/engine/client/pr_clcmd.c +++ b/engine/client/pr_clcmd.c @@ -285,6 +285,7 @@ int MP_TranslateQCtoFTECodes(int code) void QCBUILTIN PF_cl_findkeysforcommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { const char *cmdname = PR_GetStringOfs(prinst, OFS_PARM0); + //float bindmap = G_FLOAT(OFS_PARM1); int keynums[2]; char keyname[512]; @@ -298,6 +299,26 @@ void QCBUILTIN PF_cl_findkeysforcommand (pubprogfuncs_t *prinst, struct globalva RETURN_TSTRING(keyname); } +void QCBUILTIN PF_cl_findkeysforcommandex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + const char *cmdname = PR_GetStringOfs(prinst, OFS_PARM0); + int keynums[256]; + int keymods[countof(keynums)]; + char keyname[512]; + int i, count; + + count = M_FindKeysForBind(cmdname, keynums, keymods, countof(keynums)); + + keyname[0] = '\0'; + + for (i = 0; i < count; i++) + { + Q_strncatz (keyname, va("%s%s%s%s ", (keymods[i]&KEY_MODIFIER_CTRL)?"CTRL_":"", (keymods[i]&KEY_MODIFIER_ALT)?"ALT_":"", (keymods[i]&KEY_MODIFIER_SHIFT)?"SHIFT_":"", Key_KeynumToString(keynums[i])), sizeof(keyname)); + } + + RETURN_TSTRING(keyname); +} + void QCBUILTIN PF_cl_getkeybind (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { char *binding = Key_GetBinding(MP_TranslateQCtoFTECodes(G_FLOAT(OFS_PARM0))); diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 94fa8e9b7..13579b555 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -184,6 +184,7 @@ extern sfx_t *cl_sfx_r_exp3; globalvector(input_cursor_impact, "input_cursor_trace_endpos"); /*float filled by getinputstate*/ \ globalfloat(input_cursor_entitynumber, "input_cursor_entitynumber"); /*float filled by getinputstate*/ \ \ + globalfloat(dimension_default, "dimension_default"); /*float default value for dimension_hit+dimension_solid*/ \ globalfloat(autocvar_vid_conwidth, "autocvar_vid_conwidth"); /*float hackfix for dp mods*/ \ globalfloat(autocvar_vid_conheight, "autocvar_vid_conheight"); /*float hackfix for dp mods*/ \ @@ -278,6 +279,7 @@ static void CSQC_ChangeLocalPlayer(int seat) static void CSQC_FindGlobals(void) { static float csphysicsmode = 0; + static float dimension_default = 255; #define globalfloat(name,qcname) csqcg.name = (float*)PR_FindGlobal(csqcprogs, qcname, 0, NULL); #define globalint(name,qcname) csqcg.name = (int*)PR_FindGlobal(csqcprogs, qcname, 0, NULL); #define globalvector(name,qcname) csqcg.name = (float*)PR_FindGlobal(csqcprogs, qcname, 0, NULL); @@ -320,6 +322,9 @@ static void CSQC_FindGlobals(void) csqc_world.g.physics_mode = &csphysicsmode; } + if (!csqcg.dimension_default) + csqcg.dimension_default = &dimension_default; + if (csqcg.maxclients) *csqcg.maxclients = cl.allocated_client_slots; } @@ -492,6 +497,7 @@ static void cs_getframestate(csqcedict_t *in, unsigned int rflags, framestate_t } //and the normal frames. + out->g[FS_REG].endbone = 0x7fffffff; out->g[FS_REG].frame[0] = in->v->frame; out->g[FS_REG].frame[1] = in->xv->frame2; out->g[FS_REG].frame[2] = in->xv->frame3; @@ -1757,6 +1763,7 @@ void QCBUILTIN PF_R_SetViewFlag(pubprogfuncs_t *prinst, struct globalvars_s *pr_ } void R2D_PolyBlend (void); +void R_DrawNameTags(void); static void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { if (csqc_worldchanged) @@ -1775,6 +1782,7 @@ static void QCBUILTIN PF_R_RenderScene(pubprogfuncs_t *prinst, struct globalvars V_ApplyRefdef(); R_RenderView(); R2D_PolyBlend (); + R_DrawNameTags(); { srect_t srect; @@ -4062,8 +4070,6 @@ void CSQC_EntStateToCSQC(unsigned int flags, float lerptime, entity_state_t *src ent->v->colormap = src->colormap; ent->v->skin = src->skinnum; -// ent->v->glowsize = src->glowsize; -// ent->v->glowcolor = src->glowcolour; ent->xv->scale = src->scale/16.0f; ent->xv->fatness = src->fatness/16.0f; // ent->xv->drawflags = src->hexen2flags; @@ -4072,16 +4078,23 @@ void CSQC_EntStateToCSQC(unsigned int flags, float lerptime, entity_state_t *src ent->xv->colormod[0] = src->colormod[0]*(8/256.0f); ent->xv->colormod[1] = src->colormod[1]*(8/256.0f); ent->xv->colormod[2] = src->colormod[2]*(8/256.0f); + ent->xv->glowmod[0] = src->glowmod[0]*(8/256.0f); + ent->xv->glowmod[1] = src->glowmod[1]*(8/256.0f); + ent->xv->glowmod[2] = src->glowmod[2]*(8/256.0f); +// ent->xv->glow_size = src->glowsize*4; +// ent->xv->glow_color = src->glowcolour; +// ent->xv->glow_trail = !!(state->dpflags & RENDER_GLOWTRAIL); ent->xv->alpha = src->trans/255.0f; +// ent->v->solid = src->solid; +// ent->v->color[0] = src->light[0]/255.0; +// ent->v->color[1] = src->light[1]/255.0; +// ent->v->color[2] = src->light[2]/255.0; +// ent->v->light_lev = src->light[3]; // ent->xv->style = src->lightstyle; // ent->xv->pflags = src->lightpflags; -// ent->v->solid = src->solid; -// ent->v->color[0] = src->light[0]; -// ent->v->color[1] = src->light[1]; -// ent->v->color[2] = src->light[2]; -// ent->v->light_lev = src->light[3]; -// ent->xv->tagentity = src->tagentity; -// ent->xv->tagindex = src->tagindex; + + ent->xv->tag_entity = src->tagentity; + ent->xv->tag_index = src->tagindex; if (src->solid == ES_SOLID_BSP) { @@ -5346,6 +5359,7 @@ static struct { {"keynumtostring_omgwtf", PF_cl_keynumtostring, 520}, {"findkeysforcommand", PF_cl_findkeysforcommand, 521}, + {"findkeysforcommandex", PF_cl_findkeysforcommandex, 0}, {"loadfromdata", PF_loadfromdata, 529}, {"loadfromfile", PF_loadfromfile, 530}, @@ -5373,7 +5387,7 @@ static struct { {"getresolution", PF_cl_getresolution, 608}, {"keynumtostring_menu", PF_cl_keynumtostring, 609}, //while present in dp's menuqc, dp doesn't actually support keynumtostring=609 in csqc. Which is probably a good thing because csqc would have 3 separate versions if it did. - {"findkeysforcommand_dp", PF_cl_findkeysforcommand, 610}, + {"findkeysforcommand_menu", PF_cl_findkeysforcommand, 610}, {"gethostcachevalue", PF_cl_gethostcachevalue, 611}, {"gethostcachestring", PF_cl_gethostcachestring, 612}, {"parseentitydata", PF_parseentitydata, 613}, @@ -5474,7 +5488,7 @@ void VARGS CSQC_Abort (char *format, ...) //an error occured. int size = 1024*1024*8; char *buffer = BZ_Malloc(size); csqcprogs->save_ents(csqcprogs, buffer, &size, size, 3); - COM_WriteFile("csqccore.txt", buffer, size); + COM_WriteFile("csqccore.txt", FS_GAMEONLY, buffer, size); BZ_Free(buffer); } @@ -5506,11 +5520,11 @@ void PDECL CSQC_EntSpawn (struct edict_s *e, int loading) if (1) { -// ent->xv->dimension_see = 255; -// ent->xv->dimension_seen = 255; +// ent->xv->dimension_see = csqc_world.dimension_default; +// ent->xv->dimension_seen = csqc_world.dimension_default; // ent->xv->dimension_ghost = 0; - ent->xv->dimension_solid = 255; - ent->xv->dimension_hit = 255; + ent->xv->dimension_solid = *csqcg.dimension_default; + ent->xv->dimension_hit = *csqcg.dimension_default; } } @@ -5710,7 +5724,7 @@ qbyte *PDECL CSQC_PRLoadFile (const char *path, void *buffer, int bufsize, size_ #ifndef FTE_TARGET_WEB //back it up - COM_WriteFile(newname, file, *sz); + COM_WriteFile(newname, FS_GAMEONLY, file, *sz); #endif } } @@ -6147,7 +6161,7 @@ void CSQC_CoreDump(void) int size = 1024*1024*8; char *buffer = BZ_Malloc(size); csqcprogs->save_ents(csqcprogs, buffer, &size, size, 3); - COM_WriteFile("csqccore.txt", buffer, size); + COM_WriteFile("csqccore.txt", FS_GAMEONLY, buffer, size); BZ_Free(buffer); } diff --git a/engine/client/pr_menu.c b/engine/client/pr_menu.c index 7981ac09d..1fa93a953 100644 --- a/engine/client/pr_menu.c +++ b/engine/client/pr_menu.c @@ -1038,6 +1038,7 @@ static struct evalc_t frame1time; evalc_t frame2time; evalc_t renderflags; + evalc_t skinobject; } menuc_eval; static playerview_t menuview; @@ -1670,6 +1671,29 @@ static void QCBUILTIN PF_m_setmodel(pubprogfuncs_t *prinst, struct globalvars_s if (mod && maxsval) VectorCopy(mod->maxs, maxsval->_vector); } +static void QCBUILTIN PF_m_setcustomskin(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + menuedict_t *ent = (void*)G_EDICT(prinst, OFS_PARM0); + const char *fname = PR_GetStringOfs(prinst, OFS_PARM1); + const char *skindata = PF_VarString(prinst, 2, pr_globals); + eval_t *val = prinst->GetEdictFieldValue(prinst, (void*)ent, "skinobject", &menuc_eval.skinobject); + if (!val) + return; + + if (val->_float > 0) + { + Mod_WipeSkin(val->_float); + val->_float = 0; + } + + if (*fname || *skindata) + { + if (*skindata) + val->_float = Mod_ReadSkinFile(fname, skindata); + else + val->_float = -(int)Mod_RegisterSkinFile(fname); + } +} //trivially basic static void QCBUILTIN PF_m_setorigin(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) { @@ -1707,6 +1731,7 @@ static qboolean CopyMenuEdictToEntity(pubprogfuncs_t *prinst, menuedict_t *in, e eval_t *frame2timeval = prinst->GetEdictFieldValue(prinst, (void*)in, "frame2time", &menuc_eval.frame2time); eval_t *colormapval = prinst->GetEdictFieldValue(prinst, (void*)in, "colormap", &menuc_eval.colormap); eval_t *renderflagsval = prinst->GetEdictFieldValue(prinst, (void*)in, "renderflags", &menuc_eval.renderflags); + eval_t *skinobjectval = prinst->GetEdictFieldValue(prinst, (void*)in, "skinobject", &menuc_eval.skinobject); int ival; int rflags; @@ -1730,6 +1755,8 @@ static qboolean CopyMenuEdictToEntity(pubprogfuncs_t *prinst, menuedict_t *in, e out->framestate.g[FS_REG].frametime[0] = frame1timeval?frame1timeval->_float:0; out->framestate.g[FS_REG].frametime[1] = frame2timeval?frame2timeval->_float:0; + out->customskin = skinobjectval?skinobjectval->_float:0; + //FIXME: colourmap ival = colormapval?colormapval->_float:0; out->playerindex = -1; @@ -1969,6 +1996,10 @@ static struct { {"findfont", PF_CL_findfont, 356}, {"loadfont", PF_CL_loadfont, 357}, //gap +// {"dynamiclight_get", PF_R_DynamicLight_Get, 372}, +// {"dynamiclight_set", PF_R_DynamicLight_Set, 373}, + {"setcustomskin", PF_m_setcustomskin, 376}, + //gap {"memalloc", PF_memalloc, 384}, {"memfree", PF_memfree, 385}, {"memcpy", PF_memcpy, 386}, @@ -2245,7 +2276,7 @@ void VARGS Menu_Abort (char *format, ...) int size = 1024*1024*8; buffer = Z_Malloc(size); menu_world.progs->save_ents(menu_world.progs, buffer, &size, size, 3); - COM_WriteFile("menucore.txt", buffer, size); + COM_WriteFile("menucore.txt", FS_GAMEONLY, buffer, size); Z_Free(buffer); } @@ -2422,7 +2453,7 @@ void MP_CoreDump_f(void) int size = 1024*1024*8; char *buffer = BZ_Malloc(size); menu_world.progs->save_ents(menu_world.progs, buffer, &size, size, 3); - COM_WriteFile("menucore.txt", buffer, size); + COM_WriteFile("menucore.txt", FS_GAMEONLY, buffer, size); BZ_Free(buffer); } } diff --git a/engine/client/r_part.c b/engine/client/r_part.c index bbfe9cee1..77fbabaac 100644 --- a/engine/client/r_part.c +++ b/engine/client/r_part.c @@ -199,28 +199,30 @@ static void R_Clutter_Insert_Mesh(clutter_build_ctx_t *ctx, model_t *mod, float if (!mod) return; - //fill in the parts of the entity_t that Alias_GAliasBuildMesh needs. - memset(&re, 0, sizeof(re)); -// memset(&re.framestate, 0, sizeof(re.framestate)); - re.framestate.g[FS_REG].lerpweight[0] = 1; - re.model = mod; - - inf = (galiasinfo_t*)Mod_Extradata (mod); - while(inf) + if (mod->type == mod_alias) { - galiasskin_t *skins = inf->ofsskins; - re.framestate.g[FS_REG].frame[0] = randanim%inf->numanimations; - if (skins->numframes) + //fill in the parts of the entity_t that Alias_GAliasBuildMesh needs. + memset(&re, 0, sizeof(re)); + re.framestate.g[FS_REG].lerpweight[0] = 1; + re.model = mod; + + inf = (galiasinfo_t*)Mod_Extradata (mod); + while(inf) { - unsigned int frame = randskin%skins->numframes; - Alias_GAliasBuildMesh(&mesh, NULL, inf, surfnum, &re, false); - surfnum++; - //fixme: if shares verts, rewind the verts and don't add more somehow, while being careful with shaders - R_Clutter_Insert_Soup(ctx, skins->frame[frame].shader, mesh.xyz_array, mesh.st_array, mesh.normals_array, mesh.snormals_array, mesh.tnormals_array, mesh.colors4f_array[0], mesh.numvertexes, mesh.indexes, mesh.numindexes, scale, origin, axis); + galiasskin_t *skins = inf->ofsskins; + re.framestate.g[FS_REG].frame[0] = randanim%inf->numanimations; + if (skins->numframes) + { + unsigned int frame = randskin%skins->numframes; + Alias_GAliasBuildMesh(&mesh, NULL, inf, surfnum, &re, false); + surfnum++; + //fixme: if shares verts, rewind the verts and don't add more somehow, while being careful with shaders + R_Clutter_Insert_Soup(ctx, skins->frame[frame].shader, mesh.xyz_array, mesh.st_array, mesh.normals_array, mesh.snormals_array, mesh.tnormals_array, mesh.colors4f_array[0], mesh.numvertexes, mesh.indexes, mesh.numindexes, scale, origin, axis); + } + inf = inf->nextsurf; } - inf = inf->nextsurf; + Alias_FlushCache(); //it got built using an entity on the stack, make sure other stuff doesn't get hurt. } - Alias_FlushCache(); //it got built using an entity on the stack, make sure other stuff doesn't get hurt. } static void R_Clutter_Insert(void *vctx, vec3_t *fte_restrict points, size_t numtris, shader_t *surface) { diff --git a/engine/client/render.h b/engine/client/render.h index df565d215..89b6f1ff8 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -517,7 +517,7 @@ void CLQ2_FlyEffect(struct q2centity_s *ent, vec3_t org); void CLQ2_DiminishingTrail(vec3_t oldorg, vec3_t neworg, struct q2centity_s *ent, unsigned int effects); void CLQ2_BlasterTrail2(vec3_t oldorg, vec3_t neworg); -void WritePCXfile (const char *filename, qbyte *data, int width, int height, int rowbytes, qbyte *palette, qboolean upload); //data is 8bit. +void WritePCXfile (const char *filename, enum fs_relative fsroot, qbyte *data, int width, int height, int rowbytes, qbyte *palette, qboolean upload); //data is 8bit. qbyte *ReadPCXFile(qbyte *buf, int length, int *width, int *height); qbyte *ReadTargaFile(qbyte *buf, int length, int *width, int *height, qboolean *hasalpha, int asgrey); qbyte *ReadJPEGFile(qbyte *infile, int length, int *width, int *height); diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 56072d756..9a2624690 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -204,7 +204,7 @@ cvar_t vid_conwidth = CVARF ("vid_conwidth", "0", cvar_t vid_renderer = CVARFD ("vid_renderer", "", CVAR_ARCHIVE | CVAR_RENDERERLATCH, "Specifies which backend is used. Values that might work are: sv (dedicated server), gl (opengl), egl (opengl es), d3d9 (direct3d 9), d3d11 (direct3d 11, with default hardware rendering), d3d11 warp (direct3d 11, with software rendering)."); -cvar_t vid_bpp = CVARFD ("vid_bpp", "32", +cvar_t vid_bpp = CVARFD ("vid_bpp", "0", CVAR_ARCHIVE | CVAR_RENDERERLATCH, "The number of colour bits to request from the renedering context"); cvar_t vid_desktopsettings = CVARFD ("vid_desktopsettings", "0", CVAR_ARCHIVE | CVAR_RENDERERLATCH, "Ignore the values of vid_width and vid_height, and just use the same settings that are used for the desktop."); diff --git a/engine/client/screen.h b/engine/client/screen.h index ed8d74292..0b352b3ae 100644 --- a/engine/client/screen.h +++ b/engine/client/screen.h @@ -79,7 +79,8 @@ void SCR_ShowPic_Script_f(void); //a header is better than none... void Draw_TextBox (int x, int y, int width, int lines); -qboolean SCR_ScreenShot (char *filename, void *rgb_buffer, int width, int height); +enum fs_relative; +qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void *rgb_buffer, int width, int height); void SCR_DrawTwoDimensional(int uimenu, qboolean nohud); diff --git a/engine/client/sys_plugfte.c b/engine/client/sys_plugfte.c index 858b9c86a..bb2ebdca1 100644 --- a/engine/client/sys_plugfte.c +++ b/engine/client/sys_plugfte.c @@ -585,7 +585,9 @@ qboolean MyRegGetStringValue(HKEY base, const char *keyname, const char *valuena DWORD type = REG_NONE; if (RegOpenKeyEx(base, keyname, 0, KEY_READ, &subkey) == ERROR_SUCCESS) { - result = ERROR_SUCCESS == RegQueryValueEx(subkey, valuename, NULL, &type, data, &datalen); + DWORD dl = datalen; + result = ERROR_SUCCESS == RegQueryValueEx(subkey, valuename, NULL, &type, data, &dl); + datalen = dl; RegCloseKey (subkey); } diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index e029a46b2..fd6e59dc6 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -360,22 +360,38 @@ void Sys_CloseLibrary(dllhandle_t *lib) { FreeLibrary((HMODULE)lib); } +HMODULE LoadLibraryU(const char *name) +{ + if (WinNT) + { + wchar_t wide[MAX_OSPATH]; + widen(wide, sizeof(wide), name); + + return LoadLibraryW(wide); + } + else + { + wchar_t wide[MAX_OSPATH]; + char ansi[MAX_OSPATH]; + widen(wide, sizeof(wide), name); + ansi[WideCharToMultiByte(CP_ACP, 0, wide, wcslen(wide), ansi, sizeof(ansi)-1, NULL, NULL)] = 0; + return LoadLibraryA(ansi); + } +} dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs) { int i; HMODULE lib; - wchar_t wide[MAX_OSPATH]; - widen(wide, sizeof(wide), name); - lib = LoadLibraryW(wide); + lib = LoadLibraryU(name); if (!lib) { if (!strstr(COM_SkipPath(name), ".dll")) { //.dll implies that it is a system dll, or something that is otherwise windows-specific already. #ifdef _WIN64 - lib = LoadLibrary(va("%s_64", name)); + lib = LoadLibraryU(va("%s_64", name)); #elif defined(_WIN32) - lib = LoadLibrary(va("%s_32", name)); + lib = LoadLibraryU(va("%s_32", name)); #endif } if (!lib) @@ -1537,7 +1553,15 @@ void VARGS Sys_Printf (char *fmt, ...) if (debugout) OutputDebugStringW(wide); if (houtput) - WriteConsoleW(houtput, wide, wlen, &dummy, NULL); + { + if (WinNT) + WriteConsoleW(houtput, wide, wlen, &dummy, NULL); + else + { + //win95 doesn't support wide chars *sigh*. blank consoles suck. this conversion might loose stuff if the multibytes are too long. + WriteConsole(houtput, text, WideCharToMultiByte(CP_ACP, 0, wide, wlen, text, sizeof(text), NULL, NULL), &dummy, NULL); + } + } } void Sys_Quit (void) @@ -1941,7 +1965,13 @@ char *Sys_ConsoleInput (void) } else if (ch >= ' ') { wchar_t wch = ch; - WriteConsoleW(houtput, &wch, 1, &dummy, NULL); + if (WinNT) + WriteConsoleW(houtput, &wch, 1, &dummy, NULL); + else + { + char mb[8]; //hopefully ucs-2-only will be sufficient... + WriteConsoleA(houtput, mb, WideCharToMultiByte(CP_ACP, 0, &wch, 1, mb, sizeof(mb), NULL, NULL), &dummy, NULL); + } len += utf8_encode(text+len, ch, sizeof(text)-1-len); } @@ -2170,8 +2200,6 @@ HWND hwnd_dialog; -#define COBJMACROS - #include typedef struct qSHARDAPPIDINFOLINK { @@ -2413,15 +2441,24 @@ typedef struct { } qOPENASINFO; HRESULT (WINAPI *pSHOpenWithDialog)(HWND hwndParent, const qOPENASINFO *poainfo); + +LPITEMIDLIST (STDAPICALLTYPE *pSHBrowseForFolderW)(LPBROWSEINFOW lpbi); +BOOL (STDAPICALLTYPE *pSHGetPathFromIDListW)(LPCITEMIDLIST pidl, LPWSTR pszPath); +BOOL (STDAPICALLTYPE *pSHGetSpecialFolderPathW)(HWND hwnd, LPWSTR pszPath, int csidl, BOOL fCreate); +BOOL (STDAPICALLTYPE *pShell_NotifyIconW)(DWORD dwMessage, PNOTIFYICONDATAW lpData); void Win7_Init(void) { HANDLE h; HRESULT (WINAPI *pSetCurrentProcessExplicitAppUserModelID)(PCWSTR AppID); - - h = LoadLibraryW(L"shell32.dll"); + h = LoadLibraryU("shell32.dll"); if (h) { + pSHBrowseForFolderW = (void*)GetProcAddress(h, "SHBrowseForFolderW"); + pSHGetPathFromIDListW = (void*)GetProcAddress(h, "SHGetPathFromIDListW"); + pSHGetSpecialFolderPathW = (void*)GetProcAddress(h, "SHGetSpecialFolderPathW"); + pShell_NotifyIconW = (void*)GetProcAddress(h, "Shell_NotifyIconW"); + pSHOpenWithDialog = (void*)GetProcAddress(h, "SHOpenWithDialog"); pSetCurrentProcessExplicitAppUserModelID = (void*)GetProcAddress(h, "SetCurrentProcessExplicitAppUserModelID"); @@ -2507,6 +2544,13 @@ void Win7_TaskListInit(void) } } +BOOL CopyFileU(const char *src, const char *dst, BOOL bFailIfExists) +{ + wchar_t wide1[2048]; + wchar_t wide2[2048]; + return CopyFileW(widen(wide1, sizeof(wide1), src), widen(wide2, sizeof(wide2), dst), bFailIfExists); +} + //#define SVNREVISION 1 #if defined(SVNREVISION) && !defined(MINIMAL) #define SVNREVISIONSTR STRINGIFY(SVNREVISION) @@ -3115,7 +3159,7 @@ static int Sys_ProcessCommandline(char **argv, int maxargc, char *argv0) //using this like posix' access function, but with much more code, microsoftisms, and no errno codes/info //no, I don't really have a clue why it needs to be so long. #include -static BOOL microsoft_access(LPCSTR pszFolder, DWORD dwAccessDesired) +static BOOL microsoft_accessW(LPWSTR pszFolder, DWORD dwAccessDesired) { HANDLE hToken; PRIVILEGE_SET PrivilegeSet; @@ -3126,11 +3170,9 @@ static BOOL microsoft_access(LPCSTR pszFolder, DWORD dwAccessDesired) SECURITY_INFORMATION si = (SECURITY_INFORMATION)( OWNER_SECURITY_INFORMATION|GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION); PSECURITY_DESCRIPTOR psdSD = NULL; DWORD dwNeeded; - wchar_t wpath[MAX_OSPATH]; - widen(wpath, sizeof(wpath), pszFolder); - GetFileSecurityW(wpath,si,NULL,0,&dwNeeded); + GetFileSecurityW(pszFolder,si,NULL,0,&dwNeeded); psdSD = malloc(dwNeeded); - GetFileSecurityW(wpath,si,psdSD,dwNeeded,&dwNeeded); + GetFileSecurityW(pszFolder,si,psdSD,dwNeeded,&dwNeeded); ImpersonateSelf(SecurityImpersonation); OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &hToken); memset(&GenericMapping, 0xff, sizeof(GENERIC_MAPPING)); @@ -3144,8 +3186,13 @@ static BOOL microsoft_access(LPCSTR pszFolder, DWORD dwAccessDesired) free(psdSD); return fAccessGranted; } +static BOOL microsoft_accessU(LPCSTR pszFolder, DWORD dwAccessDesired) +{ + wchar_t wpath[MAX_OSPATH]; + return microsoft_accessW(widen(wpath, sizeof(wpath), pszFolder), dwAccessDesired); +} -static int MessageBoxU(HWND hWnd, char *lpText, char *lpCaption, UINT uType) +int MessageBoxU(HWND hWnd, char *lpText, char *lpCaption, UINT uType) { wchar_t widecaption[256]; wchar_t widetext[2048]; @@ -3175,8 +3222,8 @@ static LRESULT CALLBACK stoopidstoopidstoopid(HWND w, UINT m, WPARAM wp, LPARAM gah.ptDrag.x = gah.ptDrag.y = 0; gah.hdr = fu->hdr; gah.hdr.code = TVN_SELCHANGEDW; - r = CallWindowProc(omgwtfwhyohwhy,w,m,wp,lp); - CallWindowProc(omgwtfwhyohwhy,w,WM_NOTIFY,wp,(LPARAM)&gah); + r = CallWindowProcW(omgwtfwhyohwhy,w,m,wp,lp); + CallWindowProcW(omgwtfwhyohwhy,w,WM_NOTIFY,wp,(LPARAM)&gah); return r; } break; @@ -3191,8 +3238,8 @@ static LRESULT CALLBACK stoopidstoopidstoopid(HWND w, UINT m, WPARAM wp, LPARAM gah.ptDrag.x = gah.ptDrag.y = 0; gah.hdr = fu->hdr; gah.hdr.code = TVN_SELCHANGEDA; - r = CallWindowProc(omgwtfwhyohwhy,w,m,wp,lp); - CallWindowProc(omgwtfwhyohwhy,w,WM_NOTIFY,wp,(LPARAM)&gah); + r = CallWindowProcW(omgwtfwhyohwhy,w,m,wp,lp); + CallWindowProcW(omgwtfwhyohwhy,w,WM_NOTIFY,wp,(LPARAM)&gah); return r; } break; @@ -3202,7 +3249,7 @@ static LRESULT CALLBACK stoopidstoopidstoopid(HWND w, UINT m, WPARAM wp, LPARAM } break; } - return CallWindowProc(omgwtfwhyohwhy,w,m,wp,lp); + return CallWindowProcW(omgwtfwhyohwhy,w,m,wp,lp); } struct egadsthisisretarded @@ -3213,11 +3260,20 @@ struct egadsthisisretarded char statustext[MAX_OSPATH]; }; +void FS_Directorize(char *fname, size_t fnamesize) +{ + size_t l = strlen(fname); + if (!l) //technically already a directory + return; + if (fname[l-1] == '\\' || fname[l-1] == '/') + return; //already a directory + Q_strncatz(fname, "/", fnamesize); +} + static INT CALLBACK StupidBrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pDatafoo) { //'stolen' from microsoft's knowledge base. //required to work around microsoft being annoying. struct egadsthisisretarded *pData = (struct egadsthisisretarded*)pDatafoo; - TCHAR szDir[MAX_PATH]; // char *foo; HWND edit = FindWindowEx(hwnd, NULL, "EDIT", NULL); HWND list; @@ -3226,8 +3282,6 @@ static INT CALLBACK StupidBrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LP switch(uMsg) { case BFFM_INITIALIZED: - OutputDebugStringA("initialised\n"); - //combat windows putting new windows behind everything else if it takes a while for UAC prompts to go away SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); @@ -3236,53 +3290,68 @@ static INT CALLBACK StupidBrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LP if (list) omgwtfwhyohwhy = (WNDPROC)SetWindowLongPtr(list, GWLP_WNDPROC, (LONG_PTR)stoopidstoopidstoopid); + { + wchar_t szDir[MAX_PATH]; #ifndef _DEBUG - //the standard location iiuc - if (com_homepathenabled && SHGetSpecialFolderPath(NULL, szDir, CSIDL_PROGRAM_FILES, TRUE) && microsoft_access(szDir, ACCESS_READ | ACCESS_WRITE)) - ; - else if (microsoft_access("C:\\Games\\", ACCESS_READ | ACCESS_WRITE)) - Q_strncpyz(szDir, "C:\\Games\\", sizeof(szDir)); - else if (microsoft_access("C:\\", ACCESS_READ | ACCESS_WRITE)) - Q_strncpyz(szDir, "C:\\", sizeof(szDir)); - //if we're not an admin, install it somewhere else. - else if (SHGetSpecialFolderPath(NULL, szDir, CSIDL_LOCAL_APPDATA, TRUE) && microsoft_access(szDir, ACCESS_READ | ACCESS_WRITE)) - ; - else -#endif - if (GetCurrentDirectory(sizeof(szDir)/sizeof(TCHAR), szDir))// && microsoft_access(szDir, ACCESS_READ | ACCESS_WRITE)) + //the standard location iiuc + if (com_homepathenabled && pSHGetSpecialFolderPathW(NULL, szDir, CSIDL_PROGRAM_FILES, TRUE) && microsoft_accessW(szDir, ACCESS_READ | ACCESS_WRITE)) ; - SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)szDir); -// SendMessage(hwnd, BFFM_SETEXPANDED, TRUE, (LPARAM)szDir); - SendMessageW(hwnd, BFFM_SETOKTEXT, TRUE, (LPARAM)L"Install"); + else if (microsoft_accessU("C:\\Games\\", ACCESS_READ | ACCESS_WRITE)) + widen(szDir, sizeof(szDir), "C:\\Games\\"); + else if (microsoft_accessU("C:\\", ACCESS_READ | ACCESS_WRITE)) + widen(szDir, sizeof(szDir), "C:\\"); + //if we're not an admin, install it somewhere else. + else if (pSHGetSpecialFolderPathW(NULL, szDir, CSIDL_LOCAL_APPDATA, TRUE) && microsoft_accessW(szDir, ACCESS_READ | ACCESS_WRITE)) + ; + else +#endif + if (GetCurrentDirectoryW(countof(szDir), szDir))// && microsoft_access(szDir, ACCESS_READ | ACCESS_WRITE)) + ; + SendMessageW(hwnd, BFFM_SETSELECTIONW, TRUE, (LPARAM)szDir); + SendMessageW(hwnd, BFFM_SETEXPANDED, TRUE, (LPARAM)szDir); + SendMessageW(hwnd, BFFM_SETOKTEXT, TRUE, (LPARAM)widen(szDir, sizeof(szDir), "Install")); + } break; case BFFM_VALIDATEFAILEDA: - if (!microsoft_access(pData->parentdir, ACCESS_READ | ACCESS_WRITE)) - return 1; - if (edit) - GetWindowText(edit, pData->subdir, sizeof(pData->subdir)); - - if (microsoft_access(va("%s\\%s", pData->parentdir, pData->subdir), ACCESS_READ)) - return MessageBoxU(hwnd, va("%s\\%s already exists!\nThis installer will (generally) not overwrite.\nIf you want to re-install, you must manually uninstall it first.\n\nContinue?", pData->parentdir, pData->subdir), fs_gamename.string, MB_ICONWARNING|MB_OKCANCEL|MB_TOPMOST) == IDCANCEL; - else - return MessageBoxU(hwnd, va("Install to %s\\%s ?", pData->parentdir, pData->subdir), fs_gamename.string, MB_OKCANCEL) == IDCANCEL; case BFFM_VALIDATEFAILEDW: - return 1;//!microsoft_access("C:\\Games\\", ACCESS_READ | ACCESS_WRITE)) - case BFFM_SELCHANGED: - OutputDebugStringA("selchanged\n"); - if (SHGetPathFromIDList((LPITEMIDLIST)lp, pData->parentdir)) + if (!microsoft_accessU(pData->parentdir, ACCESS_READ | ACCESS_WRITE)) { -// OutputDebugString(va("selchanged: %s\n", szDir)); -// while(foo = strchr(pData->parentdir, '\\')) -// *foo = '/'; - //fixme: verify that id1 is a subdir perhaps? - if (edit) + MessageBoxU(hwnd, va("%s is not writable.", pData->parentdir), fs_gamename.string, 0); + return 1; + } + if (edit) + { + wchar_t wide[256]; + GetWindowTextW(edit, wide, countof(wide)); + narrowen(pData->subdir, sizeof(pData->subdir), wide); + } + + if (microsoft_accessU(va("%s%s", pData->parentdir, pData->subdir), ACCESS_READ)) + return MessageBoxU(hwnd, va("%s%s already exists!\nThis installer will (generally) not overwrite existing data files.\nIf you want to re-install, you must manually uninstall it first.\n\nContinue?", pData->parentdir, pData->subdir), fs_gamename.string, MB_ICONWARNING|MB_OKCANCEL|MB_TOPMOST) == IDCANCEL; + else + return MessageBoxU(hwnd, va("Install to %s%s ?", pData->parentdir, pData->subdir), fs_gamename.string, MB_OKCANCEL) == IDCANCEL; + case BFFM_SELCHANGED: + { + wchar_t wide[MAX_PATH*2+2]; + char *foo; + if (pSHGetPathFromIDListW((LPITEMIDLIST)lp, wide)) { - wchar_t wide[128]; - SetWindowTextW(edit, widen(wide, sizeof(wide), fs_gamename.string)); - SendMessageA(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)va("%s", pData->parentdir)); + narrowen(pData->parentdir, sizeof(pData->parentdir), wide); + FS_Directorize(pData->parentdir, sizeof(pData->parentdir)); + + //this'll make microsoft happy. + while(foo = strchr(pData->parentdir, '/')) + *foo = '\\'; + + if (edit) + { + wchar_t wide[128]; + SetWindowTextW(edit, widen(wide, sizeof(wide), fs_gamename.string)); + SendMessageW(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)widen(wide, sizeof(wide), va("%s", pData->parentdir))); + } + else + SendMessageW(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)widen(wide, sizeof(wide), va("%s%s", pData->parentdir, fs_gamename.string))); } - else - SendMessageA(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM)va("%s/%s", pData->parentdir, fs_gamename.string)); } break; case BFFM_IUNKNOWN: @@ -3298,16 +3367,6 @@ LRESULT CALLBACK NoCloseWindowProc(HWND w, UINT m, WPARAM wp, LPARAM lp) return DefWindowProc(w, m, wp, lp); } -void FS_Directorize(char *fname, size_t fnamesize) -{ - size_t l = strlen(fname); - if (!l) //technically already a directory - return; - if (fname[l-1] == '\\' || fname[l-1] == '/') - return; //already a directory - Q_strncatz(fname, "/", fnamesize); -} - void FS_CreateBasedir(const char *path); qboolean Sys_DoInstall(void) { @@ -3315,22 +3374,31 @@ qboolean Sys_DoInstall(void) char exepath[MAX_OSPATH]; char newexepath[MAX_OSPATH]; wchar_t wide[MAX_PATH]; + wchar_t wide2[MAX_PATH]; char resultpath[MAX_OSPATH]; BROWSEINFOW bi; LPITEMIDLIST il; struct egadsthisisretarded diediedie; + if (!pSHGetSpecialFolderPathW) + { + MessageBoxU(NULL, "SHGetSpecialFolderPathW is not supported\n", fs_gamename.string, 0); + return TRUE; + } + if (fs_manifest && fs_manifest->eula) { if (MessageBoxU(NULL, fs_manifest->eula, fs_gamename.string, MB_OKCANCEL|MB_TOPMOST|MB_DEFBUTTON2) != IDOK) return TRUE; } + Q_strncpyz(diediedie.subdir, fs_gamename.string, sizeof(diediedie.subdir)); + _snwprintf(diediedie.title, countof(diediedie.title), L"Where would you like to install %s to?", widen(wide, sizeof(wide), fs_gamename.string)); + GetCurrentDirectoryW(countof(wide)-1, wide); + memset(&bi, 0, sizeof(bi)); bi.hwndOwner = mainwindow; //note that this is usually still null bi.pidlRoot = NULL; - _snwprintf(diediedie.title, countof(diediedie.title), L"Where would you like to install %s to?", widen(wide, sizeof(wide), fs_gamename.string)); - GetCurrentDirectoryW(countof(wide)-1, wide); bi.pszDisplayName = wide; bi.lpszTitle = diediedie.title; bi.ulFlags = BIF_RETURNONLYFSDIRS|BIF_STATUSTEXT | BIF_EDITBOX|BIF_NEWDIALOGSTYLE|BIF_VALIDATE; @@ -3338,12 +3406,10 @@ qboolean Sys_DoInstall(void) bi.lParam = (LPARAM)&diediedie; bi.iImage = 0; - Q_strncpyz(diediedie.subdir, fs_gamename.string, sizeof(diediedie.subdir)); - - il = SHBrowseForFolderW(&bi); + il = pSHBrowseForFolderW?pSHBrowseForFolderW(&bi):NULL; if (il) { - SHGetPathFromIDListW(il, wide); + pSHGetPathFromIDListW(il, wide); CoTaskMemFree(il); } else @@ -3359,9 +3425,10 @@ qboolean Sys_DoInstall(void) FS_CreateBasedir(resultpath); - GetModuleFileName(NULL, exepath, sizeof(exepath)); + GetModuleFileNameW(NULL, wide, countof(wide)); + narrowen(exepath, sizeof(exepath), wide); FS_NativePath(va("%s.exe", fs_gamename.string), FS_ROOT, newexepath, sizeof(newexepath)); - CopyFile(exepath, newexepath, FALSE); + CopyFileU(exepath, newexepath, FALSE); /*the game can now be run (using regular autoupdate stuff), but most installers are expected to install the data instead of just more downloaders, so lets do that with a 'nice' progress box*/ { @@ -3453,13 +3520,15 @@ qboolean Sys_DoInstall(void) widen(wsz, sizeof(wsz), resultpath); psl->lpVtbl->SetWorkingDirectory(psl, wsz); hres = psl->lpVtbl->QueryInterface(psl, &qIID_IPersistFile, (LPVOID*)&ppf); - if (SUCCEEDED(hres) && SHGetSpecialFolderPath(NULL, startmenu, CSIDL_COMMON_PROGRAMS, TRUE)) + if (SUCCEEDED(hres) && pSHGetSpecialFolderPathW(NULL, wsz, CSIDL_COMMON_PROGRAMS, TRUE)) { WCHAR wsz[MAX_PATH]; + narrowen(startmenu, sizeof(startmenu), wsz); widen(wsz, sizeof(wsz), va("%s/%s.lnk", startmenu, fs_gamename.string)); hres = ppf->lpVtbl->Save(ppf, wsz, TRUE); - if (hres == E_ACCESSDENIED && SHGetSpecialFolderPath(NULL, startmenu, CSIDL_PROGRAMS, TRUE)) + if (hres == E_ACCESSDENIED && pSHGetSpecialFolderPathW(NULL, wsz, CSIDL_PROGRAMS, TRUE)) { + narrowen(startmenu, sizeof(startmenu), wsz); widen(wsz, sizeof(wsz), va("%s/%s.lnk", startmenu, fs_gamename.string)); hres = ppf->lpVtbl->Save(ppf, wsz, TRUE); } @@ -3470,7 +3539,7 @@ qboolean Sys_DoInstall(void) } //now start it up properly. - ShellExecute(mainwindow, "open", newexepath, Q_strcasecmp(fs_manifest->installation, "quake")?"":"+sys_register_file_associations", resultpath, SW_SHOWNORMAL); + ShellExecuteW(mainwindow, L"open", widen(wide, sizeof(wide), newexepath), Q_strcasecmp(fs_manifest->installation, "quake")?L"":L"+sys_register_file_associations", widen(wide2, sizeof(wide2), resultpath), SW_SHOWNORMAL); return true; } qboolean Sys_RunInstaller(void) @@ -3742,11 +3811,11 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin memset(&parms, 0, sizeof(parms)); - #ifndef MINGW - #if _MSC_VER > 1200 +// #ifndef MINGW +// #if _MSC_VER > 1200 Win7_Init(); - #endif - #endif +// #endif +// #endif #ifdef _MSC_VER #if _M_IX86_FP >= 1 @@ -3781,11 +3850,16 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin #endif #ifdef CATCHCRASH - LoadLibraryA ("DBGHELP"); //heap corruption can prevent loadlibrary from working properly, so do this in advance. + LoadLibraryU ("DBGHELP"); //heap corruption can prevent loadlibrary from working properly, so do this in advance. #ifdef _MSC_VER __try #else - AddVectoredExceptionHandler(true, nonmsvc_CrashExceptionHandler); + { + PVOID (WINAPI *pAddVectoredExceptionHandler)(ULONG FirstHandler, PVECTORED_EXCEPTION_HANDLER VectoredHandler); + dllfunction_t dbgfuncs[] = {{(void*)&pAddVectoredExceptionHandler, "AddVectoredExceptionHandler"}, {NULL,NULL}}; + if (Sys_LoadLibrary("kernel32.dll", dbgfuncs) && pAddVectoredExceptionHandler) + pAddVectoredExceptionHandler(0, nonmsvc_CrashExceptionHandler); + } #endif #endif { diff --git a/engine/client/textedit.c b/engine/client/textedit.c index 734c608d2..4f8aaefa0 100644 --- a/engine/client/textedit.c +++ b/engine/client/textedit.c @@ -252,7 +252,7 @@ static qboolean EditorSaveFile(char *s) //returns true if succesful pos++; } - COM_WriteFile(s, data, len); + COM_WriteFile(s, FS_GAMEONLY, data, len); madechanges = false; editenabled = true; diff --git a/engine/client/vid_headless.c b/engine/client/vid_headless.c index 69b83d11b..5d1d3e3e1 100644 --- a/engine/client/vid_headless.c +++ b/engine/client/vid_headless.c @@ -4,7 +4,7 @@ #include "shader.h" #ifdef _WIN32 -#include +#include "winquake.h" #include "resource.h" #else #include @@ -68,7 +68,10 @@ LRESULT CALLBACK HeadlessWndProc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lpara } return 0; default: - return DefWindowProcA(wnd, msg, wparam, lparam); + if (WinNT) + return DefWindowProcW(wnd, msg, wparam, lparam); + else + return DefWindowProcA(wnd, msg, wparam, lparam); } } #endif @@ -79,31 +82,64 @@ static qboolean Headless_VID_Init (rendererstate_t *info, unsigned char *pale //tray icon crap, so the user can still restore the game. extern HWND mainwindow; extern HINSTANCE global_hInstance; - WNDCLASSW wc; - NOTIFYICONDATAW data; + if (WinNT) + { + WNDCLASSW wc; + NOTIFYICONDATAW data; - //Shell_NotifyIcon requires a window to provide events etc. - wc.style = 0; - wc.lpfnWndProc = (WNDPROC)HeadlessWndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = global_hInstance; - wc.hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON1)); - wc.hCursor = LoadCursor (NULL,IDC_ARROW); - wc.hbrBackground = NULL; - wc.lpszMenuName = 0; - wc.lpszClassName = L"FTEHeadlessClass"; - RegisterClassW(&wc); + //Shell_NotifyIcon requires a window to provide events etc. + wc.style = 0; + wc.lpfnWndProc = (WNDPROC)HeadlessWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = global_hInstance; + wc.hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON1)); + wc.hCursor = LoadCursor (NULL,IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = 0; + wc.lpszClassName = L"FTEHeadlessClass"; + RegisterClassW(&wc); - mainwindow = CreateWindowExW(0L, wc.lpszClassName, L"FTE QuakeWorld", 0, 0, 0, 0, 0, NULL, NULL, global_hInstance, NULL); - data.cbSize = sizeof(data); - data.hWnd = mainwindow; - data.uID = 0; - data.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; - data.uCallbackMessage = WM_USER; - data.hIcon = wc.hIcon; - wcscpy(data.szTip, L"Right-click to restore"); - Shell_NotifyIconW(NIM_ADD, &data); + mainwindow = CreateWindowExW(0L, wc.lpszClassName, L"FTE QuakeWorld", 0, 0, 0, 0, 0, NULL, NULL, global_hInstance, NULL); + data.cbSize = sizeof(data); + data.hWnd = mainwindow; + data.uID = 0; + data.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; + data.uCallbackMessage = WM_USER; + data.hIcon = wc.hIcon; + wcscpy(data.szTip, L"Right-click to restore"); + if (pShell_NotifyIconW) + pShell_NotifyIconW(NIM_ADD, &data); + } + else + { + WNDCLASSA wc; + NOTIFYICONDATAA data; + + //Shell_NotifyIcon requires a window to provide events etc. + wc.style = 0; + wc.lpfnWndProc = (WNDPROC)HeadlessWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = global_hInstance; + wc.hIcon = LoadIcon (global_hInstance, MAKEINTRESOURCE (IDI_ICON1)); + wc.hCursor = LoadCursor (NULL,IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = 0; + wc.lpszClassName = "FTEHeadlessClass"; + RegisterClassA(&wc); + + mainwindow = CreateWindowExA(0L, wc.lpszClassName, "FTE QuakeWorld", 0, 0, 0, 0, 0, NULL, NULL, global_hInstance, NULL); + data.cbSize = sizeof(data); + data.hWnd = mainwindow; + data.uID = 0; + data.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; + data.uCallbackMessage = WM_USER; + data.hIcon = wc.hIcon; + //fixme: proper multibyte + Q_strncpyz(data.szTip, "Right-click to restore", sizeof(data.szTip)); + Shell_NotifyIconA(NIM_ADD, &data); + } #endif return true; } @@ -111,6 +147,7 @@ static void Headless_VID_DeInit (void) { #ifdef _WIN32 //tray icon crap, so the user can still restore the game. + //FIXME: remove tray icon. win95 won't do this automagically. extern HWND mainwindow; DestroyWindow(mainwindow); mainwindow = NULL; diff --git a/engine/client/view.c b/engine/client/view.c index 754460b71..7cae3beba 100644 --- a/engine/client/view.c +++ b/engine/client/view.c @@ -575,10 +575,20 @@ void V_BonusFlash_f (void) { if (v_bonusflash.value || !Cmd_FromGamecode()) { - cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215; - cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186; - cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69; - cl.cshifts[CSHIFT_BONUS].percent = 50*v_bonusflash.value; + if (Cmd_Argc() > 1) + { //this is how I understand DP expects them. + cl.cshifts[CSHIFT_BONUS].destcolor[0] = atof(Cmd_Argv(1)); + cl.cshifts[CSHIFT_BONUS].destcolor[1] = atof(Cmd_Argv(2)); + cl.cshifts[CSHIFT_BONUS].destcolor[2] = atof(Cmd_Argv(3)); + cl.cshifts[CSHIFT_BONUS].percent = atof(Cmd_Argv(4))*255*v_bonusflash.value; + } + else + { + cl.cshifts[CSHIFT_BONUS].destcolor[0] = 215; + cl.cshifts[CSHIFT_BONUS].destcolor[1] = 186; + cl.cshifts[CSHIFT_BONUS].destcolor[2] = 69; + cl.cshifts[CSHIFT_BONUS].percent = 50*v_bonusflash.value; + } } } void V_DarkFlash_f (void) @@ -1544,7 +1554,8 @@ void R_DrawNameTags(void) e = WEDICT_NUM(w->progs, i); if (e->isfree) continue; - VectorInterpolate(e->v->absmin, 0.5, e->v->absmax, org); + VectorInterpolate(e->v->mins, 0.5, e->v->maxs, org); + VectorAdd(org, e->v->origin, org); VectorSubtract(org, r_refdef.vieworg, diff); if (DotProduct(diff, diff) > 256*256) continue; diff --git a/engine/client/winquake.h b/engine/client/winquake.h index 1618e6925..e0251b9c1 100644 --- a/engine/client/winquake.h +++ b/engine/client/winquake.h @@ -74,6 +74,16 @@ LONG CDAudio_MessageHandler(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); #endif +//shell32 stuff that doesn't exist in win95 +#define COBJMACROS +#include +#include +extern LPITEMIDLIST (STDAPICALLTYPE *pSHBrowseForFolderW)(LPBROWSEINFOW lpbi); +extern BOOL (STDAPICALLTYPE *pSHGetPathFromIDListW)(LPCITEMIDLIST pidl, LPWSTR pszPath); +extern BOOL (STDAPICALLTYPE *pSHGetSpecialFolderPathW)(HWND hwnd, LPWSTR pszPath, int csidl, BOOL fCreate); +extern BOOL (STDAPICALLTYPE *pShell_NotifyIconW)(DWORD dwMessage, PNOTIFYICONDATAW lpData); + + //void VID_LockBuffer (void); //void VID_UnlockBuffer (void); diff --git a/engine/common/bothdefs.h b/engine/common/bothdefs.h index 8580aa9b9..6e8f86634 100644 --- a/engine/common/bothdefs.h +++ b/engine/common/bothdefs.h @@ -528,8 +528,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define ARCH_DL_POSTFIX ".so" #endif -#if defined(__amd64__) - #define ARCH_CPU_POSTFIX "amd" +#if defined(_M_AMD64) || defined(__amd64__) +#ifdef _WIN32 + #define ARCH_CPU_POSTFIX "x64" +#else + #define ARCH_CPU_POSTFIX "amd64" +#endif #elif defined(_M_IX86) || defined(__i386__) #define ARCH_CPU_POSTFIX "x86" #elif defined(__powerpc__) || defined(__ppc__) diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 3876b1188..32cbea41b 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -581,6 +581,20 @@ void Cmd_Exec_f (void) int defdepth = COM_FDepthFile("default.cfg", true); if (defdepth < cfgdepth) Cbuf_InsertText("exec default.cfg\n", ((Cmd_FromGamecode() || untrusted) ? RESTRICT_INSECURE : Cmd_ExecLevel), false); + + //hack to work around the more insideous hacks of other engines. + //namely: vid_restart at the end of config.cfg is evil, and NOT desired in FTE as it generally means any saved video settings are wrong. + if (l >= 13 && !strcmp(f+l-13, "\nvid_restart\n")) + { + Con_Printf(CON_WARNING "WARNING: %s came from a different engine\n", loc.rawname); + l -= 12; + } + else if (l >= 14 && !strcmp(f+l-14, "\nvid_restart\r\n")) + { + Con_Printf(CON_WARNING "WARNING: %s came from a different engine\n", loc.rawname); + l -= 13; + } + f[l] = 0; } // don't execute anything if it was from server (either the stuffcmd/localcmd, or the file) if (!strcmp(name, "default.cfg") && !(Cmd_FromGamecode() || untrusted)) diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index ebf854720..28fc492e0 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -5065,6 +5065,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) int bonemap[MAX_BONES]; char *e; size_t psasize; + void *psabuffer; pskpnts_t *pnts = NULL; pskvtxw_t *vtxw = NULL; @@ -5205,13 +5206,14 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) /*attempt to load a psa file. don't die if we can't find one*/ COM_StripExtension(mod->name, psaname, sizeof(psaname)); Q_strncatz(psaname, ".psa", sizeof(psaname)); - buffer = COM_LoadFile(psaname, 5, &psasize); - if (buffer) + buffer = NULL;//test + psabuffer = COM_LoadFile(psaname, 5, &psasize); + if (psabuffer) { pos = 0; while (pos < psasize && !fail) { - chunk = (pskchunk_t*)((char*)buffer + pos); + chunk = (pskchunk_t*)((char*)psabuffer + pos); chunk->version = LittleLong(chunk->version); chunk->recordsize = LittleLong(chunk->recordsize); chunk->numrecords = LittleLong(chunk->numrecords); @@ -5224,7 +5226,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) else if (!strcmp("BONENAMES", chunk->id) && chunk->recordsize == sizeof(pskboneinfo_t)) { /*parsed purely to ensure that the bones match the main model*/ - pskboneinfo_t *animbones = (pskboneinfo_t*)((char*)buffer + pos); + pskboneinfo_t *animbones = (pskboneinfo_t*)((char*)psabuffer + pos); pos += chunk->recordsize * chunk->numrecords; if (num_boneinfo != chunk->numrecords) { @@ -5262,7 +5264,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) else if (!strcmp("ANIMINFO", chunk->id) && chunk->recordsize == sizeof(pskaniminfo_t)) { num_animinfo = chunk->numrecords; - animinfo = (pskaniminfo_t*)((char*)buffer + pos); + animinfo = (pskaniminfo_t*)((char*)psabuffer + pos); pos += chunk->recordsize * chunk->numrecords; for (i = 0; i < num_animinfo; i++) @@ -5277,7 +5279,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) else if (!strcmp("ANIMKEYS", chunk->id) && chunk->recordsize == sizeof(pskanimkeys_t)) { num_animkeys = chunk->numrecords; - animkeys = (pskanimkeys_t*)((char*)buffer + pos); + animkeys = (pskanimkeys_t*)((char*)psabuffer + pos); pos += chunk->recordsize * chunk->numrecords; for (i = 0; i < num_animkeys; i++) @@ -5317,11 +5319,11 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) num_animkeys = 0; fail = false; } - BZ_Free(buffer); } if (fail) { + BZ_Free(psabuffer); return false; } @@ -5613,6 +5615,7 @@ qboolean QDECL Mod_LoadPSKModel(model_t *mod, void *buffer, size_t fsize) gmdl[i].nextsurf = (i != num_matt-1)?&gmdl[i+1]:NULL; } + BZ_Free(psabuffer); if (fail) { return false; diff --git a/engine/common/common.c b/engine/common/common.c index a2e4f0921..de2297960 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -2271,7 +2271,8 @@ unsigned int qchar_encode(char *out, unsigned int unicode, int maxlen, qboolean { static const char hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; //FIXME: is it a bug that we can't distinguish between true ascii and 0xe0XX ? - if (((unicode >= 32 || unicode == '\n' || unicode == '\t' || unicode == '\r') && unicode < 128) || (unicode >= 0xe000 && unicode <= 0xe0ff)) + //ntrv are considered special by parsefunstring and are not remapped back to the quake glyphs, so try to keep them as quake glyphs where possible + if (((unicode >= 32 || unicode == '\n' || unicode == '\t' || unicode == '\r') && unicode < 128) || (unicode >= 0xe000 && unicode <= 0xe0ff && unicode != (0xe000|'\n') && unicode != (0xe000|'\t') && unicode != (0xe000|'\r') && unicode != (0xe000|'\v'))) { //quake compatible chars if (maxlen < 1) return 0; @@ -2965,7 +2966,7 @@ conchar_t *COM_ParseFunString(conchar_t defaultflags, const char *str, conchar_t while(*str) { - if (*str & 0x80 && utf8 > 0) + if ((*str & 0x80) && utf8 > 0) { //check for utf-8 int decodeerror; char *end; diff --git a/engine/common/common.h b/engine/common/common.h index 700e60006..7fb02dd12 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -344,8 +344,6 @@ extern char com_homepath[MAX_OSPATH]; extern char com_configdir[MAX_OSPATH]; //dir to put cfg_save configs in //extern char *com_basedir; -void COM_WriteFile (const char *filename, const void *data, int len); - //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 defined(__amd64__) || defined(_AMD64_) || __WORDSIZE == 64 #define FS_64BIT @@ -443,6 +441,8 @@ enum fs_relative{ FS_SYSTEM //a system path. absolute paths are explicitly allowed and expected. }; +void COM_WriteFile (const char *filename, enum fs_relative fsroot, const void *data, int len); + void FS_FlushFSHashReally(qboolean domutexes); void FS_FlushFSHashWritten(void); void FS_FlushFSHashRemoved(void); @@ -458,9 +458,9 @@ vfsfile_t *FS_OpenVFS(const char *filename, const char *mode, enum fs_relative r vfsfile_t *FS_OpenTemp(void); vfsfile_t *FS_OpenTCP(const char *name, int defaultport); +#define countof(array) (sizeof(array)/sizeof(array[0])) #ifdef _WIN32 //windows doesn't support utf-8. Which is a shame really, because that's the charset we expect from everything. -#define countof(array) (sizeof(array)/sizeof(array[0])) char *narrowen(char *out, size_t outlen, wchar_t *wide); wchar_t *widen(wchar_t *out, size_t outbytes, const char *utf8); #define __L(x) L ## x diff --git a/engine/common/cvar.c b/engine/common/cvar.c index c1d329756..42bdf0db1 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -740,6 +740,7 @@ cvar_t *Cvar_SetCore (cvar_t *var, const char *value, qboolean force) if (var->latched_string && !strcmp(var->latched_string, value)) //no point, this would force the same return NULL; + Cvar_ConfigChanged(); if (var->latched_string) Z_Free(var->latched_string); if (!strcmp(var->string, value)) //latch to the original value? remove the latch. diff --git a/engine/common/fs.c b/engine/common/fs.c index 62890d0f0..6f1207882 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -581,7 +581,7 @@ COM_Path_f */ static void COM_PathLine(searchpath_t *s) { - Con_Printf("%s %s%s%s%s%s%s\n", s->logicalpath, + Con_Printf(U8("%s")" %s%s%s%s%s%s\n", s->logicalpath, (s->flags & SPF_REFERENCED)?"^[(ref)\\tip\\Referenced\\desc\\Package will auto-download to clients^]":"", (s->flags & SPF_TEMPORARY)?"^[(temp)\\tip\\Temporary\\desc\\Flushed on map change^]":"", (s->flags & SPF_COPYPROTECTED)?"^[(c)\\tip\\Copyrighted\\desc\\Copy-Protected and is not downloadable^]":"", @@ -672,15 +672,18 @@ COM_Locate_f void COM_Locate_f (void) { flocation_t loc; - if (FS_FLocateFile(Cmd_Argv(1), FSLFRT_LENGTH, &loc)>=0) + char *f = Cmd_Argv(1); + if (strchr(f, '^')) //fte's filesystem is assumed to be utf-8, but that doesn't mean that console input is. and I'm too lazy to utf-8ify the string (in part because markup can be used to exploit ascii assumptions). + Con_Printf("Warning: filename contains markup. If this is because of unicode, set com_parseutf8 1\n"); + if (FS_FLocateFile(f, FSLFRT_LENGTH, &loc)>=0) { if (!*loc.rawname) { - Con_Printf("File is %u bytes compressed inside %s\n", (unsigned)loc.len, loc.search->logicalpath); + Con_Printf("File is %u bytes compressed inside "U8("%s")"\n", (unsigned)loc.len, loc.search->logicalpath); } else { - Con_Printf("Inside %s (%u bytes)\n %s\n", loc.rawname, (unsigned)loc.len, loc.search->logicalpath); + Con_Printf("Inside "U8("%s")" (%u bytes)\n "U8("%s")"\n", loc.rawname, (unsigned)loc.len, loc.search->logicalpath); } } else @@ -694,14 +697,14 @@ COM_WriteFile The filename will be prefixed by the current game directory ============ */ -void COM_WriteFile (const char *filename, const void *data, int len) +void COM_WriteFile (const char *filename, enum fs_relative fsroot, const void *data, int len) { vfsfile_t *vfs; Sys_Printf ("COM_WriteFile: %s\n", filename); - FS_CreatePath(filename, FS_GAMEONLY); - vfs = FS_OpenVFS(filename, "wb", FS_GAMEONLY); + FS_CreatePath(filename, fsroot); + vfs = FS_OpenVFS(filename, "wb", fsroot); if (vfs) { VFS_WRITE(vfs, data, len); @@ -928,7 +931,7 @@ int FS_FLocateFile(const char *filename, FSLF_ReturnType_e returntype, flocation { int depth=0; searchpath_t *search; - char cleanpath[MAX_QPATH]; + char cleanpath[MAX_OSPATH]; flocation_t allownoloc; void *pf; @@ -3158,7 +3161,6 @@ void FS_ReloadPackFiles_f(void) #ifdef MINGW #define byte BYTE //some versions of mingw headers are broken slightly. this lets it compile. #endif -#include static qboolean Sys_SteamHasFile(char *basepath, int basepathlen, char *steamdir, char *fname) { /* @@ -3209,7 +3211,7 @@ static INT CALLBACK StupidBrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LP case BFFM_VALIDATEFAILEDW: break; //FIXME: validate that the gamedir contains what its meant to case BFFM_SELCHANGED: - if (SHGetPathFromIDListW((LPITEMIDLIST) lp, szDir)) + if (pSHGetPathFromIDListW((LPITEMIDLIST) lp, szDir)) { wchar_t statustxt[MAX_OSPATH]; while(foo = wcschr(szDir, '\\')) @@ -3225,6 +3227,7 @@ static INT CALLBACK StupidBrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LP } return 0; } +int MessageBoxU(HWND hWnd, char *lpText, char *lpCaption, UINT uType); #endif qboolean Sys_DoDirectoryPrompt(char *basepath, size_t basepathsize, const char *poshname, const char *savedname) @@ -3242,6 +3245,10 @@ qboolean Sys_DoDirectoryPrompt(char *basepath, size_t basepathsize, const char * widen(resultpath, sizeof(resultpath), poshname); _snwprintf(title, countof(title), L"Please locate your existing %s installation", resultpath); + + //force mouse to deactivate, so that we can actually see it. + INS_UpdateGrabs(false, false); + bi.lpszTitle = title; bi.ulFlags = BIF_RETURNONLYFSDIRS|BIF_STATUSTEXT; @@ -3249,33 +3256,16 @@ qboolean Sys_DoDirectoryPrompt(char *basepath, size_t basepathsize, const char * bi.lParam = 0;//(LPARAM)poshname; bi.iImage = 0; - //force mouse to deactivate, so that we can actually see it. - INS_UpdateGrabs(false, false); - - il = SHBrowseForFolderW(&bi); + il = pSHBrowseForFolderW?pSHBrowseForFolderW(&bi):NULL; if (il) { - SHGetPathFromIDListW(il, resultpath); + pSHGetPathFromIDListW(il, resultpath); CoTaskMemFree(il); narrowen(basepath, basepathsize, resultpath); if (savedname) { - HKEY key = NULL; - //and save it into the windows registry - if (RegCreateKeyExW(HKEY_CURRENT_USER, L"SOFTWARE\\" _L(FULLENGINENAME) L"\\GamePaths", - 0, NULL, - REG_OPTION_NON_VOLATILE, - KEY_WRITE, - NULL, - &key, - NULL) == ERROR_SUCCESS) - { - wchar_t wsavedname[MAX_OSPATH]; - widen(wsavedname, sizeof(wsavedname), savedname); - RegSetValueExW(key, wsavedname, 0, REG_SZ, (BYTE*)resultpath, sizeof(wchar_t)*wcslen(resultpath)); - - RegCloseKey(key); - } + if (MessageBoxU(mainwindow, va("Would you like to save the location of %s as:\n%s", poshname, resultpath), "Save Instaltion path", MB_YESNO|MB_DEFBUTTON2) == IDYES) + MyRegSetValue(HKEY_CURRENT_USER, "SOFTWARE\\" FULLENGINENAME "\\GamePaths", savedname, REG_SZ, basepath, strlen(basepath)); } return true; } diff --git a/engine/common/fs_win32.c b/engine/common/fs_win32.c index 987dac88c..4f0e825b2 100644 --- a/engine/common/fs_win32.c +++ b/engine/common/fs_win32.c @@ -328,12 +328,13 @@ 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) - h = CreateFileA(osname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_DELETE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + 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); else if (read) - h = CreateFileA(osname, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + h = CreateFileA(osname, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); else h = INVALID_HANDLE_VALUE; } @@ -341,7 +342,6 @@ static vfsfile_t *QDECL VFSW32_OpenInternal(vfsw32path_t *handle, const char *qu { wchar_t wide[MAX_OSPATH]; widen(wide, sizeof(wide), osname); - h = INVALID_HANDLE_VALUE; if (write || append) { diff --git a/engine/common/gl_q2bsp.c b/engine/common/gl_q2bsp.c index 43535bcc5..dc4fd9126 100644 --- a/engine/common/gl_q2bsp.c +++ b/engine/common/gl_q2bsp.c @@ -116,10 +116,12 @@ void CalcSurfaceExtents (model_t *mod, msurface_t *s) bmaxs[i] = ceil(maxs[i]/(1<lmshift)); s->texturemins[i] = bmins[i] << s->lmshift; - s->extents[i] = (bmaxs[i] - bmins[i]) << s->lmshift; + s->extents[i] = (bmaxs[i] - bmins[i]); -// if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 8176 ) //q2 uses 512. probably for skys. -// Con_Printf ("Bad surface extents (texture %s)\n", s->texinfo->texture->name); +// if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 17 ) //vanilla used 16(+1), glquake used 17(+1). FTE uses 255(+1), but we omit lightmapping instead of crashing if its larger than our limit, so we omit the check here. different engines use different limits here, many of them make no sense. +// Con_Printf ("Bad surface extents (texture %s, more than %i lightmap samples)\n", s->texinfo->texture->name, s->extents[i]); + + s->extents[i] <<= s->lmshift; } } diff --git a/engine/common/pmove.c b/engine/common/pmove.c index 960af0b3d..9962170f2 100644 --- a/engine/common/pmove.c +++ b/engine/common/pmove.c @@ -902,7 +902,7 @@ void PM_CategorizePosition (void) pmove.onground = false; else { - pmove.onground = true; + pmove.onground = !trace.startsolid; pmove.groundent = trace.entnum; groundplane = trace.plane; pmove.waterjumptime = 0; @@ -1273,8 +1273,8 @@ were contacted during the move. */ void PM_PlayerMove (float gamespeed) { - int i; - int tmp; //for rounding +// int i; +// int tmp; //for rounding frametime = pmove.cmd.msec * 0.001*gamespeed; pmove.numtouch = 0; @@ -1337,7 +1337,7 @@ void PM_PlayerMove (float gamespeed) else PM_AirMove (); - //round to network precision +/* //round to network precision for (i = 0; i < 3; i++) { tmp = floor(pmove.velocity[i]*8 + 0.5); @@ -1346,7 +1346,7 @@ void PM_PlayerMove (float gamespeed) pmove.origin[i] = tmp/8.0; } PM_NudgePosition (); - +*/ // set onground, watertype, and waterlevel for final spot PM_CategorizePosition (); diff --git a/engine/common/pr_bgcmd.c b/engine/common/pr_bgcmd.c index 444b924c0..571622411 100644 --- a/engine/common/pr_bgcmd.c +++ b/engine/common/pr_bgcmd.c @@ -514,7 +514,7 @@ pbool QDECL QC_WriteFile(const char *name, void *data, int len) { char buffer[256]; Q_snprintfz(buffer, sizeof(buffer), "%s", name); - COM_WriteFile(buffer, data, len); + COM_WriteFile(buffer, FS_GAMEONLY, data, len); return true; } @@ -1879,7 +1879,7 @@ void PF_fclose_i (int fnum) switch(pf_fopen_files[fnum].accessmode) { case FRIK_FILE_MMAP_RW: - COM_WriteFile(pf_fopen_files[fnum].name, pf_fopen_files[fnum].data, pf_fopen_files[fnum].len); + COM_WriteFile(pf_fopen_files[fnum].name, FS_GAMEONLY, pf_fopen_files[fnum].data, pf_fopen_files[fnum].len); /*fall through*/ case FRIK_FILE_MMAP_READ: pf_fopen_files[fnum].prinst->AddressableFree(pf_fopen_files[fnum].prinst, pf_fopen_files[fnum].data); @@ -1891,7 +1891,7 @@ void PF_fclose_i (int fnum) break; case 1: case 2: - COM_WriteFile(pf_fopen_files[fnum].name, pf_fopen_files[fnum].data, pf_fopen_files[fnum].len); + COM_WriteFile(pf_fopen_files[fnum].name, FS_GAMEONLY, pf_fopen_files[fnum].data, pf_fopen_files[fnum].len); BZ_Free(pf_fopen_files[fnum].data); break; case 3: @@ -2859,7 +2859,7 @@ void QCBUILTIN PF_chr2str (pubprogfuncs_t *prinst, struct globalvars_s *pr_globa { ch = G_FLOAT(OFS_PARM0 + i*3); if (VMUTF8 || ch > 0xff) - s += unicode_encode(s, ch, (string+sizeof(string)-1)-s, VMUTF8MARKUP); + s += unicode_encode(s, ch, (string+sizeof(string)-1)-s, VMUTF8MARKUP||(ch>0xff)); else *s++ = G_FLOAT(OFS_PARM0 + i*3); } @@ -4818,7 +4818,7 @@ void QCBUILTIN PF_coredump (pubprogfuncs_t *prinst, struct globalvars_s *pr_glob int size = 1024*1024*8; char *buffer = BZ_Malloc(size); prinst->save_ents(prinst, buffer, &size, size, 3); - COM_WriteFile("core.txt", buffer, size); + COM_WriteFile("core.txt", FS_GAMEONLY, buffer, size); BZ_Free(buffer); } void QCBUILTIN PF_eprint (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals) diff --git a/engine/common/pr_common.h b/engine/common/pr_common.h index 44b0c2668..4b8a5f18a 100644 --- a/engine/common/pr_common.h +++ b/engine/common/pr_common.h @@ -382,6 +382,7 @@ void QCBUILTIN PF_cl_GetBindMap (pubprogfuncs_t *prinst, struct globalvars_s *pr void QCBUILTIN PF_cl_SetBindMap (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_cl_keynumtostring (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_cl_findkeysforcommand (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); +void QCBUILTIN PF_cl_findkeysforcommandex (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_cl_stringtokeynum(pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_cl_getkeybind (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); void QCBUILTIN PF_cl_setmousetarget (pubprogfuncs_t *prinst, struct globalvars_s *pr_globals); diff --git a/engine/common/q3common.c b/engine/common/q3common.c index 9b3433275..9a199ae17 100644 --- a/engine/common/q3common.c +++ b/engine/common/q3common.c @@ -102,7 +102,7 @@ void VM_fclose (int fnum, int owner) case VM_FS_WRITE: case VM_FS_APPEND: case VM_FS_APPEND2: - COM_WriteFile(vm_fopen_files[fnum].name, vm_fopen_files[fnum].data, vm_fopen_files[fnum].len); + COM_WriteFile(vm_fopen_files[fnum].name, FS_GAMEONLY, vm_fopen_files[fnum].data, vm_fopen_files[fnum].len); BZ_Free(vm_fopen_files[fnum].data); break; } diff --git a/engine/common/sys_win_threads.c b/engine/common/sys_win_threads.c index 0c97d8e28..f0bc515c8 100644 --- a/engine/common/sys_win_threads.c +++ b/engine/common/sys_win_threads.c @@ -85,7 +85,12 @@ unsigned int WINAPI threadwrapper(void *args) Sys_SetThreadName(GetCurrentThreadId(), ((threadwrap_t *)args)->name); #endif #ifdef CATCHCRASH - AddVectoredExceptionHandler(true, nonmsvc_CrashExceptionHandler); + { + PVOID (WINAPI *pAddVectoredExceptionHandler)(ULONG FirstHandler, PVECTORED_EXCEPTION_HANDLER VectoredHandler); + dllfunction_t dbgfuncs[] = {{(void*)&pAddVectoredExceptionHandler, "AddVectoredExceptionHandler"}, {NULL,NULL}}; + if (Sys_LoadLibrary("kernel32.dll", dbgfuncs) && pAddVectoredExceptionHandler) + pAddVectoredExceptionHandler(0, nonmsvc_CrashExceptionHandler); + } #endif free(args); @@ -177,7 +182,7 @@ void *Sys_CreateMutex(void) return (void *)mutex; } -qboolean Sys_TryLockMutex(void *mutex) +/*qboolean Sys_TryLockMutex(void *mutex) { #ifdef _DEBUG if (!mutex) @@ -196,7 +201,7 @@ qboolean Sys_TryLockMutex(void *mutex) return true; } return false; -} +}*/ qboolean Sys_LockMutex(void *mutex) { diff --git a/engine/d3d/vid_d3d.c b/engine/d3d/vid_d3d.c index ac3b9e051..8b911a5bd 100644 --- a/engine/d3d/vid_d3d.c +++ b/engine/d3d/vid_d3d.c @@ -595,13 +595,13 @@ static void initD3D9(HWND hWnd, rendererstate_t *info) d3d9dll = LoadLibrary("d3d9.dll"); if (!d3d9dll) { - Con_Printf("Direct3d 9 does not appear to be installed\n"); + Con_Printf(CON_ERROR "Direct3d 9 does not appear to be installed\n"); return; } pDirect3DCreate9 = (void*)GetProcAddress(d3d9dll, "Direct3DCreate9"); if (!pDirect3DCreate9) { - Con_Printf("Direct3d 9 does not appear to be installed properly\n"); + Con_Printf(CON_ERROR "Direct3d 9 does not appear to be installed properly\n"); return; } diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index c1cc77da2..cac18b1e3 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -582,9 +582,9 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e if (!sk->qwskin && *sk->qwskinname) sk->qwskin = Skin_Lookup(sk->qwskinname); if (sk->q1lower != Q1UNSPECIFIED) - bc = sk->q1lower; + bc = e->bottomcolour = sk->q1lower; if (sk->q1upper != Q1UNSPECIFIED) - bc = sk->q1upper; + bc = e->topcolour = sk->q1upper; plskin = sk->qwskin; } } @@ -610,7 +610,9 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e if (!gl_nocolors.ival || forced) { - if (!plskin || plskin->loadstate == SKIN_FAILED) + if (plskin && plskin->loadstate == SKIN_LOADING) + plskin = NULL; + else if (!plskin || plskin->loadstate != SKIN_LOADED) { if (e->playerindex >= 0 && e->playerindex <= MAX_CLIENTS) { diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index 9884a123e..f8bd05a9a 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -6620,7 +6620,7 @@ void Mod_Terrain_Create_f(void) "origin \"0 0 1024\"\n" "}\n" , Cmd_Argv(2)); - COM_WriteFile(mname, mdata, strlen(mdata)); + COM_WriteFile(mname, FS_GAMEONLY, mdata, strlen(mdata)); //FIXME: create 4 sections around the origin } diff --git a/engine/gl/gl_vidnt.c b/engine/gl/gl_vidnt.c index 4a3d9826a..38d80a564 100644 --- a/engine/gl/gl_vidnt.c +++ b/engine/gl/gl_vidnt.c @@ -79,7 +79,8 @@ typedef BOOL (WINAPI *lpfnSetLayeredWindowAttributes)(HWND hwnd, COLORREF crKey, extern cvar_t vid_conwidth, vid_conautoscale; -#define WINDOW_CLASS_NAME L"FTEGLQuake" +#define WINDOW_CLASS_NAME_W L"FTEGLQuake" +#define WINDOW_CLASS_NAME_A "FTEGLQuake" #define MAX_MODE_LIST 128 #define VID_ROW_SIZE 3 @@ -470,8 +471,10 @@ qboolean GLInitialise (char *renderer) if (*renderer && stricmp(renderer, "opengl32.dll") && stricmp(renderer, "opengl32")) { + unsigned int emode = SetErrorMode(SEM_FAILCRITICALERRORS); /*no annoying errors if they use glide*/ Con_DPrintf ("Loading renderer dll \"%s\"", renderer); hInstGL = Sys_LoadLibrary(opengldllname, NULL); + SetErrorMode(emode); if (hInstGL) { usingminidriver = true; @@ -531,7 +534,7 @@ qboolean GLInitialise (char *renderer) } else { - qwglSwapLayerBuffers = (void *)getwglfunc("wglSwapLayerBuffers"); + qwglSwapLayerBuffers = NULL;//(void *)getwglfunc("wglSwapLayerBuffers"); qSwapBuffers = SwapBuffers; qChoosePixelFormat = ChoosePixelFormat; qSetPixelFormat = SetPixelFormat; @@ -765,18 +768,36 @@ qboolean VID_SetWindowedMode (rendererstate_t *info) WindowRect = centerrect(pleft, ptop, pwidth, pheight, wwidth, wheight); // Create the DIB window - dibwindow = CreateWindowExW ( - ExWindowStyle, - WINDOW_CLASS_NAME, - _L(FULLENGINENAME), - WindowStyle, - WindowRect.left, WindowRect.top, - WindowRect.right - WindowRect.left, - WindowRect.bottom - WindowRect.top, - sys_parentwindow, - NULL, - global_hInstance, - NULL); + if (WinNT) + { + dibwindow = CreateWindowExW ( + ExWindowStyle, + WINDOW_CLASS_NAME_W, + _L(FULLENGINENAME), + WindowStyle, + WindowRect.left, WindowRect.top, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + sys_parentwindow, + NULL, + global_hInstance, + NULL); + } + else + { + dibwindow = CreateWindowExA ( + ExWindowStyle, + WINDOW_CLASS_NAME_A, + FULLENGINENAME, + WindowStyle, + WindowRect.left, WindowRect.top, + WindowRect.right - WindowRect.left, + WindowRect.bottom - WindowRect.top, + sys_parentwindow, + NULL, + global_hInstance, + NULL); + } if (!dibwindow) { @@ -889,7 +910,7 @@ qboolean VID_SetFullDIBMode (rendererstate_t *info) gdevmode.dmBitsPerPel = info->bpp; if (info->bpp && (gdevmode.dmBitsPerPel < 15)) { - Con_Printf("Forcing at least 16bpp\n"); + Con_Printf("Forcing at least 15bpp\n"); gdevmode.dmBitsPerPel = 16; } gdevmode.dmDisplayFrequency = info->rate; @@ -899,7 +920,7 @@ qboolean VID_SetFullDIBMode (rendererstate_t *info) if (ChangeDisplaySettings (&gdevmode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { - Con_SafePrintf((gdevmode.dmFields&DM_DISPLAYFREQUENCY)?"Windows rejected mode %i*%i*%i*%i\n":"Windows rejected mode %i*%i*%i\n", (int)gdevmode.dmPelsWidth, (int)gdevmode.dmPelsHeight, (int)gdevmode.dmBitsPerPel, (int)gdevmode.dmDisplayFrequency); + Con_SafePrintf((gdevmode.dmFields&DM_DISPLAYFREQUENCY)?"Windows rejected mode %i*%i*%ibpp@%ihz\n":"Windows rejected mode %i*%i*%ibpp\n", (int)gdevmode.dmPelsWidth, (int)gdevmode.dmPelsHeight, (int)gdevmode.dmBitsPerPel, (int)gdevmode.dmDisplayFrequency); return false; } } @@ -927,18 +948,36 @@ qboolean VID_SetFullDIBMode (rendererstate_t *info) wheight = rect.bottom - rect.top; // Create the DIB window - dibwindow = CreateWindowExW ( - ExWindowStyle, - WINDOW_CLASS_NAME, - _L(FULLENGINENAME), - WindowStyle, - rect.left, rect.top, - wwidth, - wheight, - NULL, - NULL, - global_hInstance, - NULL); + if(WinNT) + { + dibwindow = CreateWindowExW ( + ExWindowStyle, + WINDOW_CLASS_NAME_W, + _L(FULLENGINENAME), + WindowStyle, + rect.left, rect.top, + wwidth, + wheight, + NULL, + NULL, + global_hInstance, + NULL); + } + else + { + dibwindow = CreateWindowExA ( + ExWindowStyle, + WINDOW_CLASS_NAME_A, + FULLENGINENAME, + WindowStyle, + rect.left, rect.top, + wwidth, + wheight, + NULL, + NULL, + global_hInstance, + NULL); + } if (!dibwindow) Sys_Error ("Couldn't create DIB window"); @@ -1002,20 +1041,40 @@ static void Win_Touch_Init(HWND wnd); static qboolean CreateMainWindow(rendererstate_t *info) { qboolean stat; - WNDCLASSW wc; - /* Register the frame class */ - wc.style = CS_OWNDC; - wc.lpfnWndProc = (WNDPROC)GLMainWndProc; - wc.cbClsExtra = 0; - wc.cbWndExtra = 0; - wc.hInstance = global_hInstance; - wc.hIcon = hIcon; - wc.hCursor = hArrowCursor; - wc.hbrBackground = NULL; - wc.lpszMenuName = 0; - wc.lpszClassName = WINDOW_CLASS_NAME; - if (!RegisterClassW (&wc)) //this isn't really fatal, we'll let the CreateWindow fail instead. - Con_Printf("RegisterClass failed\n"); + if (WinNT) + { + WNDCLASSW wc; + /* Register the frame class */ + wc.style = CS_OWNDC; + wc.lpfnWndProc = (WNDPROC)GLMainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = global_hInstance; + wc.hIcon = hIcon; + wc.hCursor = hArrowCursor; + wc.hbrBackground = NULL; + wc.lpszMenuName = 0; + wc.lpszClassName = WINDOW_CLASS_NAME_W; + if (!RegisterClassW (&wc)) //this isn't really fatal, we'll let the CreateWindow fail instead. + Con_Printf("RegisterClass failed\n"); + } + else + { + WNDCLASSA wc; + /* Register the frame class */ + wc.style = CS_OWNDC; + wc.lpfnWndProc = (WNDPROC)GLMainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = global_hInstance; + wc.hIcon = hIcon; + wc.hCursor = hArrowCursor; + wc.hbrBackground = NULL; + wc.lpszMenuName = 0; + wc.lpszClassName = WINDOW_CLASS_NAME_A; + if (!RegisterClassA (&wc)) //this isn't really fatal, we'll let the CreateWindow fail instead. + Con_Printf("RegisterClass failed\n"); + } if (!info->fullscreen) { @@ -2464,7 +2523,10 @@ LONG WINAPI GLMainWndProc ( default: /* pass all unhandled messages to DefWindowProc */ - lRet = DefWindowProcW (hWnd, uMsg, wParam, lParam); + if (WinNT) + lRet = DefWindowProcW (hWnd, uMsg, wParam, lParam); + else + lRet = DefWindowProcA (hWnd, uMsg, wParam, lParam); break; } @@ -2521,7 +2583,10 @@ void GLVID_DeInit (void) Cvar_Unhook(&vid_wndalpha); Cmd_RemoveCommand("vid_recenter"); - UnregisterClassW(WINDOW_CLASS_NAME, global_hInstance); + if (WinNT) + UnregisterClassW(WINDOW_CLASS_NAME_W, global_hInstance); + else + UnregisterClassA(WINDOW_CLASS_NAME_A, global_hInstance); } /* diff --git a/engine/http/httpclient.c b/engine/http/httpclient.c index 74a478b8c..632f3127d 100644 --- a/engine/http/httpclient.c +++ b/engine/http/httpclient.c @@ -491,7 +491,7 @@ static qboolean HTTP_DL_Work(struct dl_download *dl) nl = strchr(msg, '\n'); if (nl) *nl = '\0'; - Con_Printf("HTTP: %s %s\n", buffer, COM_TrimString(msg, trimmed, sizeof(trimmed))); + Con_Printf("HTTP: %s %s (%s)\n", buffer, COM_TrimString(msg, trimmed, sizeof(trimmed)), Location); if (!*Location) Con_Printf("Server redirected to null location\n"); else @@ -1324,6 +1324,21 @@ void HTTP_CL_Think(void) #endif } } + +void HTTP_CL_Terminate(void) +{ + struct dl_download *dl = activedownloads; + struct dl_download *next = NULL; + next = activedownloads; + activedownloads = NULL; + while (next) + { + dl = next; + next = dl->next; + DL_Close(dl); + } + HTTP_CL_Think(); +} #endif #endif /*WEBCLIENT*/ diff --git a/engine/http/iweb.h b/engine/http/iweb.h index c980e862b..52f8a2966 100644 --- a/engine/http/iweb.h +++ b/engine/http/iweb.h @@ -150,6 +150,7 @@ struct dl_download vfsfile_t *VFSPIPE_Open(void); void HTTP_CL_Think(void); +void HTTP_CL_Terminate(void); //kills all active downloads struct dl_download *HTTP_CL_Get(const char *url, const char *localfile, void (*NotifyFunction)(struct dl_download *dl)); struct dl_download *HTTP_CL_Put(const char *url, const char *mime, const char *data, size_t datalen, void (*NotifyFunction)(struct dl_download *dl)); diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index cd86013ad..4407d0b41 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -559,6 +559,11 @@ extern pbool keyword_inline; //don't write the def to the output. extern pbool keyword_strip; //don't write the def to the output. extern pbool keyword_union; //you surly know what a union is! +extern pbool keyword_unused; +extern pbool keyword_used; +extern pbool keyword_static; +extern pbool keyword_nonstatic; +extern pbool keyword_ignore; extern pbool keywords_coexist; extern pbool output_parms; diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index ccd73a700..1b12ee9be 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -50,6 +50,11 @@ pbool keyword_struct; pbool keyword_var; //allow it to be initialised and set around the place. pbool keyword_vector; //for skipping the local +pbool keyword_static; +pbool keyword_nonstatic; +pbool keyword_used; +pbool keyword_unused; + pbool keyword_enum; //kinda like in c, but typedef not supported. pbool keyword_enumflags; //like enum, but doubles instead of adds 1. @@ -69,6 +74,7 @@ pbool keyword_noref; //nowhere else references this, don't strip it. pbool keyword_nosave; //don't write the def to the output. pbool keyword_inline; pbool keyword_strip; +pbool keyword_ignore; pbool keyword_union; //you surly know what a union is! #define keyword_not 1 //hexenc support needs this, and fteqcc can optimise without it, but it adds an extra token after the if, so it can cause no namespace conflicts @@ -2038,6 +2044,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ case OP_LOADA_I: { QCC_sref_t nd = var_a; + QCC_FreeTemp(var_b); nd.ofs += eval_b->_int; //FIXME: case away the array... return nd; @@ -2884,7 +2891,7 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ //a - (n * floor(a/n)); //(except using v|v instead of floor) var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_F], var_a, var_b, NULL, STFL_CONVERTA|STFL_CONVERTB|STFL_PRESERVEA|STFL_PRESERVEB); - var_c = QCC_PR_Statement(&pr_opcodes[OP_BITOR_F], var_c, var_c, NULL); + var_c = QCC_PR_StatementFlags(&pr_opcodes[OP_BITOR_F], var_c, var_c, NULL, STFL_PRESERVEA); var_c = QCC_PR_Statement(&pr_opcodes[OP_MUL_F], var_b, var_c, NULL); return QCC_PR_Statement(&pr_opcodes[OP_SUB_F], var_a, var_c, NULL); @@ -2960,7 +2967,10 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ { const QCC_eval_t *eval_a = QCC_SRef_EvalConst(var_a); if (eval_a) + { + QCC_FreeTemp(var_a); var_a = QCC_MakeFloatConst(eval_a->_int); + } else { var_c = QCC_PR_GetSRef(NULL, "itof", NULL, false, 0, 0); @@ -2983,7 +2993,10 @@ QCC_sref_t QCC_PR_StatementFlags ( QCC_opcode_t *op, QCC_sref_t var_a, QCC_sref_ { const QCC_eval_t *eval_a = QCC_SRef_EvalConst(var_a); if (eval_a) + { + QCC_FreeTemp(var_a); var_a = QCC_MakeIntConst(eval_a->_float); + } else { var_c = QCC_PR_GetSRef(NULL, "ftoi", NULL, false, 0, 0); @@ -3972,6 +3985,7 @@ struct inlinectx_s { QCC_def_t *def; QCC_def_t *srcsym; + int bias; } locals[64]; int numlocals; }; @@ -3993,6 +4007,7 @@ static pbool QCC_PR_InlinePushResult(struct inlinectx_s *ctx, QCC_sref_t src, QC else if (ctx->locals[i].def) QCC_FreeDef(ctx->locals[i].def); ctx->locals[i].def = mappedto.sym; + ctx->locals[i].bias = mappedto.ofs - src.ofs; //FIXME: this feels unsafe (needed for array[immediate] fixups) return true; } @@ -4015,10 +4030,11 @@ static QCC_sref_t QCC_PR_InlineFindDef(struct inlinectx_s *ctx, QCC_sref_t src, { QCC_FreeDef(ctx->locals[p].def); ctx->locals[p].def = NULL; + ctx->locals[p].bias = 0; d = NULL; return QCC_MakeSRefForce(NULL, 0, NULL); } - return QCC_MakeSRefForce(d, src.ofs, src.cast); + return QCC_MakeSRefForce(d, src.ofs + ctx->locals[p].bias, src.cast); } } @@ -4030,7 +4046,7 @@ static QCC_sref_t QCC_PR_InlineFindDef(struct inlinectx_s *ctx, QCC_sref_t src, { if (src.sym->symbolheader == local) { - return QCC_MakeSRefForce(ctx->arglist[p].sym->symbolheader, ctx->arglist[p].sym->ofs+src.ofs, src.cast); + return QCC_MakeSRefForce(ctx->arglist[p].sym->symbolheader, src.ofs, src.cast); } } @@ -4082,9 +4098,14 @@ static QCC_sref_t QCC_PR_InlineFindDef(struct inlinectx_s *ctx, QCC_sref_t src, //returns a string saying why inlining failed. static char *QCC_PR_InlineStatements(struct inlinectx_s *ctx) { + /*FIXME: what happens with: + t = foo; + foo = 5; + return t; + */ QCC_sref_t a, b, c; QCC_statement_t *st; -// const QCC_eval_t *eval; + const QCC_eval_t *eval; // float af,bf; // int i; @@ -4099,6 +4120,10 @@ static char *QCC_PR_InlineStatements(struct inlinectx_s *ctx) case OP_IFNOT_I: case OP_IF_S: case OP_IFNOT_S: + if (st->b.ofs > 0 && st[st->b.ofs].op == OP_DONE) + { + //logically, an if statement around the entire function is safe because the locals are safe + } case OP_GOTO: case OP_SWITCH_F: case OP_SWITCH_I: @@ -4138,20 +4163,18 @@ static char *QCC_PR_InlineStatements(struct inlinectx_s *ctx) return "OP_RETURN no a"; } return NULL; -#ifdef IAMNOTLAZY case OP_BOUNDCHECK: - a = QCC_PR_InlineFindDef(ctx, st->a); + a = QCC_PR_InlineFindDef(ctx, st->a, false); QCC_PR_InlinePushResult(ctx, st->a, a); - eval = QCC_SRef_EvalConst(st->a); + eval = QCC_SRef_EvalConst(a); if (eval) { - if (eval->_int < st->c || eval->_int >= st->b) + if (eval->_int < (int)st->c.ofs || eval->_int >= (int)st->b.ofs) QCC_PR_ParseWarning(0, "constant value exceeds bounds failed bounds check while inlining\n"); } else - QCC_PR_SimpleStatement(OP_BOUNDCHECK, a->ofs, st->b, st->c, false); + QCC_PR_SimpleStatement(&pr_opcodes[OP_BOUNDCHECK], a, st->b, st->c, false); break; -#endif default: if ((st->op >= OP_CALL0 && st->op <= OP_CALL8) || (st->op >= OP_CALL1H && st->op <= OP_CALL8H)) { @@ -4910,6 +4933,7 @@ QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the func cou if (!func.sym->initialized) func.sym->initialized = 3; func.sym->referenced = true; + QCC_FreeTemp(func); e = QCC_PR_Expression(TOP_PRIORITY, EXPR_DISALLOW_COMMA); QCC_PR_Expect(")"); @@ -4917,6 +4941,7 @@ QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the func cou d = QCC_PR_GetSRef(NULL, "nextent", NULL, false, 0, false); if (!d.cast) QCC_PR_ParseError(0, "the nextent builtin is not defined"); + QCC_UnFreeTemp(e); d = QCC_PR_GenerateFunctionCall (nullsref, d, &e, &type_float, 1); d = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_F], d, QCC_MakeIntConst(1), NULL, 0); e = QCC_PR_StatementFlags(&pr_opcodes[OP_DIV_F], e, d, NULL, 0); @@ -5600,7 +5625,7 @@ void QCC_PR_EmitClassFromFunction(QCC_def_t *scope, QCC_type_t *basetype) pr.local_head.nextlocal = NULL; pr.local_tail = &pr.local_head; - + scope->initialized = true; scope->symboldata[scope->ofs].function = pr_scope - functions; ed = QCC_PR_GetSRef(type_entity, "self", NULL, true, 0, false); @@ -6295,6 +6320,8 @@ QCC_ref_t *QCC_PR_ParseRefValue (QCC_ref_t *refbuf, QCC_type_t *assumeclass, pbo } } + d.sym->referenced = true; + //class code uses self as though it was 'this'. its a hack, but this is QC. if (assumeclass && pr_classtype && !strcmp(name, "self")) { @@ -7340,11 +7367,11 @@ QCC_sref_t QCC_StoreToRef(QCC_ref_t *dest, QCC_sref_t source, pbool readable, pb TypeName(source.cast, typea, sizeof(typea)); TypeName(dest->cast, typeb, sizeof(typeb)); if (dest->type == REF_FIELD) - QCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, "type mismatch: %s to %s %s.%s", typea, typeb, dest->base.cast->name, QCC_GetSRefName(dest->index)); + QCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, "type mismatch: %s %s to %s %s.%s", typea, QCC_GetSRefName(source), typeb, QCC_GetSRefName(dest->base), QCC_GetSRefName(dest->index)); else if (dest->index.cast) - QCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, "type mismatch: %s to %s[%s]", typea, typeb, QCC_GetSRefName(dest->base), QCC_GetSRefName(dest->index)); + QCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, "type mismatch: %s %s to %s[%s]", typea, QCC_GetSRefName(source), typeb, QCC_GetSRefName(dest->base), QCC_GetSRefName(dest->index)); else - QCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, "type mismatch: %s to %s %s", typea, typeb, QCC_GetSRefName(dest->base)); + QCC_PR_ParseWarning(WARN_STRICTTYPEMISMATCH, "type mismatch: %s %s to %s %s", typea, QCC_GetSRefName(source), typeb, QCC_GetSRefName(dest->base)); } } } @@ -10646,6 +10673,7 @@ void QCC_PR_EmitArrayGetFunction(QCC_def_t *scope, QCC_def_t *arraydef, char *ar index = QCC_PR_GetSRef(type_float, "__indexg", pr_scope, true, 0, false); + scope->initialized = true; scope->symboldata[scope->ofs]._int = pr_scope - functions; /* if (fasttrackpossible) @@ -10828,6 +10856,7 @@ void QCC_PR_EmitArraySetFunction(QCC_def_t *scope, QCC_def_t *arraydef, char *ar index = QCC_PR_GetSRef(type_float, "indexs___", pr_scope, true, 0, false); value = QCC_PR_GetSRef(thearray.cast, "value___", pr_scope, true, 0, false); + scope->initialized = true; scope->symboldata[scope->ofs]._int = pr_scope - functions; if (fasttrackpossible.cast) @@ -11272,6 +11301,8 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, char *name, struct QCC_function_s *s QCC_PR_ParseWarning(WARN_MISUSEDAUTOCVAR, "Autocvar \"%s\" defined as constant", name); else if (flags & GDF_STATIC) QCC_PR_ParseWarning(WARN_MISUSEDAUTOCVAR, "Autocvar \"%s\" defined as static", name); + if (!(flags & GDF_STRIP)) + flags |= GDF_USED; } def = QCC_PR_DummyDef(type, name, scope, arraysize, NULL, ofs, true, flags); @@ -11829,7 +11860,7 @@ void QCC_PR_ParseDefs (char *classname) pbool inlinefunction = false; pbool allowinline = false; pbool dostrip = false; - pbool ignored = false; + pbool forceused = false; int arraysize; unsigned int gd_flags; @@ -12046,20 +12077,24 @@ void QCC_PR_ParseDefs (char *classname) isconstant = true; else if (QCC_PR_CheckKeyword(keyword_var, "var")) isvar = true; - else if (QCC_PR_CheckKeyword(keyword_var, "static")) + else if (QCC_PR_CheckKeyword(keyword_static, "static")) isstatic = true; - else if (!pr_scope && QCC_PR_CheckKeyword(keyword_var, "nonstatic")) + else if (!pr_scope && QCC_PR_CheckKeyword(keyword_nonstatic, "nonstatic")) isstatic = false; else if (QCC_PR_CheckKeyword(keyword_noref, "noref")) noref=true; + else if (QCC_PR_CheckKeyword(keyword_unused, "unused")) + noref=true; + else if (QCC_PR_CheckKeyword(keyword_used, "used")) + forceused=true; else if (QCC_PR_CheckKeyword(keyword_nosave, "nosave")) nosave = true; else if (QCC_PR_CheckKeyword(keyword_strip, "strip")) dostrip = true; else if (QCC_PR_CheckKeyword(keyword_inline, "inline")) allowinline = true; - else if (QCC_PR_CheckKeyword(keyword_inline, "ignore")) - ignored = true; + else if (QCC_PR_CheckKeyword(keyword_ignore, "ignore")) + dostrip = true; else break; } @@ -12085,7 +12120,7 @@ void QCC_PR_ParseDefs (char *classname) def = QCC_PR_GetDef (type, name, NULL, true, 0, false); - if (autoprototype || ignored) + if (autoprototype || dostrip) { //ignore the code and stuff if (QCC_PR_CheckKeyword(keyword_external, "external")) @@ -12283,7 +12318,7 @@ void QCC_PR_ParseDefs (char *classname) gd_flags |= GDF_INLINE; if (dostrip) gd_flags |= GDF_STRIP; - if (isvar && !ignored) //FIXME: make proper pragma(used) thingie + else if (forceused) //FIXME: make proper pragma(used) thingie gd_flags |= GDF_USED; #if IAMNOTLAZY @@ -12366,10 +12401,12 @@ void QCC_PR_ParseDefs (char *classname) // QCC_PR_ParseErrorPrintDef(ERR_REDECLARATION, def, "%s redeclared", name); } - if (autoprototype || ignored) + if (autoprototype || dostrip) { //ignore the code and stuff - if (ignored && !def->initialized) + if (dostrip && !def->initialized) def->initialized = 3; + if (dostrip) + def->referenced = true; if (QCC_PR_CheckToken("[")) { while (!QCC_PR_CheckToken("]")) @@ -12425,7 +12462,12 @@ void QCC_PR_ParseDefs (char *classname) if (type->type == ev_function) isconstant = !isvar; - if (type->type == ev_field) + if (dostrip) + { + def->constant = isconstant; + def->referenced = true; + } + else if (type->type == ev_field) { //fields are const by default, even when not initialised (as they are initialised behind the scenes) if (isconstant) diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 7d9d9f482..0fcff48ec 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -4642,6 +4642,7 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) pbool isvirt = false; pbool isnonvirt = false; pbool isstatic = false; + pbool isignored = false; while(1) { if (QCC_PR_CheckKeyword(1, "nonvirtual")) @@ -4650,6 +4651,10 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) isstatic = true; else if (QCC_PR_CheckKeyword(1, "virtual")) isvirt = true; + else if (QCC_PR_CheckKeyword(1, "ignore")) + isignored = true; + else if (QCC_PR_CheckKeyword(1, "strip")) + isignored = true; else break; } @@ -4721,19 +4726,27 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) QC_snprintfz(membername, sizeof(membername), "%s::%s", classname, parmname); if (isnull) { - def = QCC_PR_GetDef(newparm, membername, NULL, true, 0, 0); - def->symboldata[def->ofs].function = 0; - def->initialized = 1; + if (isignored) + def = NULL; + else + { + def = QCC_PR_GetDef(newparm, membername, NULL, true, 0, 0); + def->symboldata[def->ofs].function = 0; + def->initialized = 1; + } } else { - def = QCC_PR_GetDef(newparm, membername, NULL, true, 0, GDF_CONST); + if (isignored) + def = NULL; + else + def = QCC_PR_GetDef(newparm, membername, NULL, true, 0, GDF_CONST); if (newparm->type != ev_function) QCC_Error(ERR_INTERNAL, "Can only initialise member functions"); else { - if (autoprototype) + if (autoprototype || isignored) { QCC_PR_Expect("{"); @@ -4765,9 +4778,10 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) } } } - QCC_FreeDef(def); + if (def) + QCC_FreeDef(def); - if (!isvirt) + if (!isvirt && !isignored) { QCC_def_t *fdef; QCC_type_t *pc; @@ -4799,6 +4813,9 @@ QCC_type_t *QCC_PR_ParseType (int newtype, pbool silentfail) QCC_PR_Expect(";"); + if (isignored) //member doesn't really exist + continue; + //static members are technically just funny-named globals, and do not generate fields. if (isnonvirt || isstatic || (newparm->type == ev_function && !arraysize)) { diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 42e66d63b..e6d8ece68 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -282,7 +282,13 @@ compiler_flag_t compiler_flag[] = { {&keyword_goto, defaultkeyword, "goto", "Keyword: goto", "Disables the 'goto' keyword."}, {&keyword_int, defaultkeyword, "int", "Keyword: int", "Disables the 'int' keyword."}, {&keyword_integer, defaultkeyword, "integer", "Keyword: integer", "Disables the 'integer' keyword."}, - {&keyword_noref, defaultkeyword, "noref", "Keyword: noref", "Disables the 'noref' keyword."}, //nowhere else references this, don't strip it. + {&keyword_noref, defaultkeyword, "noref", "Keyword: noref", "Disables the 'noref' keyword."}, //nowhere else references this, don't warn about it. + {&keyword_unused, nondefaultkeyword, "unused", "Keyword: unused", "Disables the 'unused' keyword. 'unused' means that the variable is unused, you're aware that its unused, and you'd rather not know about all the warnings this results in."}, + {&keyword_used, nondefaultkeyword, "used", "Keyword: used", "Disables the 'used' keyword. 'used' means that the variable is used even if the qcc can't see how - thus preventing it from ever being stripped."}, + {&keyword_static, defaultkeyword, "static", "Keyword: static", "Disables the 'static' keyword. 'static' means that a variable has altered scope. On globals, the variable is visible only to the current .qc file. On locals, the variable's value does not change between calls to the function. On class variables, specifies that the field is a scoped global instead of a local. On class functions, specifies that 'this' is expected to be invalid and that the function will access any memembers via it."}, + {&keyword_nonstatic, defaultkeyword, "nonstatic", "Keyword: nonstatic", "Disables the 'nonstatic' keyword. 'nonstatic' acts upon globals+functions, reverting the defaultstatic pragma on a per-variable basis. For use by people who prefer to keep their APIs explicit."}, + {&keyword_ignore, nondefaultkeyword, "ignore", "Keyword: ignore", "Disables the 'ignore' keyword. 'ignore' is expected to typically be hidden behind a 'csqconly' define, and in such a context can be used to conditionally compile functions a little more gracefully. The opposite of the 'used' keyword. These variables/functions/members are ALWAYS stripped, and effectively ignored."}, + {&keyword_nosave, defaultkeyword, "nosave", "Keyword: nosave", "Disables the 'nosave' keyword."}, //don't write the def to the output. {&keyword_inline, defaultkeyword, "inline", "Keyword: inline", "Disables the 'inline' keyword."}, //don't write the def to the output. {&keyword_strip, defaultkeyword, "strip", "Keyword: strip", "Disables the 'strip' keyword."}, //don't write the def to the output. @@ -1307,9 +1313,9 @@ pbool QCC_WriteData (int crc) for (i = 0; i < nummodels; i++) { if (!precache_model[i].used) - QCC_PR_Warning(WARN_EXTRAPRECACHE, precache_model[i].filename, precache_model[i].fileline, "Model %s was precached but not directly used", precache_model[i].name); + QCC_PR_Warning(WARN_EXTRAPRECACHE, precache_model[i].filename, precache_model[i].fileline, "Model \"%s\" was precached but not directly used", precache_model[i].name); else if (!precache_model[i].block) - QCC_PR_Warning(WARN_NOTPRECACHED, precache_model[i].filename, precache_model[i].fileline, "Model %s was used but not precached", precache_model[i].name); + QCC_PR_Warning(WARN_NOTPRECACHED, precache_model[i].filename, precache_model[i].fileline, "Model \"%s\" was used but not precached", precache_model[i].name); } //PrintStrings (); //PrintFunctions (); diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 44aac91cb..1f9660a07 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -205,11 +205,11 @@ void PDECL ED_Spawned (struct edict_s *ent, int loading) if (!loading || !ent->xv->Version) { - ent->xv->dimension_see = 255; - ent->xv->dimension_seen = 255; + ent->xv->dimension_see = pr_global_struct->dimension_default; + ent->xv->dimension_seen = pr_global_struct->dimension_default; ent->xv->dimension_ghost = 0; - ent->xv->dimension_solid = 255; - ent->xv->dimension_hit = 255; + ent->xv->dimension_solid = pr_global_struct->dimension_default; + ent->xv->dimension_hit = pr_global_struct->dimension_default; if (progstype != PROG_H2) ent->xv->drawflags = SCALE_ORIGIN_ORIGIN; //if not running hexen2, default the scale origin to the actual origin. @@ -666,6 +666,7 @@ void PR_LoadGlabalStruct(qboolean muted) static float svphysicsmode = 2; static float writeonly; static float dimension_send_default; + static float dimension_default = 255; static float zero_default; static float input_buttons_default; static float input_timelength_default; @@ -725,6 +726,7 @@ void PR_LoadGlabalStruct(qboolean muted) globalfunc (false, SetChangeParms); globalfloat (false, cycle_wrapped); globalfloat (false, dimension_send); + globalfloat (false, dimension_default); globalfloat (false, clientcommandframe); @@ -747,6 +749,7 @@ void PR_LoadGlabalStruct(qboolean muted) // make sure these entries are always valid pointers ensureglobal(dimension_send, dimension_send_default); + ensureglobal(dimension_default, dimension_default); ensureglobal(trace_endcontents, writeonly); ensureglobal(trace_surfaceflags, writeonly); @@ -804,8 +807,8 @@ void PR_LoadGlabalStruct(qboolean muted) //this can be a map start or a loadgame. don't hurt stuff. if (!pr_global_struct->dimension_send) - pr_global_struct->dimension_send = 255; -/* pr_global_struct->dimension_send = 255; + pr_global_struct->dimension_send = pr_global_struct->dimension_default; +/* pr_global_struct->serverflags = 0; pr_global_struct->total_secrets = 0; pr_global_struct->total_monsters = 0; @@ -1218,7 +1221,7 @@ void PR_SSCoreDump_f(void) int size = 1024*1024*8; char *buffer = BZ_Malloc(size); svprogfuncs->save_ents(svprogfuncs, buffer, &size, size, 3); - COM_WriteFile("ssqccore.txt", buffer, size); + COM_WriteFile("ssqccore.txt", FS_GAMEONLY, buffer, size); BZ_Free(buffer); } } @@ -9681,7 +9684,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"keynumtostring_csqc",PF_Fixme,0, 0, 0, 340, D("string(float keynum)", "Returns a hunam-readable name for the given keycode, as a tempstring.")},// (found in menuqc) {"stringtokeynum", PF_Fixme, 0, 0, 0, 341, D("float(string keyname)", "Looks up the key name in the same way that the bind command would, returning the keycode for that key.")},// (EXT_CSQC) {"stringtokeynum_csqc", PF_Fixme,0, 0, 0, 341, D("float(string keyname)", "Looks up the key name in the same way that the bind command would, returning the keycode for that key.")},// (found in menuqc) - {"getkeybind", PF_Fixme, 0, 0, 0, 342, D("string(float keynum)", "Finds the current binding for the given key (ignores modifiers like shift/alt/ctrl).")},// (EXT_CSQC) + {"getkeybind", PF_Fixme, 0, 0, 0, 342, D("string(float keynum)", "Returns the current binding for the given key (returning only the command executed when no modifiers are pressed).")},// (EXT_CSQC) {"setcursormode", PF_Fixme, 0, 0, 0, 343, D("void(float usecursor, optional string cursorimage, optional vector hotspot, optional float scale)", "Pass TRUE if you want the engine to release the mouse cursor (absolute input events + touchscreen mode). Pass FALSE if you want the engine to grab the cursor (relative input events + standard looking). If the image name is specified, the engine will use that image for a cursor (use an empty string to clear it again), in a way that will not conflict with the console. Images specified this way will be hardware accelerated, if supported by the platform/port.")}, {"getmousepos", PF_Fixme, 0, 0, 0, 344, D("vector()", "Nasty convoluted DP extension. Typically returns deltas instead of positions. Use CSQC_InputEvent for such things in csqc mods.")}, // #344 This is a DP extension @@ -9918,7 +9921,7 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"uri_escape", PF_uri_escape, 0, 0, 0, 510, "string(string in)"},//DP_QC_URI_ESCAPE {"uri_unescape", PF_uri_unescape, 0, 0, 0, 511, "string(string in)"},//DP_QC_URI_ESCAPE {"num_for_edict", PF_num_for_edict, 0, 0, 0, 512, "float(entity ent)"},//DP_QC_NUM_FOR_EDICT - {"uri_get", PF_uri_get, 0, 0, 0, 513, D("#define uri_post uri_get\nfloat(string uril, float id, optional string postmimetype, optional string postdata)", "uri_get() gets content from an URL and calls a callback \"uri_get_callback\" with it set as string; an unique ID of the transfer is returned\nreturns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string")},//DP_QC_URI_GET + {"uri_get", PF_uri_get, 0, 0, 0, 513, D("#define uri_post uri_get\nfloat(string uril, float id, optional string postmimetype, optional string postdata)", "uri_get() gets content from an URL and calls a callback \"uri_get_callback\" with it set as string; an unique ID of the transfer is returned\nreturns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string\nFor a POST request, you will typically want the postmimetype set to application/x-www-form-urlencoded.\nFor a GET request, omit the mime+data entirely.\nConsult your webserver/php/etc documentation for best-practise.")},//DP_QC_URI_GET {"uri_post", PF_uri_get, 0, 0, 0, 513, D("float(string uril, float id, optional string postmimetype, optional string postdata)", "uri_get() gets content from an URL and calls a callback \"uri_get_callback\" with it set as string; an unique ID of the transfer is returned\nreturns 1 on success, and then calls the callback with the ID, 0 or the HTTP status code, and the received data in a string"), true},//DP_QC_URI_POST {"tokenize_console",PF_tokenize_console,0, 0, 0, 514, D("float(string str)", "Tokenize a string exactly as the console's tokenizer would do so. The regular tokenize builtin became bastardized for convienient string parsing, which resulted in a large disparity that can be exploited to bypass checks implemented in a naive SV_ParseClientCommand function, therefore you can use this builtin to make sure it exactly matches.")}, {"argv_start_index",PF_argv_start_index,0, 0, 0, 515, D("float(float idx)", "Returns the character index that the tokenized arg started at.")}, @@ -9927,7 +9930,8 @@ BuiltinList_t BuiltinList[] = { //nq qw h2 ebfs {"cvar_description",PF_cvar_description,0, 0, 0, 518, D("string(string cvarname)", "Retrieves the description of a cvar, which might be useful for tooltips or help files. This may still not be useful.")}, {"gettime", PF_gettime, 0, 0, 0, 519, "float(optional float timetype)"}, {"keynumtostring_omgwtf",PF_Fixme, 0, 0, 0, 520, "string(float keynum)"}, //excessive third version in dp's csqc. - {"findkeysforcommand",PF_Fixme, 0, 0, 0, 521, "string(string command, optional float bindmap)"}, + {"findkeysforcommand",PF_Fixme, 0, 0, 0, 521, D("string(string command, optional float bindmap)", "Returns a list of keycodes that perform the given console command in a format that can only be parsed via tokenize (NOT tokenize_console). This only and always returns two values - if only one key is actually bound, -1 will be returned. The bindmap argument is listed for compatibility with dp-specific defs, but is ignored in FTE.")}, + {"findkeysforcommandex",PF_Fixme, 0, 0, 0, 0, D("string(string command)", "Returns a list of key bindings in keyname format instead of keynums. Use tokenize to parse. This list may contain modifiers. May return large numbers of keys.")}, // {"initparticlespawner",PF_Fixme, 0, 0, 0, 522, "void(float max_themes)"}, // {"resetparticle", PF_Fixme, 0, 0, 0, 523, "void()"}, // {"particletheme", PF_Fixme, 0, 0, 0, 524, "void(float theme)"}, @@ -10698,6 +10702,8 @@ void PR_DumpPlatform_f(void) {"parm17, parm18, parm19, parm20, parm21, parm22, parm23, parm24, parm25, parm26, parm27, parm28, parm29, parm30, parm31, parm32", "float", QW|NQ}, {"parm33, parm34, parm35, parm36, parm37, parm38, parm39, parm40, parm41, parm42, parm43, parm44, parm45, parm46, parm47, parm48", "float", QW|NQ}, {"parm49, parm50, parm51, parm52, parm53, parm54, parm55, parm56, parm57, parm58, parm59, parm60, parm61, parm62, parm63, parm64", "float", QW|NQ}, + {"dimension_send", "var float", QW|NQ, "Used by multicast functionality. Multicasts (and related builtins that multicast internally) will only be sent to players where (player.dimension_see & dimension_send) is non-zero."}, + {"dimension_default", "//var float", QW|NQ, "Default dimension bitmask", 255}, {"physics_mode", "var float", QW|NQ|CS, "0: original csqc - physics are not run\n1: DP-compat. Thinks occur, but not true movetypes.\n2: movetypes occur just as they do in ssqc.", 2}, {"gamespeed", "float", CS, "Set by the engine, this is the value of the sv_gamespeed cvar"}, {"numclientseats", "float", CS, "This is the number of splitscreen clients currently running on this client."}, diff --git a/engine/server/pr_lua.c b/engine/server/pr_lua.c index ed3fdd852..0fdb5b89a 100644 --- a/engine/server/pr_lua.c +++ b/engine/server/pr_lua.c @@ -1950,6 +1950,7 @@ static void Lua_SetupGlobals(world_t *world) memset(&lua.globals, 0, sizeof(lua.globals)); lua.globals.physics_mode = 2; lua.globals.dimension_send = 255; + lua.globals.dimension_default = 255; flds = 0; bucks = 64; diff --git a/engine/server/progdefs.h b/engine/server/progdefs.h index 2a5a512a3..6d1dc8ccc 100644 --- a/engine/server/progdefs.h +++ b/engine/server/progdefs.h @@ -81,6 +81,7 @@ typedef struct nqglobalvars_s func_t *SetChangeParms; float *cycle_wrapped; float *dimension_send; + float *dimension_default; float *physics_mode; float *clientcommandframe; diff --git a/engine/server/savegame.c b/engine/server/savegame.c index fb01f293b..a0cd7ada8 100644 --- a/engine/server/savegame.c +++ b/engine/server/savegame.c @@ -1215,7 +1215,7 @@ void SV_Savegame (char *savename) rgbbuffer = VID_GetRGBInfo(0, &width, &height); if (rgbbuffer) { - SCR_ScreenShot(savefilename, rgbbuffer, width, height); + SCR_ScreenShot(savefilename, FS_GAMEONLY, rgbbuffer, width, height); BZ_Free(rgbbuffer); diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index dd4c57114..9476eb25e 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -2573,6 +2573,31 @@ void SV_ReallyEvilHack_f(void) } */ +void SV_PrecacheList_f(void) +{ + unsigned int i; + for (i = 0; i < sizeof(sv.strings.vw_model_precache)/sizeof(sv.strings.vw_model_precache[0]); i++) + { + if (sv.strings.vw_model_precache[i]) + Con_Printf("vweap %u: %s\n", i, sv.strings.vw_model_precache[i]); + } + for (i = 0; i < MAX_PRECACHE_MODELS; i++) + { + if (sv.strings.model_precache[i]) + Con_Printf("model %u: %s\n", i, sv.strings.model_precache[i]); + } + for (i = 0; i < MAX_PRECACHE_SOUNDS; i++) + { + if (*sv.strings.sound_precache[i]) + Con_Printf("sound %u: %s\n", i, sv.strings.sound_precache[i]); + } + for (i = 0; i < MAX_SSPARTICLESPRE; i++) + { + if (*sv.strings.particle_precache[i]) + Con_Printf("pticl %u: %s\n", i, sv.strings.particle_precache[i]); + } +} + /* ================== SV_InitOperatorCommands @@ -2636,6 +2661,7 @@ void SV_InitOperatorCommands (void) Cmd_AddCommand ("killserver", SV_KillServer_f); Cmd_AddCommand ("map", SV_Map_f); + Cmd_AddCommandD ("precaches", SV_PrecacheList_f, "Displays a list of current server precaches."); #ifdef Q3SERVER Cmd_AddCommand ("spmap", SV_Map_f); #endif diff --git a/engine/server/sv_cluster.c b/engine/server/sv_cluster.c index 9a12ff359..f38a4a7af 100644 --- a/engine/server/sv_cluster.c +++ b/engine/server/sv_cluster.c @@ -119,9 +119,11 @@ pubsubserver_t *MSV_FindSubServer(unsigned int id) } //"5" finds server 5 only +//"5:" is equivelent to the above //"5:dm4" finds server 5, and will start it if not known (even if a different node is running the same map) //"0:dm4" starts a new server running dm4, even if its already running //":dm4" finds any server running dm4. starts a new one if needed. +//"dm4" is ambiguous, in the case of a map beginning with a number (bah). don't use. pubsubserver_t *MSV_FindSubServerName(const char *servername) { pubsubserver_t *s; @@ -136,8 +138,11 @@ pubsubserver_t *MSV_FindSubServerName(const char *servername) forcenew = true; mapname++; } - else + else if (*mapname) + { + Con_Printf("Invalid node name (lacks colon): %s\n", servername); mapname = ""; + } if (id) { @@ -216,17 +221,9 @@ void MSV_MapCluster_f(void) //child processes return 0 and fall through memset(&sv, 0, sizeof(sv)); - if (Cmd_Argc() > 1) + if (atoi(Cmd_Argv(1))) { - char *dbname = Cmd_Argv(1); - char *sqlparams[] = - { - "", //host - "", //username - "", //password - dbname, //db - }; - Con_Printf("Opening database \"%s\"\n", dbname); + Con_Printf("Opening database \"%s\"\n", sqlparams[3]); sv.logindatabase = SQL_NewServer("sqlite", sqlparams); if (sv.logindatabase == -1) { @@ -777,9 +774,22 @@ void SSV_ReadFromControlServer(void) for (i = 0; i < sv.allocated_client_slots; i++) { cl = &svs.clients[i]; + if (cl->state >= ca_connected) if (!*dest || !strcmp(dest, cl->name)) { - SV_PrintToClient(cl, PRINT_HIGH, va("%s from [%s]: %s\n", cmd, from, info)); + if (!strcmp(cmd, "say")) + SV_PrintToClient(cl, PRINT_HIGH, va("^[%s^]: %s\n", from, info)); + else if (!strcmp(cmd, "join")) + { + SV_PrintToClient(cl, PRINT_HIGH, va("^[%s^] is joining you\n", from)); + SSV_Send(from, cl->name, "joinnode", va("%i", svs.clusterserverid)); + } + else if (!strcmp(cmd, "joinnode")) + { + SSV_InitiatePlayerTransfer(cl, info); + } + else + SV_PrintToClient(cl, PRINT_HIGH, va("%s from [%s]: %s\n", cmd, from, info)); if (*dest) break; } @@ -815,7 +825,7 @@ void SSV_UpdateAddresses(void) { if (addr[i].type == NA_IP) { - NET_StringToAdr(sv_serverip.string, BigShort(addr[i].port), &addr[0]); + NET_StringToAdr2(sv_serverip.string, BigShort(addr[i].port), &addr[0], sizeof(addr)/sizeof(addr[0])); count = 1; break; } diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index e5a8885b4..6a7d473a9 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -295,7 +295,7 @@ void VARGS SV_Error (char *error, ...) int size = 1024*1024*8; char *buffer = BZ_Malloc(size); svprogfuncs->save_ents(svprogfuncs, buffer, &size, size, 3); - COM_WriteFile("ssqccore.txt", buffer, size); + COM_WriteFile("ssqccore.txt", FS_GAMEONLY, buffer, size); BZ_Free(buffer); } } diff --git a/engine/server/sv_sys_win.c b/engine/server/sv_sys_win.c index b9a80d03e..743c2a6ad 100644 --- a/engine/server/sv_sys_win.c +++ b/engine/server/sv_sys_win.c @@ -1053,17 +1053,41 @@ void ApplyColour(unsigned int chr) } } -void Sys_PrintColouredChar(unsigned int chr) +//this could be much more efficient. +static void Sys_PrintColouredChars(conchar_t *start, conchar_t *end) { + conchar_t m; + wchar_t wc[256]; + int l; DWORD dummy; - wchar_t wc; - if (chr & CON_HIDDEN) - return; - ApplyColour(chr); + while(start < end) + { + l = 0; + m = *start & CON_FLAGSMASK; + for (;;) + { + if (start == end || m != (*start & CON_FLAGSMASK) || l >= countof(wc)) + { + ApplyColour(m); + if (WinNT) + WriteConsoleW(hconsoleout, wc, l, &dummy, NULL); + else + { + //win95 doesn't support wide chars *sigh*. blank consoles suck. + char ac[256]; + l = WideCharToMultiByte(CP_ACP, 0, wc, l, ac, sizeof(ac), NULL, NULL); + WriteConsole(hconsoleout, ac, l, &dummy, NULL); + } + break; + } + if (!(*start & CON_HIDDEN)) + wc[l++] = *start & CON_CHARMASK; + start++; + } + } - wc = chr & CON_CHARMASK; - WriteConsoleW(hconsoleout, &wc, 1, &dummy, NULL); + ApplyColour(CON_WHITEMASK); } /* @@ -1112,13 +1136,9 @@ void Sys_Printf (char *fmt, ...) if (sys_colorconsole.value && hconsoleout) { - conchar_t out[MAXPRINTMSG], *c, *end; + conchar_t out[MAXPRINTMSG], *end; end = COM_ParseFunString(CON_WHITEMASK, msg, out, sizeof(out), false); - - for (c = out; c < end; c++) - Sys_PrintColouredChar (*c); - - ApplyColour(CON_WHITEMASK); + Sys_PrintColouredChars (out, end); } else { diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index e69edb477..1eb56cabc 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -4346,7 +4346,7 @@ void Cmd_Fly_f (void) } #ifdef SUBSERVERS -void Cmd_Transfer_f(void) +void Cmd_SSV_Transfer_f(void) { char *dest = Cmd_Argv(1); if (!SV_MayCheat()) @@ -4357,6 +4357,46 @@ void Cmd_Transfer_f(void) SSV_InitiatePlayerTransfer(host_client, dest); } + +void Cmd_SSV_AllSay_f(void) +{ + char *text = Cmd_Args(); + if (!SV_MayCheat()) + { + SV_TPrintToClient(host_client, PRINT_HIGH, "Cheats are not allowed on this server\n"); + return; + } + + SSV_Send("", host_client->name, "say", text); +} + +void Cmd_SSV_Join_f(void) +{ + int i; + char *who = Cmd_Argv(1); + if (!SV_MayCheat()) + { + SV_TPrintToClient(host_client, PRINT_HIGH, "Cheats are not allowed on this server\n"); + return; + } + + for (i = 0; i < sv.allocated_client_slots; i++) + { + if (!strcmp(who, svs.clients[i].name)) + { +// VectorCopy(svs.clients[i].edict->v->origin, sv_player->v->oldorigin); + VectorCopy(svs.clients[i].edict->v->origin, sv_player->v->origin); + World_LinkEdict (&sv.world, (wedict_t*)sv_player, false); + + sv_player->xv->dimension_hit = (int)sv_player->xv->dimension_hit & ~128; + sv_player->xv->dimension_solid = (int)sv_player->xv->dimension_solid & 128; + svs.clients[i].edict->xv->dimension_hit = (int)svs.clients[i].edict->xv->dimension_hit & ~128; + svs.clients[i].edict->xv->dimension_solid = (int)svs.clients[i].edict->xv->dimension_solid & 128; + return; + } + } + SSV_Send(who, host_client->name, "join", ""); +} #endif /* @@ -5272,7 +5312,9 @@ ucmd_t ucmds[] = {"notarget", Cmd_Notarget_f}, {"setpos", Cmd_SetPos_f}, #ifdef SUBSERVERS - {"transfer", Cmd_Transfer_f}, //transfer the player to a different map/server + {"ssvtransfer", Cmd_SSV_Transfer_f},//transfer the player to a different map/server + {"ssvsay", Cmd_SSV_AllSay_f}, //transfer the player to a different map/server + {"ssvjoin", Cmd_SSV_Join_f}, //transfer the player to a different map/server #endif #ifdef NQPROT diff --git a/plugins/Makefile b/plugins/Makefile index 9260eccf7..b47ffa772 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -9,7 +9,7 @@ PLUG_NATIVE_EXT=_amd.dll PLUG_LDFLAGS=-Lavplug/lib64 -L../engine/libs/mingw64-libs -lz -Wl,--support-old-code endif -PLUG_LDFLAGS?=-L/usr/local/lib -Wl,-R/usr/local/lib -lz +PLUG_LDFLAGS?=-L/usr/local/lib -Wl,-R/usr/local/lib -lz -lm ifneq ($(PLUG_NATIVE_EXT),) #if we're on windows, we'll put our windows-specific hacks here. @@ -129,7 +129,7 @@ native: $(OUT_DIR)/fteplug_irc$(PLUG_NATIVE_EXT) #for compat with ezquake $(OUT_DIR)/fteplug_ezhud$(PLUG_NATIVE_EXT): ezhud/ezquakeisms.c ezhud/hud.c ezhud/hud_common.c ezhud/hud_editor.c plugin.c qvm_api.c - $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $(OUT_DIR)/fteplug_ezhud$(PLUG_NATIVE_EXT) -shared $(PLUG_CFLAGS) -Iirc $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) + $(CC) $(BASE_CFLAGS) $(CFLAGS) -DFTEPLUGIN -o $(OUT_DIR)/fteplug_ezhud$(PLUG_NATIVE_EXT) -shared $(PLUG_CFLAGS) -Iezhud $^ $(PLUG_DEFFILE) $(PLUG_LDFLAGS) native: $(OUT_DIR)/fteplug_ezhud$(PLUG_NATIVE_EXT) native: diff --git a/plugins/jabber/jabberclient.c b/plugins/jabber/jabberclient.c index 2f74a84ee..db20e238b 100644 --- a/plugins/jabber/jabberclient.c +++ b/plugins/jabber/jabberclient.c @@ -60,6 +60,12 @@ client compat: #include #include "xmpp.h" +#ifdef DEFAULTDOMAIN + #define EXAMPLEDOMAIN DEFAULTDOMAIN //used in examples / default text field (but not otherwise assumed when omitted) +#else + #define EXAMPLEDOMAIN "gmail.com" //used in examples +#endif + #ifdef JINGLE icefuncs_t *piceapi; @@ -1578,7 +1584,7 @@ qintptr_t JCL_ConsoleLink(qintptr_t *args) else if (!strcmp(what, "newaccount")) { pCon_SetConsoleFloat(BUDDYLISTTITLE, "linebuffered", true); - pCon_SetConsoleString(BUDDYLISTTITLE, "footer", "Please enter your account name"); + pCon_SetConsoleString(BUDDYLISTTITLE, "footer", "Please enter your XMPP account name\neg: example@"EXAMPLEDOMAIN); jclient_action_cl = jcl; jclient_action_buddy = NULL; jclient_action = ACT_NEWACCOUNT; @@ -1746,7 +1752,7 @@ qintptr_t JCL_ConExecuteCommand(qintptr_t *args) case ACT_NONE: break; case ACT_NEWACCOUNT: - if (*args) + if (!*args) break; //they didn't enter anything! oh well. for (i = 0; i < sizeof(jclients)/sizeof(jclients[0]); i++) { @@ -2208,14 +2214,13 @@ jclient_t *JCL_Connect(int accnum, char *server, int forcetls, char *account, ch } else { +#ifdef DEFAULTDOMAIN domain = DEFAULTDOMAIN; - if (domain && *domain) - Con_Printf("XMPP: domain not specified, assuming %s\n", domain); - else - { - Con_Printf("XMPP: domain not specified\n"); - return NULL; - } + Con_Printf("XMPP: domain not specified, assuming %s\n", domain); +#else + Con_Printf("XMPP: domain not specified\n"); + return NULL; +#endif } x = XML_CreateNode(NULL, "account", "", ""); @@ -5401,7 +5406,7 @@ void XMPP_Menu_Connect(void) y = 36; pCmd_AddText(va("menutext 48 %i \"^sXMPP Sign In\"\n", y), false); y+=16; pCmd_AddText(va("menueditpriv 48 %i \"Username\" \"example\"\n", y), false);y+=16; - pCmd_AddText(va("menueditpriv 48 %i \"Domain\" \"gmail.com\"\n", y), false);y+=16; + pCmd_AddText(va("menueditpriv 48 %i \"Domain\" \""EXAMPLEDOMAIN"\"\n", y), false);y+=16; pCmd_AddText(va("menueditpriv 48 %i \"Resource\" \"\"\n", y), false);y+=32; pCmd_AddText(va("menutext 48 %i \"Sign In\" SignIn\n", y), false); pCmd_AddText(va("menutext 256 %i \"Cancel\" cancel\n", y), false); diff --git a/plugins/jabber/xmpp.h b/plugins/jabber/xmpp.h index 8985169bc..17fc833e7 100644 --- a/plugins/jabber/xmpp.h +++ b/plugins/jabber/xmpp.h @@ -22,7 +22,7 @@ #define JCL_BUILD "4" -#define DEFAULTDOMAIN "" +//#define DEFAULTDOMAIN "triptohell.info" #define DEFAULTRESOURCE "Quake" #define QUAKEMEDIAXMLNS "http://fteqw.com/protocol/quake" #define DISCONODE "http://fteqw.com/ftexmpp" //some sort of client identifier diff --git a/specs/mapcluster.txt b/specs/mapcluster.txt new file mode 100644 index 000000000..00f6115af --- /dev/null +++ b/specs/mapcluster.txt @@ -0,0 +1,79 @@ +WARNING: this mode is not currently compiled in by default, in order to avoid potential unexpected resource usage. +you will need to enable it by enabling the SUBSERVERS preprocessor define at compile time. + + + + + +random useful commands (bash or console): +make sv-rel CFLAGS=-DSUBSERVERS +mapcluster +ssv 1 quit +ssv * say The Admin Is Awesome +sv_serverip 127.0.0.1 + +the mapcluster command provides support for a single host running multiple maps as a single server cluster. +this is achieved by running multiple instances of the server in multiple processes, each with their own port numbers. +the initial server acts as a login server, which communicates with the various nodes. +ssqc is able to transfer clients from one node to another within the cluster, or send messages to the node hosting clients (or, if the node's id is explicitly known, can be used to directly communicate with other nodes). + + + +node naming rules: +a node name takes the form of id:name +if the id is empty, then it is considered unspecified, and the message/transfer will id used will be updated to any existing node with the same name (:e1m1 will pick ANY e1m1). +if the id is 0, then it will NEVER use an existing node. +if the id is invalid/0/empty, and the name is not blank, and the node is named via a transfer request, a new node will be started using that name as the new node's initial map. +note that "0:start$\\foo\\bar" can be used, in which case you will ALWAYS get a new instance, using maps/start.bsp and that infoget(startspot, "foo") will return "bar". + +event destinations: +event destinations logically use player names. However, if the name takes the form "\\5" for example, then the message will be sent to the node with id 5, rather than to the node of a name player. You can use this to directly communicate server-to-server. If the node id does not exist, the event will be returned to the sender as an error - no new node will be created with this (fixme: it probably should be, but blurgh). + + +ssqc builtins: +forceinfokey(player, "*transfer", ":e1m1"); + initiates a transfer request. the infokey will be cleared out upon failure, and the client will drop on success. + their parms will be transfered immediately, and will be available to the new server once the transfer completes. + WARNING: it is the QC's responsibility to terminate servers {localcmd("quit\n");} - the engine will not automatically do this. + If you are implementing an mmo-like game, you would presumably do this when there are no players and all monsters were respawned (such that restarting will not be noticed). Otherwise you may choose to terminate the node instantly or after some delay after the last player has left. If you only have limited maps, you might opt to never terminate any nodes - lucky you. + +void(string dest, string from, string cmd, string info) clusterevent = #; + sends a message to the destination. + if dest is blank, the event will be broadcast to EVERY node. + if the destination is prefixed with a \ char, (eg: "\\1"), then the message is sent to node id 1. + if the destination is a player name, will send to whichever node that player is currently on. the receiving server is expected to handle the event on behalf of the player (perhaps by forwarding to csqc, or just doing a sprint or something). + if the destination appears invalid (and cmd is not prefixed with 'error:'), the cmd will be prefixed with error:, the dest+from args swapped, and the event will be sent back to the receiver. + info and cmd have meaning to the engine only if the receiver's ssqc does not handle the event. + +float serverid + this float contains the node's unique id. no two active nodes will use the same id, and can be used in replies to identify the node as the receipient of a transfer. + +float(string dest, string from, string cmd, string info) SV_ParseClusterEvent = {}; + this function is called by the engine in response to a clusterevent call. the arguments match those of the prior call. + if you return false, the engine will attempt to handle it the best it can. which is limited and probably not ideal. any other result will cause the node to carry out no further action (meaning the from+cmd+info args are completely free-form if you do so). + the dest argument follows the restrictions/expectations specified for clusterevent. + this is handled by the receipient server, and as such the qc is expected to find the named player (if not direct-to-server) and handle the event itself. + the senders and receivers of these messages are all part of the same cluster, and so should all be considered trustworthy - unless you permit direct message injection. for this reason, you should *probably* ALWAYS use constants for the cmd argument, and thereby avoid surprises. this allows you to make assumptions about the dest+from+info fields more easily, simplifying your parsing. + it is possible for a client to have transfered off the server (or be in the process of transfering and thus ownership of the player's properties uncertain). in such cases, you are recommended to mimic the error: behaviour so that the sender can handle things in a consistent way. + + +debug/cheat commands (mods are expected to provide their own functionality for these things, and thus these are considered cheats to limit their use): +cmd ssvtransfer :e1m1 + transfers the player to the named node (see node naming rules), just like forceinfokey would. + +cmd ssvsay hello world + broadcasts the message to every single player on every single node (instead of merely the node the player is on). + (this does: clusterevent("", self.netname, "say", args()) ) + +cmd ssvjoin playername + attempts to teleport the player to the named player's server. + if the named player is already on the same node, teleports the player on top of the named player. + (this does: clusterevent(player, self.netname, "join", ""), the default join handler will reply with joinnode, and the handler of that will initiate a transfer request to the node specified by the joinnode's info string) + + +gotchas: +in order to invite another player, you will likely need to implement some sort of timeout serverside, in order to avoid issues with people's UIs ignoring the request (or not receiving it due to a player transfering at the time). +you probably shouldn't allow people to join others without mutual consent, to reduce trolling. probably you'll want the server of the person being joined in ultimate control of the join, if only because its nice and easy to pull another player to your node on account of the puller already knowing the destination's serverid. + +due to players disconnecting or randomly transfering before your messages get to them, all of these messages should be considered unreliable. +if you block transfers of one player, you can hopefully get away with a race condition between the reply and a timeout. for non-parm based things, you can use sql transaction support to ensure atomicity. \ No newline at end of file