From fa0c73d33bc984b45bd79b1da4b2f35faecb6ba9 Mon Sep 17 00:00:00 2001 From: Spoike Date: Sat, 16 Feb 2019 19:09:07 +0000 Subject: [PATCH] Fixed crash from too many csqc entities (reported by shpuld) Added .psd, .pbm/.pgm/.ppm, .pfm, and .hdr image formats. Extensions NOT added to r_imageextensions. png (and the above formats) can now be loaded as RGBA16, instead of being truncated to RGBA8 (8bit pngs not affected). r_imagelist will now show images from memory, instead of potentially loading new/different ones from disk. Fix serverbrowser bug being too eager to join the server (eg from alt+tab). Don't send ipv6 packets to qw/q2 masters. They won't be able to report ipv6 addresses anyway, and this reduces warnings when a host STILL has no ipv6 (my ISP sucks). this does not affect q3/dpmasters, for people without ipv4 addresses. Tried to improve compat with Bloodshot's particle effects. Fixed a couple of issues with R_AddTrisoup. Fixed string tokenizing bug where it was using the wrong buffer size values. Don't show link-local/localhost addresses in eg the status command (unless developer). qtv-rel is now an easier target, for new qtv releases. qtv warning fixes. added a nailtrail effect to 'high' particles. fixed terrain shaders. fixed fogged water issue (on one of bal's maps). first attempt at gltf2 format support git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5400 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- CMakeLists.txt | 54 +- engine/Makefile | 52 +- engine/client/cl_ents.c | 4 +- engine/client/cl_parse.c | 2 +- engine/client/client.h | 1 + engine/client/console.c | 9 + engine/client/image.c | 1039 ++++++++++--- engine/client/m_download.c | 29 +- engine/client/m_master.c | 2 +- engine/client/m_mp3.c | 2 +- engine/client/m_options.c | 114 +- engine/client/menu.c | 2 - engine/client/merged.h | 2 +- engine/client/net_master.c | 51 +- engine/client/p_script.c | 128 +- engine/client/pr_csqc.c | 14 +- engine/client/r_partset.c | 16 + engine/client/render.h | 5 +- engine/client/renderer.c | 4 +- engine/client/screen.h | 14 +- engine/client/sys_win.c | 6 +- engine/client/view.c | 7 + engine/common/cmd.c | 25 +- engine/common/com_mesh.c | 160 +- engine/common/com_mesh.h | 3 + engine/common/common.c | 4 +- engine/common/common.h | 2 + engine/common/config_fteqw.h | 3 + engine/common/fs.c | 10 +- engine/common/net.h | 5 +- engine/common/net_ssl_winsspi.c | 4 + engine/common/net_wins.c | 277 ++-- engine/common/plugin.c | 4 +- engine/common/pmove.h | 2 +- engine/common/qvm.c | 10 +- engine/d3d/d3d11_image.c | 8 + engine/droid/AndroidManifest.xml | 3 + engine/gl/gl_alias.c | 2 +- engine/gl/gl_draw.c | 4 + engine/gl/gl_heightmap.c | 3 +- engine/gl/gl_model.c | 8 + engine/gl/gl_rlight.c | 2 +- engine/gl/gl_rmain.c | 5 +- engine/gl/gl_shader.c | 2 +- engine/gl/gl_vidlinuxglx.c | 55 +- engine/gl/glsupp.h | 1 + engine/gl/r_bishaders.h | 41 +- engine/http/ftpserver.c | 18 +- engine/partcfgs/high.cfg | 16 + engine/qclib/execloop.h | 2 +- engine/qclib/qcc_pr_comp.c | 32 +- engine/qclib/qccgui.c | 2 +- engine/qclib/qccmain.c | 2 +- engine/server/pr_q1qvm.c | 24 +- engine/server/savegame.c | 2 +- engine/server/sv_ents.c | 18 +- engine/server/sv_sys_unix.c | 8 + engine/shaders/glsl/defaultskin.glsl | 22 +- engine/shaders/glsl/defaultwarp.glsl | 2 +- engine/shaders/glsl/rtlight.glsl | 4 +- engine/shaders/glsl/terrain.glsl | 13 +- engine/vk/vk_init.c | 5 + engine/web/ftejslib.js | 1 - fteqtv/cmd.h | 4 +- fteqtv/control.c | 25 +- fteqtv/forward.c | 4 +- fteqtv/httpsv.c | 4 +- fteqtv/menu.c | 2 +- fteqtv/netchan.c | 2 +- fteqtv/parse.c | 6 +- fteqtv/qtv.h | 61 +- fteqtv/qw.c | 22 +- fteqtv/rcon.c | 10 +- fteqtv/source.c | 290 ++-- plugins/jabber/jabberclient.c | 12 +- plugins/models/gltf.c | 2160 ++++++++++++++++++++++++++ plugins/models/models.c | 843 ++++++++++ plugins/plugin.h | 14 +- plugins/xsv/m_x.c | 298 +++- plugins/xsv/qux.h | 5 + plugins/xsv/x_reqs.c | 604 +++++-- plugins/xsv/x_res.c | 123 +- 82 files changed, 5801 insertions(+), 1059 deletions(-) create mode 100644 plugins/models/gltf.c create mode 100644 plugins/models/models.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 1778411b4..864f768da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,7 +133,7 @@ IF(CMAKE_C_COMPILER_ID MATCHES "GNU") #might as well do this, public builds use the regular Makefile. set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native") IF(CMAKE_BUILD_TYPE MATCHES "Debug") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wno-pointer-sign -Wno-unknown-pragmas -Wno-format-zero-length -Wno-strict-aliasing") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wcast-align -Werror -Wno-pointer-sign -Wno-unknown-pragmas -Wno-format-zero-length -Wno-strict-aliasing -Wno-error=cpp") ELSE() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") ENDIF() @@ -740,6 +740,31 @@ ELSE() ) SET_TARGET_PROPERTIES(iqmtool PROPERTIES COMPILE_DEFINITIONS "${FTE_REVISON}") + ADD_EXECUTABLE(qtv + fteqtv/netchan.c + fteqtv/parse.c + fteqtv/msg.c + fteqtv/qw.c + fteqtv/source.c + fteqtv/bsp.c + fteqtv/rcon.c + fteqtv/mdfour.c + fteqtv/crc.c + fteqtv/control.c + fteqtv/forward.c + fteqtv/pmove.c + fteqtv/menu.c + fteqtv/httpsv.c + fteqtv/libqtvc/glibc_sucks.c + engine/common/sha1.c + ) + SET_TARGET_PROPERTIES(qtv PROPERTIES COMPILE_DEFINITIONS "${FTE_REVISON};stricmp=strcasecmp;strnicmp=strncasecmp") + IF(WIN32) + TARGET_LINK_LIBRARIES(qtv ${ZLIB_LIBRARIES} ws2_32 winmm) + ELSE() + TARGET_LINK_LIBRARIES(qtv m) + ENDIF() + IF(NOT WIN32) ADD_EXECUTABLE(ftemaster ${FTESV_ARCH_FILES} @@ -785,7 +810,6 @@ ELSE() SET_TARGET_PROPERTIES(fteqcc PROPERTIES COMPILE_DEFINITIONS "${FTE_LIB_DEFINES};${FTE_REVISON}") TARGET_LINK_LIBRARIES(fteqcc ${ZLIB_LIBRARIES} m) - IF(${WIN32}) ADD_EXECUTABLE(fteqccgui WIN32 engine/qclib/qccgui.c @@ -882,6 +906,32 @@ SET_TARGET_PROPERTIES(irc PROPERTIES PREFIX "fteplug_") SET_TARGET_PROPERTIES(irc PROPERTIES LINK_FLAGS "-Wl,--no-undefined") TARGET_LINK_LIBRARIES(irc m) +#model formats plugin +ADD_LIBRARY(models MODULE + plugins/qvm_api.c + plugins/plugin.c + plugins/models/models.c + plugins/models/gltf.c +) +SET_TARGET_PROPERTIES(models PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;${FTE_LIB_DEFINES}") +SET_TARGET_PROPERTIES(models PROPERTIES PREFIX "fteplug_") +SET_TARGET_PROPERTIES(models PROPERTIES LINK_FLAGS "-Wl,--no-undefined") +TARGET_LINK_LIBRARIES(models m) + +#x11 formats plugin +ADD_LIBRARY(x11 MODULE + plugins/qvm_api.c + plugins/plugin.c + plugins/xsv/m_x.c + plugins/xsv/x_reqs.c + plugins/xsv/x_res.c + engine/qclib/hash.c +) +SET_TARGET_PROPERTIES(x11 PROPERTIES COMPILE_DEFINITIONS "FTEPLUGIN;${FTE_LIB_DEFINES}") +SET_TARGET_PROPERTIES(x11 PROPERTIES PREFIX "fteplug_") +SET_TARGET_PROPERTIES(x11 PROPERTIES LINK_FLAGS "-Wl,--no-undefined") +TARGET_LINK_LIBRARIES(x11 m) + #ffmpeg client plugin. no proper way to detect dependancies right now, so I've gotta try the manual way. FIND_PATH(AVCODEC_INCLUDE_DIR libavcodec/avcodec.h) FIND_PATH(AVFORMAT_INCLUDE_DIR libavformat/avformat.h) diff --git a/engine/Makefile b/engine/Makefile index fc192b1d9..145af58fa 100644 --- a/engine/Makefile +++ b/engine/Makefile @@ -569,13 +569,14 @@ ifeq ($(FTE_TARGET),vc) WARNINGFLAGS=-W3 -D_CRT_SECURE_NO_WARNINGS GNUC_FUNCS= else - WARNINGFLAGS=-Wall -Wno-pointer-sign -Wno-unknown-pragmas -Wno-format-zero-length -Wno-strict-aliasing + WARNINGFLAGS=-Wall -Wno-pointer-sign -Wno-unknown-pragmas -Wno-format-zero-length -Wno-strict-aliasing -Wcast-align GNUC_FUNCS= -Dstrnicmp=strncasecmp -Dstricmp=strcasecmp endif SDL_INCLUDES= #-I$(LIBS_DIR)/sdl/include -I/usr/include/SDL -I$(LIBS_DIR)/sdl/include/SDL -BASE_CFLAGS=$(WARNINGFLAGS) $(GNUC_FUNCS) -I$(CLIENT_DIR) -I$(SERVER_DIR) -I$(COMMON_DIR) -I$(GL_DIR) -I$(D3D_DIR) -I$(PROGS_DIR) -I. -I$(LIBS_DIR) -I$(LIBS_DIR)/dxsdk9/include -I$(LIBS_DIR)/dxsdk7/include $(SDL_INCLUDES) -I./libs/freetype2/include -I./libs/freetype2/include/freetype $(BOTLIB_CFLAGS) $(SVNREVISION) +BASE_INCLUDES=-I$(CLIENT_DIR) -I$(SERVER_DIR) -I$(COMMON_DIR) -I$(GL_DIR) -I$(D3D_DIR) -I$(PROGS_DIR) -I. -I$(LIBS_DIR) +BASE_CFLAGS=$(WARNINGFLAGS) $(GNUC_FUNCS) $(BASE_INCLUDES) -I$(LIBS_DIR)/dxsdk9/include -I$(LIBS_DIR)/dxsdk7/include $(SDL_INCLUDES) -I./libs/freetype2/include -I./libs/freetype2/include/freetype $(BOTLIB_CFLAGS) $(SVNREVISION) CLIENT_ONLY_CFLAGS=-DCLIENTONLY SERVER_ONLY_CFLAGS=-DSERVERONLY JOINT_CFLAGS= @@ -1263,6 +1264,8 @@ ifeq (win,$(findstring win,$(FTE_TARGET))$(findstring _SDL,$(FTE_TARGET))) BASELDFLAGS+=-lcomctl32 EXEPOSTFIX=.exe + QTV_LDFLAGS=-lws2_32 -lwinmm + LIBS_DIR = $(BASE_DIR)/libs SV_EXE_NAME=../$(EXE_NAME)sv$(BITS)$(EXEPOSTFIX) SV_LDFLAGS=$(MINGW_LIBS_DIR)/libz.a -lws2_32 -lwinmm @@ -2233,21 +2236,46 @@ libs-$(ARCH)/libBulletDynamics.a: makelibs: libs-$(ARCH)/libjpeg.a libs-$(ARCH)/libz.a libs-$(ARCH)/libpng.a libs-$(ARCH)/libogg.a libs-$(ARCH)/libvorbis.a libs-$(ARCH)/libopus.a libs-$(ARCH)/libspeex.a libs-$(ARCH)/libspeexdsp.a libs-$(ARCH)/libfreetype.a $(MAKELIBS) HTTP_OBJECTS=http/httpserver.c http/iwebiface.c common/fs_stdio.c http/ftpserver.c -$(RELEASE_DIR)/httpserver$(BITS): $(HTTP_OBJECTS) - $(CC) -o $(RELEASE_DIR)/httpserver$(BITS) -Icommon -Iclient -Iqclib -Igl -Iserver -DWEBSERVER -DWEBSVONLY -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp -DNO_PNG $(HTTP_OBJECTS) -httpserver: $(RELEASE_DIR)/httpserver$(BITS) +$(RELEASE_DIR)/httpserver$(BITS)$(EXEPOSTFIX): $(HTTP_OBJECTS) + $(CC) -o $@ -Icommon -Iclient -Iqclib -Igl -Iserver -DWEBSERVER -DWEBSVONLY -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp -DNO_PNG $(HTTP_OBJECTS) +httpserver: $(RELEASE_DIR)/httpserver$(BITS)$(EXEPOSTFIX) IQM_OBJECTS=../iqm/iqm.cpp -$(RELEASE_DIR)/iqm$(BITS): $(IQM_OBJECTS) - $(CC) -o $(RELEASE_DIR)/iqm$(BITS) $(IQM_OBJECTS) -lstdc++ -lm -iqm: $(RELEASE_DIR)/iqm$(BITS) +$(RELEASE_DIR)/iqm$(BITS)$(EXEPOSTFIX): $(IQM_OBJECTS) + $(CC) -o $@ $(IQM_OBJECTS) -lstdc++ -lm +iqm-rel: $(RELEASE_DIR)/iqm$(BITS)$(EXEPOSTFIX) +iqm: iqm-rel MASTER_OBJECTS=server/sv_sys_unix.c common/sys_linux_threads.c common/net_ssl_gnutls.c server/sv_master.c common/net_wins.c common/cvar.c common/cmd.c common/sha1.c http/httpclient.c common/log.c common/fs.c common/fs_stdio.c common/common.c common/translate.c common/zone.c qclib/hash.c -$(RELEASE_DIR)/ftemaster$(BITS): $(MASTER_OBJECTS) - $(CC) -o $(RELEASE_DIR)/master$(BITS) $(MASTER_OBJECTS) -Icommon -Iclient -Iqclib -Igl -Iserver -DMASTERONLY -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp -lm -ldl -master: $(RELEASE_DIR)/ftemaster$(BITS) +$(RELEASE_DIR)/ftemaster$(BITS)$(EXEPOSTFIX): $(MASTER_OBJECTS) + $(CC) -o $@ $(MASTER_OBJECTS) -Icommon -Iclient -Iqclib -Igl -Iserver -DMASTERONLY -Dstricmp=strcasecmp -Dstrnicmp=strncasecmp -lm -ldl +master-rel: $(RELEASE_DIR)/ftemaster$(BITS)$(EXEPOSTFIX) +master: master-rel -utils: httpserver iqm master +QTV_OBJECTS= \ + netchan.c \ + parse.c \ + msg.c \ + qw.c \ + source.c \ + bsp.c \ + rcon.c \ + mdfour.c \ + crc.c \ + control.c \ + forward.c \ + pmove.c \ + menu.c \ + httpsv.c \ + sha1.c \ + libqtvc/glibc_sucks.c +$(RELEASE_DIR)/qtv$(BITS)$(EXEPOSTFIX): $(QTV_OBJECTS) + $(CC) -o $@ $? -lstdc++ -lm $(BASE_INCLUDES) $(QTV_LDFLAGS) +qtv-rel: + $(MAKE) $(RELEASE_DIR)/qtv$(BITS)$(EXEPOSTFIX) VPATH="$(BASE_DIR)/../fteqtv:$(VPATH)" +qtv: qtv-rel + +utils: httpserver iqm master qtv-rel prefix ?= /usr/local exec_prefix ?= $(prefix) diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index 530b14c32..6785b221f 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -3894,7 +3894,9 @@ void CL_LinkPacketEntities (void) int trailef, trailidx; int modelflags; struct itemtimer_s *timer, **timerlink; - float timestep = host_frametime; + float timestep = cl.time-cl.lastlinktime; + cl.lastlinktime = cl.time; + timestep = bound(0, timestep, 0.1); pack = cl.currentpackentities; if (!pack) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 93610a750..ad9ac1e7f 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -8304,7 +8304,7 @@ struct sortedsvcs_s const char *name; size_t bytes; }; -static QDECL int sorttraffic(const void *l, const void *r) +static int QDECL sorttraffic(const void *l, const void *r) { const struct sortedsvcs_s *a=l, *b=r; diff --git a/engine/client/client.h b/engine/client/client.h index 048c3f304..0eb83d759 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -809,6 +809,7 @@ typedef struct double time; // this is the time value that the client // is rendering at. always <= realtime double lasttime; //cl.time from last frame. + double lastlinktime; //cl.time from last frame. float servertime; //current server time, bound between gametime and gametimemark float mtime; //server time as on the server when we last received a packet. not allowed to decrease. diff --git a/engine/client/console.c b/engine/client/console.c index b88257b1c..f4a725c49 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -2862,6 +2862,15 @@ void Con_DrawConsole (int lines, qboolean noback) if ((buf = FS_LoadMallocFile (key, &fsize))) Image_LoadTextureFromMemory(shader->defaulttextures->base, shader->defaulttextures->base->flags|IF_NOWORKER, key, key, buf, fsize); } + } + key = Info_ValueForKey(info, "tipimgptr"); + if (*key) + { + shader = R2D_SafeCachePic("tiprawimg"); + shader->defaulttextures->base = Image_TextureIsValid(strtoull(key, NULL, 0)); + } + if (shader && shader->defaulttextures->base) + { shader->width = shader->defaulttextures->base->width; shader->height = shader->defaulttextures->base->height; if (shader->width > 320) diff --git a/engine/client/image.c b/engine/client/image.c index 823585498..98bd2d33f 100644 --- a/engine/client/image.c +++ b/engine/client/image.c @@ -37,6 +37,19 @@ char *r_defaultimageextensions = " jpg" //q3 uses some jpegs, for some reason //" jpeg" //thankfuly the quake community stuck to .jpg instead #endif +#ifdef IMAGEFMT_PBM + //" pfm" //float version (technically seperate, but similarish) + //" pbm" //1-bit v grey + //" pgm" //greyscale + //" ppm" //rgb values + //" pam" //'arbitrary' version +#endif +#ifdef IMAGEFMT_PSD + //" psd" //paintshop images (8bit+16bit, but base layer only) +#endif +#ifdef IMAGEFMT_HDR + //" hdr" //some file that uses RGBE formatted data, for hdr images. +#endif #ifdef IMAGEFMT_PKM //" pkm" //compressed format, but lacks mipmaps which makes it terrible to use. #endif @@ -935,6 +948,7 @@ static png_byte (PNGAPI *qpng_get_channels) PNGARG((png_const_structp png_ptr, p #endif static void (PNGAPI *qpng_read_update_info) PNGARG((png_structp png_ptr, png_infop info_ptr)) PSTATIC(png_read_update_info); static void (PNGAPI *qpng_set_strip_16) PNGARG((png_structp png_ptr)) PSTATIC(png_set_strip_16); +static void (PNGAPI *qpng_set_swap) PNGARG((png_structp png_ptr)) PSTATIC(png_set_swap); static void (PNGAPI *qpng_set_expand) PNGARG((png_structp png_ptr)) PSTATIC(png_set_expand); static void (PNGAPI *qpng_set_gray_to_rgb) PNGARG((png_structp png_ptr)) PSTATIC(png_set_gray_to_rgb); static void (PNGAPI *qpng_set_tRNS_to_alpha) PNGARG((png_structp png_ptr)) PSTATIC(png_set_tRNS_to_alpha); @@ -987,6 +1001,7 @@ qboolean LibPNG_Init(void) {(void **) &qpng_get_rowbytes, "png_get_rowbytes"}, {(void **) &qpng_read_update_info, "png_read_update_info"}, {(void **) &qpng_set_strip_16, "png_set_strip_16"}, + {(void **) &qpng_set_swap, "png_set_swap"}, {(void **) &qpng_set_expand, "png_set_expand"}, {(void **) &qpng_set_gray_to_rgb, "png_set_gray_to_rgb"}, {(void **) &qpng_set_tRNS_to_alpha, "png_set_tRNS_to_alpha"}, @@ -1105,12 +1120,12 @@ static void VARGS png_onwarning(png_structp png_ptr, png_const_charp warning_msg #endif } -qbyte *ReadPNGFile(qbyte *buf, int length, int *width, int *height, const char *fname) +qbyte *ReadPNGFile(const char *fname, qbyte *buf, int length, int *width, int *height, uploadfmt_t *format) { qbyte header[8], **rowpointers = NULL, *data = NULL; png_structp png; png_infop pnginfo; - int y, bitdepth, colortype, interlace, compression, filter, bytesperpixel; + int y, bitdepth, colortype, interlace, compression, filter, channels; unsigned long rowbytes; pngreadinfo_t ri; png_uint_32 pngwidth, pngheight; @@ -1164,7 +1179,7 @@ error: if (colortype == PNG_COLOR_TYPE_PALETTE) { qpng_set_palette_to_rgb(png); - qpng_set_filler(png, 255, PNG_FILLER_AFTER); + qpng_set_filler(png, ~0u, PNG_FILLER_AFTER); } if (colortype == PNG_COLOR_TYPE_GRAY && bitdepth < 8) @@ -1180,26 +1195,34 @@ error: qpng_set_tRNS_to_alpha(png); if (bitdepth >= 8 && colortype == PNG_COLOR_TYPE_RGB) - qpng_set_filler(png, 255, PNG_FILLER_AFTER); + qpng_set_filler(png, ~0u, PNG_FILLER_AFTER); if (colortype == PNG_COLOR_TYPE_GRAY || colortype == PNG_COLOR_TYPE_GRAY_ALPHA) { qpng_set_gray_to_rgb( png ); - qpng_set_filler(png, 255, PNG_FILLER_AFTER); + qpng_set_filler(png, ~0u, PNG_FILLER_AFTER); } if (bitdepth < 8) qpng_set_expand (png); - else if (bitdepth == 16) + else if (bitdepth == 16 && !format) qpng_set_strip_16(png); - + else if (bitdepth == 16) + qpng_set_swap(png); qpng_read_update_info(png, pnginfo); rowbytes = qpng_get_rowbytes(png, pnginfo); - bytesperpixel = qpng_get_channels(png, pnginfo); + channels = qpng_get_channels(png, pnginfo); bitdepth = qpng_get_bit_depth(png, pnginfo); - if (bitdepth != 8 || bytesperpixel != 4) + if (bitdepth == 8 && channels == 4) + { + if (format) + *format = PTI_RGBA8; + } + else if (bitdepth == 16 && channels == 4) + *format = PTI_RGBA16; + else { Con_Printf ("Bad PNG color depth and/or bpp (%s)\n", fname); qpng_destroy_read_struct(&png, &pnginfo, NULL); @@ -2652,7 +2675,7 @@ qboolean WriteBMPFile(char *filename, enum fs_relative fsroot, qbyte *in, int in return true; } -static qbyte *ReadICOFile(qbyte *buf, int length, int *width, int *height, const char *fname) +static qbyte *ReadICOFile(const char *fname, qbyte *buf, int length, int *width, int *height, uploadfmt_t *fmt) { qbyte *ret; size_t imgcount = buf[4] | (buf[5]<<8); @@ -2693,7 +2716,7 @@ static qbyte *ReadICOFile(qbyte *buf, int length, int *width, int *height, const qbyte *indata = buf + (bestimg->dwOffset_low | (bestimg->dwOffset_high<<16)); size_t insize = (bestimg->dwSize_low | (bestimg->dwSize_high<<16)); #ifdef AVAIL_PNGLIB - if (insize > 4 && (indata[0] == 137 && indata[1] == 'P' && indata[2] == 'N' && indata[3] == 'G') && (ret = ReadPNGFile(indata, insize, width, height, fname))) + if (insize > 4 && (indata[0] == 137 && indata[1] == 'P' && indata[2] == 'N' && indata[3] == 'G') && (ret = ReadPNGFile(fname, indata, insize, width, height, fmt))) { TRACE(("dbg: Read32BitImageFile: icon png\n")); return ret; @@ -2702,7 +2725,8 @@ static qbyte *ReadICOFile(qbyte *buf, int length, int *width, int *height, const #endif if ((ret = ReadRawBMPFile(indata, insize, width, height, 0))) { - TRACE(("dbg: Read32BitImageFile: icon png\n")); + *fmt = PTI_RGBA8; + TRACE(("dbg: Read32BitImageFile: icon bmp\n")); return ret; } } @@ -2711,6 +2735,435 @@ static qbyte *ReadICOFile(qbyte *buf, int length, int *width, int *height, const } #endif +#ifdef IMAGEFMT_PBM +static int PBM_ParseNum(qbyte **buf, qbyte *end) +{ + qbyte token[256]; + size_t l; + while (*buf < end) + { + if (**buf <= ' ') + (*buf)++; + else if (**buf == '#') + { + while (*buf < end && **buf != '\n' && **buf != '\r') + (*buf)++; + } + else + break; + } + + for (l = 0; *buf < end && **buf > ' ';) + if (l < countof(token)) + token[l++] = *(*buf)++; + token[l] = 0; + return strtol(token, NULL, 0); +} +static qbyte *ReadPBMFile(qbyte *buf, size_t len, const char *fname, int *width, int *height, uploadfmt_t *format) +{ //this isn't expected to be fast. + qbyte *end = buf+len; + int maxval = *width = *height = 0; + qbyte *r, *bo; + unsigned short *so; + size_t l, x, y; + float m, *fo, *fi; + int c = buf[1]; + buf+=2; + switch(c) + { + case '7': //arbitrary + //WIDTH HEIGHT DEPTH MAXVAL TUPLTYPE ENDHDR + return NULL; + case '6': //raw ppm + case '3': //plain ppm + *width = PBM_ParseNum(&buf, end); + *height = PBM_ParseNum(&buf, end); + maxval = PBM_ParseNum(&buf, end); + + if (maxval > 255) + { + r = BZ_Malloc(*width**height*8); + for(y = 0; y < *height; y++) + for(x = 0, so=(unsigned short*)r+(*height-y-1)*4**width; x < *height; x++) + { + *so++ = (65535u*PBM_ParseNum(&buf, end))/maxval; + *so++ = (65535u*PBM_ParseNum(&buf, end))/maxval; + *so++ = (65535u*PBM_ParseNum(&buf, end))/maxval; + *so++ = 65535u; + } + *format = PTI_RGBA16; + } + else + { + r = BZ_Malloc(*width**height*4); + for(y = 0; y < *height; y++) + for(x = 0, bo=(qbyte*)r+(*height-y-1)*4**width; x < *height; x++) + { + *bo++ = (255u*PBM_ParseNum(&buf, end))/maxval; + *bo++ = (255u*PBM_ParseNum(&buf, end))/maxval; + *bo++ = (255u*PBM_ParseNum(&buf, end))/maxval; + *bo++ = 255u; + } + *format = PTI_RGBA8; + } + return r; + case '5': //raw pgm + case '2': //plain pgm + case '4': //raw pbm + case '1': //plain pbm + *width = PBM_ParseNum(&buf, end); + *height = PBM_ParseNum(&buf, end); + + if (c == '4' || c == '1') + maxval = 1; + else + maxval = PBM_ParseNum(&buf, end); + + l = (size_t)*width*(size_t)*height; + if (maxval > 255) + { + r = BZ_Malloc(*width**height*sizeof(*so)); + for(y = 0; y < *height; y++) + for(x = 0, so=(unsigned short*)r+(*height-y-1)**width; x < *height; x++) + *so++ = (65535u*PBM_ParseNum(&buf, end))/maxval; + *format = PTI_R16; + } + else + { + r = BZ_Malloc(*width**height*sizeof(*bo)); + for(y = 0; y < *height; y++) + for(x = 0, bo=(qbyte*)r+(*height-y-1)**width; x < *height; x++) + *bo++ = (255u*PBM_ParseNum(&buf, end))/maxval; + *format = PTI_R8; + } + return r; + + case 'F': //rgb pfm + case 'f': //grey pfm + *width = PBM_ParseNum(&buf, end); + *height = PBM_ParseNum(&buf, end); + m = PBM_ParseNum(&buf, end); + + if (*buf == '\n') + buf++; + fi = (float*)buf; + + l = (size_t)*width*(size_t)*height; + if ((qbyte*)(fi+l*((c=='F')?3:1)) != end) + return NULL; + r = BZ_Malloc(l*sizeof(float) * ((c=='F')?4:1)); + if (c == 'F') + { + if (m < 0) + { + r = BZ_Malloc(*width**height*4*sizeof(float)); + for(y = 0; y < *height; y++) + for(x = 0, fo=(float*)r+(*height-y-1)*4**width; x < *height; x++) + { + *fo++ = LittleFloat(*fi++); + *fo++ = LittleFloat(*fi++); + *fo++ = LittleFloat(*fi++); + *fo++ = 1; + } + } + else + { + r = BZ_Malloc(*width**height*4*sizeof(float)); + for(y = 0; y < *height; y++) + for(x = 0, fo=(float*)r+(*height-y-1)*4**width; x < *height; x++) + { + *fo++ = BigFloat(*fi++); + *fo++ = BigFloat(*fi++); + *fo++ = BigFloat(*fi++); + *fo++ = 1; + } + } + *format = PTI_RGBA32F; + } + else + { + r = BZ_Malloc(l*sizeof(float)); + if (m < 0) + { + r = BZ_Malloc(*width**height*sizeof(float)); + for(y = 0; y < *height; y++) + for(x = 0, fo=(float*)r+(*height-y-1)**width; x < *height; x++) + *fo++ = LittleFloat(*fi++); + } + else + { + r = BZ_Malloc(*width**height*sizeof(float)); + for(y = 0; y < *height; y++) + for(x = 0, fo=(float*)r+(*height-y-1)**width; x < *height; x++) + *fo++ = BigFloat(*fi++); + } + *format = PTI_R32F; + } + return r; //erk? + } + return NULL; +} +#endif +#ifdef IMAGEFMT_HDR //baselayer only. +//Radiance files are some weird BGRE8 hdr format that somehow managed to get reasonably well supported +static void *ReadRadianceFile(qbyte *buf, size_t len, const char *fname, int *width, int *height, uploadfmt_t *format) +{ //this isn't expected to be fast. + qbyte *end = buf+len; + size_t l, x, y, w, h; + float *r, *o, m; + qbyte rgbe[4]; + + char fmt[128]; + char line[256]; + w = h = 0; + *fmt = 0; + while (buf < end) + { + l = 0; + while(buf < end && *buf != '\n') + { + if (*buf == '\r' && buf[1] == '\n') + continue; + if (l < countof(line)-1) + line[l++] = *buf++; + } + line[l] = 0; + buf++; + if (!strncmp(line, "FORMAT=", 7)) + Q_strncpyz(fmt, line+7, sizeof(fmt)); + if (!l) + break; + } + if (strncmp(buf, "-Y ", 3)) + { + Con_Printf("%s uses unsupported orientation\n", fname); + return NULL; + } + w = strtol(buf+3, (char**)&buf, 0); + if (strncmp(buf, " +X ", 4)) + { + Con_Printf("%s uses unsupported orientation\n", fname); + return NULL; + } + h = strtol(buf+4, (char**)&buf, 0); + if (*buf == '\r') + buf++; + if (*buf++ != '\n') + return NULL; + + if (strcmp(fmt, "32-bit_rle_rgbe")) + { + Con_Printf("%s uses unsupported pixel format (%s)\n", fname, fmt); + return NULL; + } + + r = o = BZ_Malloc(sizeof(float)*4*w*h); + if (!r) + return NULL; + for (y=0; y < h; y++) + { + for (x=0; x < w; x++) + { + rgbe[0] = *buf++; + rgbe[1] = *buf++; + rgbe[2] = *buf++; + rgbe[3] = *buf++; + if (rgbe[0] == 2 && rgbe[1] == 2 && rgbe[2] < 127) + { //new rle logic + Con_Printf("%s uses unsupported (new) RLE compression\n", fname); + goto fail; + } + if (rgbe[0] == 1 && rgbe[1] == 1 && rgbe[2] == 1) + { //old rle logic + Con_Printf("%s uses unsupported (old) RLE compression\n", fname); + goto fail; + } + + m = ldexp(1,rgbe[3]-136); + *o++ = m * rgbe[0]; + *o++ = m * rgbe[1]; + *o++ = m * rgbe[2]; + *o++ = 1; + } + } + *width = w; + *height = h; + //FIXME: should probably convert to e5bgr9 or something. + *format = PTI_RGBA32F; + return r; +fail: + BZ_Free(r); + return NULL; +} +#endif + +#ifdef IMAGEFMT_PSD //baselayer only. +// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/ +struct psdctx_s +{ + qbyte *buf; + qbyte *end; +}; +static qbyte PSD_Byte(struct psdctx_s *ctx) +{ + if (ctx->buf == ctx->end) + return 0; + return *ctx->buf++; +} +static unsigned short PSD_UShort(struct psdctx_s *ctx) +{ + return (PSD_Byte(ctx)<<8)|PSD_Byte(ctx); +} +static unsigned int PSD_UInt(struct psdctx_s *ctx) +{ + return (PSD_Byte(ctx)<<24)|(PSD_Byte(ctx)<<16)|(PSD_Byte(ctx)<<8)|PSD_Byte(ctx); +} +static void *PSD_Block(struct psdctx_s *ctx, size_t sz) +{ + void *r; + if (ctx->buf+sz <= ctx->end) + { + r = ctx->buf; + ctx->buf += sz; + } + else + r = NULL; + return r; +} +static void *ReadPSDFile(qbyte *buf, size_t len, const char *fname, int *outwidth, int *outheight, uploadfmt_t *outformat) +{ + unsigned short ver, chans, depth, clrmode, cmp; + unsigned int width, height, clrsize, ressize, lyrsize; + struct psdctx_s ctx; + size_t l, c, y; + ctx.buf = buf; + ctx.end = buf+len; + + /*magic =*/ PSD_UInt(&ctx); + ver = PSD_UShort(&ctx); + if (ver != 1) + { + Con_Printf("%s unsupported .psd version\n", fname); + return NULL; + } + /*reserved*/PSD_Block(&ctx, 6); + chans = PSD_UShort(&ctx); + width = PSD_UInt(&ctx); + height = PSD_UInt(&ctx); + depth = PSD_UShort(&ctx); + clrmode = PSD_UShort(&ctx); + clrsize = PSD_UInt(&ctx); + /*palette =*/ PSD_Block(&ctx, clrsize); + ressize = PSD_UInt(&ctx); + /*resdata =*/ PSD_Block(&ctx, ressize); + lyrsize = PSD_UInt(&ctx); + /*lyrdata =*/ PSD_Block(&ctx, lyrsize); + cmp = PSD_UShort(&ctx); + + if (width <= 0 || height <= 0 || width > 16384 || height > 16384) + { + Con_Printf("%s too large dimensions (%u * %u)\n", fname, width, height); + return NULL; + } + if (chans <= 0) + { + Con_Printf("%s has no colour channels\n", fname); + return NULL; + } + if (clrmode == 3 && (depth == 8 || depth == 16)) //RGB + ; + else + { + Con_Printf("%s not 8 or 16bpp RGB .psd image\n", fname); + return NULL; + } + + //the data is planer + if (cmp == 0 || (cmp == 1 && depth==8)) + { + if (cmp) + PSD_Block(&ctx, 2*chans*height); //2 byte run size per plane*scanline + if (depth == 16) + { + unsigned short *r, *o; + r = o = BZ_Malloc(sizeof(*o)*4*width*height); + for(c = 0; c < 4; c++) + { + o = r+c; + if (c < chans) + { + for(l = 0; l < width*height; l++, o+=4) + *o = PSD_UShort(&ctx); + } + else + { //pad colour to 0, alpha 1 + for(l = 0; l < width*height; l++, o+=4) + *o = (c==3)?0xffff:0; + } + } + *outwidth = width; + *outheight = height; + *outformat = PTI_RGBA16; + return r; + } + else if (depth == 8) + { + qbyte *r, *o; + r = o = BZ_Malloc(sizeof(*o)*4*width*height); + for(c = 0; c < 4; c++) + { + o = r+c; + if (c < chans) + { + if (cmp == 1) + { + for(y = 0; y < height; y++) + { + for(l = 0; l < width; ) + { + qbyte run = PSD_Byte(&ctx), val; + if (run < 128) + { //copy + run++; + for (; l < width && run --> 0; l++, o+=4) + *o = PSD_Byte(&ctx); + } + else + { + run = 1-(char)run; + val = PSD_Byte(&ctx); + for (; l < width && run --> 0; l++, o+=4) + *o = val; + } + } + } + } + else for(l = 0; l < width*height; l++, o+=4) + *o = PSD_Byte(&ctx); + } + else + { //pad colour to 0, alpha 1 + for(l = 0; l < width*height; l++, o+=4) + *o = (c==3)?0xff:0; + } + } + *outwidth = width; + *outheight = height; + if (chans >= 4) + *outformat = PTI_RGBA8; + else + *outformat = PTI_RGBX8; + return r; + } + } + else if (cmp == 1) + { + } + Con_Printf("%s unsupported compression type (%u)\n", fname, cmp); + return NULL; +} +#endif + #ifndef NPFTE @@ -3003,12 +3456,15 @@ qboolean Image_WriteKTXFile(const char *filename, enum fs_relative fsroot, struc case PTI_L8A8_SRGB: header.glinternalformat = 0x8C45/*GL_SLUMINANCE8_ALPHA8*/; header.glbaseinternalformat = 0x190A/*GL_LUMINANCE_ALPHA*/; header.glformat = 0x190A/*GL_LUMINANCE_ALPHA*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break; case PTI_RGB8: header.glinternalformat = 0x8051/*GL_RGB8*/; header.glbaseinternalformat = 0x1907/*GL_RGB*/; header.glformat = 0x1907/*GL_RGB*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break; case PTI_BGR8: header.glinternalformat = 0x8051/*GL_RGB8*/; header.glbaseinternalformat = 0x1907/*GL_RGB*/; header.glformat = 0x80E0/*GL_BGR*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break; + case PTI_R16: header.glinternalformat = 0x822A/*GL_R16*/; header.glbaseinternalformat = 0x1903/*GL_RED*/; header.glformat = 0x1903/*GL_RED*/; header.gltype = 0x1403/*GL_UNSIGNED_SHORT*/; header.gltypesize = 2; break; + case PTI_RGBA16: header.glinternalformat = 0x805B/*GL_RGBA16*/; header.glbaseinternalformat = 0x1903/*GL_RED*/; header.glformat = 0x1903/*GL_RED*/; header.gltype = 0x1403/*GL_UNSIGNED_SHORT*/; header.gltypesize = 2; break; case PTI_R16F: header.glinternalformat = 0x822D/*GL_R16F*/; header.glbaseinternalformat = 0x1903/*GL_RED*/; header.glformat = 0x1903/*GL_RED*/; header.gltype = 0x140B/*GL_HALF_FLOAT*/; header.gltypesize = 2; break; case PTI_R32F: header.glinternalformat = 0x822E/*GL_R32F*/; header.glbaseinternalformat = 0x1903/*GL_RED*/; header.glformat = 0x1903/*GL_RED*/; header.gltype = 0x1406/*GL_FLOAT*/; header.gltypesize = 4; break; case PTI_RGBA16F: header.glinternalformat = 0x881A/*GL_RGBA16F*/; header.glbaseinternalformat = 0x1908/*GL_RGBA*/; header.glformat = 0x1908/*GL_RGBA*/; header.gltype = 0x140B/*GL_HALF_FLOAT*/; header.gltypesize = 2; break; case PTI_RGBA32F: header.glinternalformat = 0x8814/*GL_RGBA32F*/; header.glbaseinternalformat = 0x1908/*GL_RGBA*/; header.glformat = 0x1908/*GL_RGBA*/; header.gltype = 0x1406/*GL_FLOAT*/; header.gltypesize = 4; break; case PTI_A2BGR10: header.glinternalformat = 0x8059/*GL_RGB10_A2*/; header.glbaseinternalformat = 0x1908/*GL_RGBA*/; header.glformat = 0x1908/*GL_RGBA*/; header.gltype = 0x8368/*GL_UNSIGNED_INT_2_10_10_10_REV*/; header.gltypesize = 4; break; case PTI_E5BGR9: header.glinternalformat = 0x8C3D/*GL_RGB9_E5*/; header.glbaseinternalformat = 0x8C3D/*GL_RGB9_E5*/; header.glformat = 0x1907/*GL_RGB*/; header.gltype = 0x8C3E/*GL_UNSIGNED_INT_5_9_9_9_REV*/; header.gltypesize = 4; break; + case PTI_P8: case PTI_R8: header.glinternalformat = 0x8229/*GL_R8*/; header.glbaseinternalformat = 0x1903/*GL_RED*/; header.glformat = 0x1903/*GL_RED*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break; case PTI_RG8: header.glinternalformat = 0x822B/*GL_RG8*/; header.glbaseinternalformat = 0x8227/*GL_RG*/; header.glformat = 0x8227/*GL_RG*/; header.gltype = 0x1401/*GL_UNSIGNED_BYTE*/; header.gltypesize = 1; break; case PTI_R8_SNORM: header.glinternalformat = 0x8F94/*GL_R8_SNORM*/; header.glbaseinternalformat = 0x1903/*GL_RED*/; header.glformat = 0x1903/*GL_RED*/; header.gltype = 0x1400/*GL_BYTE*/; header.gltypesize = 1; break; @@ -3162,6 +3618,7 @@ static struct pendingtextureinfo *Image_ReadKTXFile(unsigned int flags, const ch case 0x80E1/*GL_BGRA_EXT*/: encoding = PTI_BGRA8; break; //not even an internal format case 0x1908/*GL_RGBA*/: case 0x8058/*GL_RGBA8*/: encoding = (header->glformat==0x80E1/*GL_BGRA*/)?PTI_BGRA8:PTI_RGBA8; break; //unsized types shouldn't really be here + case 0x805B/*GL_RGBA16*/: encoding = PTI_RGBA16; break; case 0x8C43/*GL_SRGB8_ALPHA8*/: encoding = (header->glformat==0x80E1/*GL_BGRA*/)?PTI_BGRA8_SRGB:PTI_RGBA8_SRGB; break; case 0x8040/*GL_LUMINANCE8*/: encoding = PTI_L8; break; case 0x8045/*GL_LUMINANCE8_ALPHA8*/: encoding = PTI_L8A8; break; @@ -3169,6 +3626,7 @@ static struct pendingtextureinfo *Image_ReadKTXFile(unsigned int flags, const ch case 0x8814/*GL_RGBA32F_ARB*/: encoding = PTI_RGBA32F; break; case 0x8059/*GL_RGB10_A2*/: encoding = PTI_A2BGR10; break; case 0x8229/*GL_R8*/: encoding = PTI_R8; break; + case 0x822A/*GL_R16*/: encoding = PTI_R16; break; case 0x822B/*GL_RG8*/: encoding = PTI_RG8; break; case 0x8F94/*GL_R8_SNORM*/: encoding = PTI_R8_SNORM; break; case 0x8F95/*GL_RG8_SNORM*/: encoding = PTI_RG8_SNORM; break; @@ -3330,7 +3788,7 @@ static struct pendingtextureinfo *Image_ReadKTXFile(unsigned int flags, const ch static struct pendingtextureinfo *Image_ReadPKMFile(unsigned int flags, const char *fname, qbyte *filedata, size_t filesize) { struct pendingtextureinfo *mips; - unsigned int encoding, blockbytes; + unsigned int encoding, blockbytes, blockwidth, blockheight; unsigned short ver, dfmt; unsigned short datawidth, dataheight; unsigned short imgwidth, imgheight; @@ -3377,28 +3835,8 @@ static struct pendingtextureinfo *Image_ReadPKMFile(unsigned int flags, const ch else return NULL; - switch(encoding) - { - case PTI_ETC1_RGB8: - case PTI_ETC2_RGB8: - case PTI_ETC2_RGB8_SRGB: - case PTI_ETC2_RGB8A1: - case PTI_ETC2_RGB8A1_SRGB: - case PTI_EAC_R11: - case PTI_EAC_R11_SNORM: - blockbytes = 8; - break; - case PTI_ETC2_RGB8A8: - case PTI_ETC2_RGB8A8_SRGB: - case PTI_EAC_RG11: - case PTI_EAC_RG11_SNORM: - blockbytes = 16; - break; - default: - return NULL; - } - - if (16+(datawidth/4)*(dataheight/4)*blockbytes != filesize) + Image_BlockSizeForEncoding(encoding, &blockbytes, &blockwidth, &blockheight); + if (16+((datawidth+blockwidth-1)/blockwidth)*((dataheight+blockheight-1)/blockheight)*blockbytes != filesize) return NULL; //err, not the right size! mips = Z_Malloc(sizeof(*mips)); @@ -3794,12 +4232,15 @@ qboolean Image_WriteDDSFile(const char *filename, enum fs_relative fsroot, struc case PTI_L8A8_SRGB: return false; //unsupported case PTI_RGB8: return false; //unsupported case PTI_BGR8: return false; //unsupported + case PTI_R16: h10.dxgiformat = 56/*DXGI_FORMAT_R16_UNORM*/; break; + case PTI_RGBA16: h10.dxgiformat = 11/*DXGI_FORMAT_R16G16B16A16_UNORM*/; break; case PTI_R16F: h10.dxgiformat = 54/*DXGI_FORMAT_R16_FLOAT*/; break; case PTI_R32F: h10.dxgiformat = 41/*DXGI_FORMAT_R32_FLOAT*/; break; case PTI_RGBA16F: h10.dxgiformat = 10/*DXGI_FORMAT_R16G16B16A16_FLOAT*/; break; case PTI_RGBA32F: h10.dxgiformat = 2/*DXGI_FORMAT_R32G32B32A32_FLOAT*/; break; case PTI_A2BGR10: h10.dxgiformat = 24/*DXGI_FORMAT_R10G10B10A2_UNORM*/; break; case PTI_E5BGR9: h10.dxgiformat = 67/*DXGI_FORMAT_R9G9B9E5_SHAREDEXP*/; break; + case PTI_P8: case PTI_R8: h10.dxgiformat = 61/*DXGI_FORMAT_R8_UNORM*/; break; case PTI_RG8: h10.dxgiformat = 49/*DXGI_FORMAT_R8G8_UNORM*/; break; case PTI_R8_SNORM: h10.dxgiformat = 63/*DXGI_FORMAT_R8_SNORM*/; break; @@ -4233,9 +4674,8 @@ qbyte *ReadRawImageFile(qbyte *buf, int len, int *width, int *height, uploadfmt_ } #ifdef AVAIL_PNGLIB - if (len > 4 && (buf[0] == 137 && buf[1] == 'P' && buf[2] == 'N' && buf[3] == 'G') && (data = ReadPNGFile(buf, len, width, height, fname))) + if (len > 4 && (buf[0] == 137 && buf[1] == 'P' && buf[2] == 'N' && buf[3] == 'G') && (data = ReadPNGFile(fname, buf, len, width, height, format))) { - *format = PTI_RGBA8; TRACE(("dbg: Read32BitImageFile: png\n")); return data; } @@ -4266,14 +4706,26 @@ qbyte *ReadRawImageFile(qbyte *buf, int len, int *width, int *height, uploadfmt_ return data; } - if (len > 6 && buf[0]==0&&buf[1]==0 && buf[2]==1&&buf[3]==0 && (data = ReadICOFile(buf, len, width, height, fname))) + if (len > 6 && buf[0]==0&&buf[1]==0 && buf[2]==1&&buf[3]==0 && (data = ReadICOFile(fname, buf, len, width, height, format))) { - *format = PTI_RGBA8; TRACE(("dbg: Read32BitImageFile: ico\n")); return data; } #endif +#ifdef IMAGEFMT_PBM + if (len > 2 && buf[0] == 'P' && ((buf[1] >= '1' && buf[1] <= '7') || buf[1] == 'F' || buf[1] == 'f') && (data = ReadPBMFile(buf, len, fname, width, height, format))) + return data; +#endif +#ifdef IMAGEFMT_HDR + if (len > 10 && (!strncmp(buf, "#?RADIANCE", 10)||!strncmp(buf, "#?RGBE", 6)) && (data = ReadRadianceFile(buf, len, fname, width, height, format))) + return data; +#endif +#ifdef IMAGEFMT_PSD + if (len > 26 && !strncmp(buf, "8BPS", 4) && (data = ReadPSDFile(buf, len, fname, width, height, format))) + return data; +#endif + #if 1//def IMAGEFMT_LMP if (len >= 8) //.lmp has no magic id. guess at it. { @@ -4428,7 +4880,7 @@ static struct #endif }; -static void Image_MipMap8888 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight) +static void Image_MipMap4X8 (qbyte *in, int inwidth, int inheight, qbyte *out, int outwidth, int outheight) { int i, j; qbyte *inrow; @@ -4484,6 +4936,151 @@ static void Image_MipMap8888 (qbyte *in, int inwidth, int inheight, qbyte *out, } } +//oh how I wish I had C++'s template stuff right now +static void Image_MipMap4X16 (unsigned short *in, int inwidth, int inheight, unsigned short *out, int outwidth, int outheight) +{ + int i, j; + unsigned short *inrow; + + int rowwidth = inwidth*4; //rowwidth is the byte width of the input + inrow = in; + + //mips round down, except for when the input is 1. which bugs out. + if (inwidth <= 1 && inheight <= 1) + { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + } + else if (inheight <= 1) + { + //single row, don't peek at the next + for (in = inrow, j=0 ; j>1; + out[1] = (in[1] + in[5])>>1; + out[2] = (in[2] + in[6])>>1; + out[3] = (in[3] + in[7])>>1; + } + } + else if (inwidth <= 1) + { + //single colum, peek only at this pixel + for (i=0 ; i>1; + out[1] = (in[1] + in[rowwidth+1])>>1; + out[2] = (in[2] + in[rowwidth+2])>>1; + out[3] = (in[3] + in[rowwidth+3])>>1; + } + } + } + else + { + for (i=0 ; i>2; + out[1] = (in[1] + in[5] + in[rowwidth+1] + in[rowwidth+5])>>2; + out[2] = (in[2] + in[6] + in[rowwidth+2] + in[rowwidth+6])>>2; + out[3] = (in[3] + in[7] + in[rowwidth+3] + in[rowwidth+7])>>2; + } + } + } +} + +static void Image_MipMap4X32F (float *in, int inwidth, int inheight, float *out, int outwidth, int outheight) +{ + int i, j; + float *inrow; + + int rowwidth = inwidth*4; //rowwidth is the byte width of the input + inrow = in; + + //mips round down, except for when the input is 1. which bugs out. + if (inwidth <= 1 && inheight <= 1) + { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + out[3] = in[3]; + } + else if (inheight <= 1) + { + //single row, don't peek at the next + for (in = inrow, j=0 ; j>1; + } + else if (inwidth <= 1) + { + //single colum, peek only at this pixel + for (i=0 ; i>1; + } + else + { + for (i=0 ; i>2; + } +} + static qbyte Image_BlendPalette_2(qbyte a, qbyte b) { return a; @@ -4537,7 +5134,7 @@ static void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int fla switch(mips->encoding) { - case PTI_R8: + case PTI_P8: if (sh_config.can_mipcap) return; //if we can cap mips, do that. it'll save lots of expensive lookups and uglyness. for (mip = mips->mipcount; mip < 32; mip++) @@ -4559,6 +5156,68 @@ static void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int fla mips->mipcount = mip+1; } return; + case PTI_R8: + if (sh_config.can_mipcap) + return; //if we can cap mips, do that. it'll save lots of expensive lookups and uglyness. + for (mip = mips->mipcount; mip < 32; mip++) + { + mips->mip[mip].width = mips->mip[mip-1].width >> 1; + mips->mip[mip].height = mips->mip[mip-1].height >> 1; + mips->mip[mip].depth = 1; + if (mips->mip[mip].width < 1 && mips->mip[mip].height < 1) + break; + if (mips->mip[mip].width < 1) + mips->mip[mip].width = 1; + if (mips->mip[mip].height < 1) + mips->mip[mip].height = 1; + mips->mip[mip].datasize = ((mips->mip[mip].width+3)&~3) * mips->mip[mip].height*4; + mips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize); + mips->mip[mip].needfree = true; + + Image_MipMap1X8(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height); + mips->mipcount = mip+1; + } + return; + case PTI_RGBA32F: + for (mip = mips->mipcount; mip < 32; mip++) + { + mips->mip[mip].width = mips->mip[mip-1].width >> 1; + mips->mip[mip].height = mips->mip[mip-1].height >> 1; + mips->mip[mip].depth = 1; + if (mips->mip[mip].width < 1 && mips->mip[mip].height < 1) + break; + if (mips->mip[mip].width < 1) + mips->mip[mip].width = 1; + if (mips->mip[mip].height < 1) + mips->mip[mip].height = 1; + mips->mip[mip].datasize = ((mips->mip[mip].width+3)&~3) * mips->mip[mip].height * sizeof(float)*4; + mips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize); + mips->mip[mip].needfree = true; + + Image_MipMap4X32F(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height); + mips->mipcount = mip+1; + } + break; + case PTI_RGBA16: + for (mip = mips->mipcount; mip < 32; mip++) + { + mips->mip[mip].width = mips->mip[mip-1].width >> 1; + mips->mip[mip].height = mips->mip[mip-1].height >> 1; + mips->mip[mip].depth = 1; + if (mips->mip[mip].width < 1 && mips->mip[mip].height < 1) + break; + if (mips->mip[mip].width < 1) + mips->mip[mip].width = 1; + if (mips->mip[mip].height < 1) + mips->mip[mip].height = 1; + mips->mip[mip].datasize = ((mips->mip[mip].width+3)&~3) * mips->mip[mip].height * sizeof(unsigned short)*4; + mips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize); + mips->mip[mip].needfree = true; + + Image_MipMap4X16(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height); + mips->mipcount = mip+1; + } + break; case PTI_RGBA8_SRGB: case PTI_RGBX8_SRGB: case PTI_BGRA8_SRGB: @@ -4578,11 +5237,11 @@ static void Image_GenerateMips(struct pendingtextureinfo *mips, unsigned int fla mips->mip[mip].width = 1; if (mips->mip[mip].height < 1) mips->mip[mip].height = 1; - mips->mip[mip].datasize = ((mips->mip[mip].width+3)&~3) * mips->mip[mip].height*4; + mips->mip[mip].datasize = ((mips->mip[mip].width+3)&~3) * mips->mip[mip].height * sizeof(qbyte)*4; mips->mip[mip].data = BZ_Malloc(mips->mip[mip].datasize); mips->mip[mip].needfree = true; - Image_MipMap8888(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height); + Image_MipMap4X8(mips->mip[mip-1].data, mips->mip[mip-1].width, mips->mip[mip-1].height, mips->mip[mip].data, mips->mip[mip].width, mips->mip[mip].height); mips->mipcount = mip+1; } break; @@ -5202,6 +5861,27 @@ static void Image_8888toARGB4444(struct pendingtextureinfo *mips, qboolean bgra) } } +static void Image_4X16to8888(struct pendingtextureinfo *mips) +{ + unsigned int mip; + for (mip = 0; mip < mips->mipcount; mip++) + { + unsigned short *in = mips->mip[mip].data; + qbyte *out = mips->mip[mip].data; + unsigned int w = mips->mip[mip].width; + unsigned int h = mips->mip[mip].height; + unsigned int p = w*h*4; + if (!mips->mip[mip].needfree && !mips->extrafree) + { + mips->mip[mip].needfree = true; + mips->mip[mip].data = out = BZ_Malloc(sizeof(*out)*p); + } + + while(p-->0) + *out++ = *in++>>8; + } +} + //may operate in place static void Image_8_BGR_RGB_Swap(qbyte *data, unsigned int w, unsigned int h) { @@ -5720,18 +6400,21 @@ void Image_BlockSizeForEncoding(uploadfmt_t encoding, unsigned int *blockbytes, b = 4; break; + case PTI_R16: case PTI_R16F: b = 1*2; break; case PTI_R32F: b = 1*4; break; + case PTI_RGBA16: case PTI_RGBA16F: b = 4*2; break; case PTI_RGBA32F: b = 4*4; break; + case PTI_P8: case PTI_R8: case PTI_R8_SNORM: b = 1; @@ -5867,6 +6550,9 @@ const char *Image_FormatName(uploadfmt_t fmt) case PTI_R32F: return "R32F"; case PTI_RGBA16F: return "RGBA16F"; case PTI_RGBA32F: return "RGBA32F"; + case PTI_R16: return "R16"; + case PTI_RGBA16: return "RGBA16"; + case PTI_P8: return "P8"; case PTI_R8: return "R8"; case PTI_R8_SNORM: return "R8_SNORM"; case PTI_RG8: return "RG8"; @@ -6195,7 +6881,7 @@ static void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int fla Image_DecompressFormat(mips); //force-decompress it, so that we can palettise it. if (mips->encoding == PTI_RGBX8 || mips->encoding == PTI_RGBA8) { - mips->encoding = PTI_R8; + mips->encoding = PTI_P8; for (mip = 0; mip < mips->mipcount; mip++) { unsigned int i; @@ -6250,6 +6936,14 @@ static void Image_ChangeFormat(struct pendingtextureinfo *mips, unsigned int fla //Fixme: PTI_RGBA16F -> PTI_RGBA32F //FIXME: PTI_RGBA32F -> PTI_RGBA8 + if (mips->encoding == PTI_RGBA16) + { + Image_4X16to8888(mips); + mips->encoding = PTI_RGBA8; + if (sh_config.texfmt[mips->encoding]) + return; //okay, that got it. + } + if ((mips->encoding == PTI_RGBX8 && sh_config.texfmt[PTI_BGRX8]) || (mips->encoding == PTI_BGRX8 && sh_config.texfmt[PTI_RGBX8]) || (mips->encoding == PTI_RGBA8 && sh_config.texfmt[PTI_BGRA8]) || @@ -6329,6 +7023,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag unsigned int *rgbadata = rawdata; int i; qboolean valid; + unsigned int bb, bw, bh; mips->mip[0].width = imgwidth; mips->mip[0].height = imgheight; @@ -6337,52 +7032,17 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag switch(fmt) { - case TF_DEPTH16: - mips->encoding = PTI_DEPTH16; - break; - case TF_DEPTH24: - mips->encoding = PTI_DEPTH24; - break; - case TF_DEPTH32: - mips->encoding = PTI_DEPTH32; - break; - case TF_RGBA16F: - case TF_RGBA32F: - if (rawdata) - { - Con_Printf("R_LoadRawTexture: bad format\n"); - if (freedata) - BZ_Free(rawdata); - return false; - } - mips->encoding = (fmt==TF_RGBA16F)?PTI_RGBA16F:TF_RGBA32F; + default: + mips->encoding = fmt; break; - default: case TF_INVALID: Con_Printf("R_LoadRawTexture: bad format\n"); if (freedata) BZ_Free(rawdata); return false; - case TF_RGBX32: - mips->encoding = PTI_RGBX8; - break; - case TF_RGBA32: - mips->encoding = PTI_RGBA8; - break; - case TF_BGRX32: - mips->encoding = PTI_BGRX8; - break; - case TF_BGRA32: - mips->encoding = PTI_BGRA8; - break; - case PTI_A2BGR10: - mips->encoding = PTI_A2BGR10; - break; - case PTI_E5BGR9: - mips->encoding = PTI_E5BGR9; - break; - case TF_MIP4_R8: + + case TF_MIP4_P8: //8bit indexed data. Image_RoundDimensions(&mips->mip[0].width, &mips->mip[0].height, flags); flags |= IF_NOPICMIP; @@ -6394,7 +7054,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag (imgwidth>>2) * (imgheight>>2) + (imgwidth>>3) * (imgheight>>3); - mips->encoding = PTI_R8; + mips->encoding = PTI_P8; rgbadata = BZ_Malloc(pixels); memcpy(rgbadata, rawdata, pixels); @@ -6418,9 +7078,6 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag return true; } //fall through - case PTI_R8: - mips->encoding = PTI_R8; - break; case PTI_LLLX8: if (sh_config.texfmt[((vid.flags & VID_SRGBAWARE) /*&& (flags & IF_SRGB)*/ && !(flags & IF_NOSRGB))?PTI_L8_SRGB:PTI_L8]) { //if we can compact it, then do so! @@ -6452,9 +7109,6 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag else mips->encoding = PTI_RGBA8; break; - case PTI_L8: - mips->encoding = PTI_L8; - break; case TF_MIP4_SOLID8: //8bit opaque data Image_RoundDimensions(&mips->mip[0].width, &mips->mip[0].height, flags); @@ -6793,6 +7447,7 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag case PTI_BGRA8_SRGB: mips->encoding = PTI_BGRX8_SRGB; break; + case PTI_RGBA16: case PTI_RGBA16F: case PTI_RGBA32F: case PTI_ARGB4444: @@ -6854,6 +7509,8 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag break; case PTI_L8: case PTI_L8_SRGB: + case PTI_R16: + case PTI_P8: case PTI_R8: case PTI_R8_SNORM: case PTI_RG8: @@ -7036,7 +7693,8 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag } else mips->mip[0].data = NULL; - mips->mip[0].datasize = mips->mip[0].width*mips->mip[0].height*4; + Image_BlockSizeForEncoding(mips->encoding, &bb, &bw, &bh); + mips->mip[0].datasize = ((mips->mip[0].width+bw-1)/bw) * ((mips->mip[0].height+bh-1)/bh) * bb; if (mips->type == PTI_3D) { @@ -7067,10 +7725,31 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag { //works for rgba or bgra int i; - qbyte *fte_restrict premul = (qbyte*)mips->mip[0].data; switch(mips->encoding) { - case PTI_LLLX8: + case PTI_RGBA32F: + { + float *fte_restrict premul = (float*)mips->mip[0].data; + for (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++, premul+=4) + { + premul[0] = (premul[0] * premul[3]); + premul[1] = (premul[1] * premul[3]); + premul[2] = (premul[2] * premul[3]); + } + } + break; + case PTI_RGBA16: + { + unsigned short *fte_restrict premul = (unsigned short*)mips->mip[0].data; + for (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++, premul+=4) + { + premul[0] = (premul[0] * premul[3])>>16; + premul[1] = (premul[1] * premul[3])>>16; + premul[2] = (premul[2] * premul[3])>>16; + } + } + break; + case PTI_LLLX8: //FIXME: why the Xs? case PTI_LLLA8: case PTI_RGBA8: case PTI_RGBX8: @@ -7080,18 +7759,24 @@ static qboolean Image_GenMip0(struct pendingtextureinfo *mips, unsigned int flag case PTI_RGBX8_SRGB: case PTI_BGRA8_SRGB: case PTI_BGRX8_SRGB: - for (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++, premul+=4) { - premul[0] = (premul[0] * premul[3])>>8; - premul[1] = (premul[1] * premul[3])>>8; - premul[2] = (premul[2] * premul[3])>>8; + qbyte *fte_restrict premul = (qbyte*)mips->mip[0].data; + for (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++, premul+=4) + { + premul[0] = (premul[0] * premul[3])>>8; + premul[1] = (premul[1] * premul[3])>>8; + premul[2] = (premul[2] * premul[3])>>8; + } } break; case PTI_L8A8: case PTI_L8A8_SRGB: - for (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++, premul+=2) - premul[0] = (premul[0] * premul[1])>>8; - break; + { + qbyte *fte_restrict premul = (qbyte*)mips->mip[0].data; + for (i = 0; i < mips->mip[0].width*mips->mip[0].height; i++, premul+=2) + premul[0] = (premul[0] * premul[1])>>8; + break; + } default: break; //format not known, so no idea how to premultiply it. bc2/3 might already be premultiplied or not... } @@ -7129,7 +7814,6 @@ static qboolean Image_LoadRawTexture(texid_t tex, unsigned int flags, void *rawd return true; } -#if 1 //always frees filedata, even on failure. //also frees the textures fallback data, but only on success static struct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char *iname, const char *fname, qbyte *filedata, int filesize) @@ -7188,6 +7872,8 @@ static struct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char switch(format) { + default: + break; case PTI_RGBA32F: case PTI_RGBA16F: case PTI_L8A8: @@ -7242,8 +7928,6 @@ static struct pendingtextureinfo *Image_LoadMipsFromMemory(int flags, const char } } break; - default: - break; } mips = Z_Malloc(sizeof(*mips)); @@ -7303,128 +7987,6 @@ qboolean Image_LoadTextureFromMemory(texid_t tex, int flags, const char *iname, } return false; } -#else -//always frees filedata, even on failure. -//also frees the textures fallback data, but only on success -qboolean Image_LoadTextureFromMemory(texid_t tex, int flags, const char *iname, char *fname, qbyte *filedata, int filesize) -{ - qboolean hasalpha; - qbyte *rgbadata; - - int imgwidth, imgheight; - - struct pendingtextureinfo *mips = NULL; - - //these formats have special handling, because they cannot be implemented via Read32BitImageFile - they don't result in rgba images. -#ifdef IMAGEFMT_KTX - if (!mips) - mips = Image_ReadKTXFile(flags, fname, filedata, filesize); -#endif -#ifdef IMAGEFMT_PKM - if (!mips) - mips = Image_ReadPKMFile(flags, fname, filedata, filesize); -#endif -#ifdef IMAGEFMT_DDS - if (!mips) - mips = Image_ReadDDSFile(flags, fname, filedata, filesize); -#endif -#ifdef IMAGEFMT_BLP - if (!mips && filedata[0] == 'B' && filedata[1] == 'L' && filedata[2] == 'P' && filedata[3] == '2') - mips = Image_ReadBLPFile(flags, fname, filedata, filesize); -#endif - - if (mips) - { - tex->width = mips->mip[0].width; - tex->height = mips->mip[0].height; - Image_ChangeFormat(mips, flags, TF_INVALID); - - if (flags & IF_NOWORKER) - Image_LoadTextureMips(tex, mips, 0, 0); - else - COM_AddWork(WG_MAIN, Image_LoadTextureMips, tex, mips, 0, 0); - return true; - } - - hasalpha = false; - if ((rgbadata = Read32BitImageFile(filedata, filesize, &imgwidth, &imgheight, &hasalpha, fname))) - { - extern cvar_t vid_hardwaregamma; - if (!(flags&IF_NOGAMMA) && !vid_hardwaregamma.value) - BoostGamma(rgbadata, imgwidth, imgheight); - - if (hasalpha) - flags &= ~IF_NOALPHA; - else if (!(flags & IF_NOALPHA)) - { - unsigned int alpha_width, alpha_height, p; - char aname[MAX_QPATH]; - unsigned char *alphadata; - char *alph; - size_t alphsize; - char ext[8]; - COM_StripExtension(fname, aname, sizeof(aname)); - COM_FileExtension(fname, ext, sizeof(ext)); - Q_strncatz(aname, "_alpha.", sizeof(aname)); - Q_strncatz(aname, ext, sizeof(aname)); - if (!strchr(aname, ':') && (alph = FS_LoadMallocFile (aname, &alphsize))) - { - if ((alphadata = Read32BitImageFile(alph, alphsize, &alpha_width, &alpha_height, &hasalpha, aname))) - { - if (alpha_width == imgwidth && alpha_height == imgheight) - { - for (p = 0; p < alpha_width*alpha_height; p++) - { - rgbadata[(p<<2) + 3] = (alphadata[(p<<2) + 0] + alphadata[(p<<2) + 1] + alphadata[(p<<2) + 2])/3; - } - } - BZ_Free(alphadata); - } - BZ_Free(alph); - } - } - - if (Image_LoadRawTexture(tex, flags, rgbadata, NULL, imgwidth, imgheight, TF_RGBA32)) - { - BZ_Free(filedata); - - //and kill the fallback now that its loaded, as it won't be needed any more. - BZ_Free(tex->fallbackdata); - tex->fallbackdata = NULL; - return true; - } - BZ_Free(rgbadata); - } -#ifdef FTE_TARGET_WEB - else if (1) - { - struct pendingtextureinfo *mips; - mips = Z_Malloc(sizeof(*mips)); - mips->type = (flags & IF_3DMAP)?PTI_3D:PTI_2D; - mips->mipcount = 1; - mips->encoding = PTI_WHOLEFILE; - mips->extrafree = NULL; - mips->mip[0].width = 1; - mips->mip[0].height = 1; - mips->mip[0].depth = 1; - mips->mip[0].data = filedata; - mips->mip[0].datasize = filesize; - mips->mip[0].needfree = true; - //width+height are not yet known. bah. - if (flags & IF_NOWORKER) - Image_LoadTextureMips(tex, mips, 0, 0); - else - COM_AddWork(WG_MAIN, Image_LoadTextureMips, tex, mips, 0, 0); - return true; - } -#endif - else - Con_Printf("Unable to read file %s (format unsupported)\n", fname); - - BZ_Free(filedata); - return false; -} -#endif static struct pendingtextureinfo *Image_LoadCubemapTextureData(const char *nicename, char *subpath, unsigned int texflags) { @@ -7971,8 +8533,18 @@ static void Image_LoadHiResTextureWorker(void *ctx, void *data, size_t a, size_t COM_AddWork(WG_MAIN, Image_LoadTexture_Failed, tex, NULL, 0, 0); } - - +//returns the pointer if its valid, otherwise null +//this is to pass pointers via the console +image_t *Image_TextureIsValid(qintptr_t address) +{ + image_t *img; + for (img = imagelist; img; img = img->next) + { + if (img == (image_t*)address) + break; + } + return img; +} //find an existing texture image_t *Image_FindTexture(const char *identifier, const char *subdir, unsigned int flags) @@ -8128,6 +8700,7 @@ image_t *QDECL Image_GetTexture(const char *identifier, const char *subpath, uns b *= 1; break; case PTI_R8: + case PTI_P8: case TF_SOLID8: case TF_TRANS8: case TF_TRANS8_FULLBRIGHT: @@ -8147,7 +8720,7 @@ image_t *QDECL Image_GetTexture(const char *identifier, const char *subpath, uns case TF_MIP4_8PAL24: case TF_MIP4_8PAL24_T255: pb = 3*256; - case TF_MIP4_R8: + case TF_MIP4_P8: case TF_MIP4_SOLID8: b = (fallbackwidth>>0)*(fallbackheight>>0) + (fallbackwidth>>1)*(fallbackheight>>1) + @@ -8407,15 +8980,15 @@ void Image_List_f(void) while((bullshit=strchr(defuck, '\\'))) *bullshit = '/'; - if ((tex->flags&(IF_CLAMP|IF_PALETTIZE)) == 0) - Con_Printf("^[%s\\tip\\%s/%s\\tiprawimg\\%s^]: ", tex->ident, defuck, fname, tex->ident); - else - Con_Printf("^[%s\\tip\\%s/%s^]: ", tex->ident, defuck, fname); +// if ((tex->flags&(IF_CLAMP|IF_PALETTIZE)) == 0) + Con_Printf("^[%s\\tip\\%s/%s\\tipimgptr\\%#"PRIxSIZE"^]: ", tex->ident, defuck, fname, (size_t)tex); +// else +// Con_Printf("^[%s\\tip\\%s/%s^]: ", tex->ident, defuck, fname); } else { loc.len = 0; - Con_Printf("%s: ", tex->ident); + Con_Printf("^[%s\\tipimgptr\\%#"PRIxSIZE"^]: ", tex->ident, (size_t)tex); } for (a = tex->aliasof; a; a = a->aliasof) diff --git a/engine/client/m_download.c b/engine/client/m_download.c index 04afe4cb6..ffc285906 100644 --- a/engine/client/m_download.c +++ b/engine/client/m_download.c @@ -1023,26 +1023,45 @@ static void PM_WriteInstalledPackages(void); static package_t *PM_FindPackage(const char *packagename); static int QDECL PM_EnumeratedPlugin (const char *name, qofs_t size, time_t mtime, void *param, searchpathfuncs_t *spath) { + static const char *knownarch[] = + { + "x32", "x64", "amd64", "x86", //various x86 ABIs + "arm", "arm64", "armhf", //various arm ABIs + "ppc", "unk", //various misc ABIs + }; package_t *p; struct packagedep_s *dep; char vmname[MAX_QPATH]; - int len; + int len, l, a; char *dot; if (!strncmp(name, "fteplug_", 8)) Q_strncpyz(vmname, name+8, sizeof(vmname)); else Q_strncpyz(vmname, name, sizeof(vmname)); len = strlen(vmname); - len -= strlen(ARCH_CPU_POSTFIX ARCH_DL_POSTFIX); - if (!strcmp(vmname+len, ARCH_CPU_POSTFIX ARCH_DL_POSTFIX)) + l = strlen(ARCH_CPU_POSTFIX ARCH_DL_POSTFIX); + if (len > l && !strcmp(vmname+len-l, ARCH_CPU_POSTFIX ARCH_DL_POSTFIX)) + { + len -= l; vmname[len] = 0; + } else { dot = strchr(vmname, '.'); if (dot) + { *dot = 0; + len = strlen(vmname); + + //if we can find a known cpu arch there then ignore it - its a different cpu arch + for (a = 0; a < countof(knownarch); a++) + { + l = strlen(knownarch[a]); + if (len > l && !Q_strcasecmp(vmname + len-l, knownarch[a])) + return true; //wrong arch! ignore it. + } + } } - len = strlen(vmname); if (len > 0 && vmname[len-1] == '_') vmname[len-1] = 0; @@ -1112,7 +1131,7 @@ static void PM_PreparePackageList(void) char nat[MAX_OSPATH]; FS_NativePath("", FS_BINARYPATH, nat, sizeof(nat)); Con_DPrintf("Loading plugins from \"%s\"\n", nat); - Sys_EnumerateFiles(nat, "fteplug_*" ARCH_CPU_POSTFIX ARCH_DL_POSTFIX, PM_EnumeratedPlugin, &foundone, NULL); + Sys_EnumerateFiles(nat, "fteplug_*" ARCH_DL_POSTFIX, PM_EnumeratedPlugin, &foundone, NULL); if (foundone && !pluginpromptshown) { pluginpromptshown = true; diff --git a/engine/client/m_master.c b/engine/client/m_master.c index 09ad795c3..523415740 100644 --- a/engine/client/m_master.c +++ b/engine/client/m_master.c @@ -790,7 +790,7 @@ static qboolean SL_Key (int key, menu_t *menu) Master_FindRoute(server->adr); serverpreview = 4; } - else if (key == 'b' || key == 'o' || key == 'j' || key == K_ENTER || key == K_KP_ENTER || key != K_GP_START) //join + else if (key == 'b' || key == 'o' || key == 'j' || key == K_ENTER || key == K_KP_ENTER || key == K_GP_START) //join { if (key == 's' || key == 'o') { diff --git a/engine/client/m_mp3.c b/engine/client/m_mp3.c index 8bd72832a..fb7e25fb1 100644 --- a/engine/client/m_mp3.c +++ b/engine/client/m_mp3.c @@ -2322,7 +2322,7 @@ static cin_t *Media_Static_TryLoad(char *name) (staticfilmimage = ReadJPEGFile(file, fsize, &imagewidth, &imageheight)) || #endif #ifdef AVAIL_PNGLIB - (staticfilmimage = ReadPNGFile(file, fsize, &imagewidth, &imageheight, fullname)) || + (staticfilmimage = ReadPNGFile(fullname, file, fsize, &imagewidth, &imageheight, &format)) || #endif 0) { diff --git a/engine/client/m_options.c b/engine/client/m_options.c index 9daa90e07..e278f6704 100644 --- a/engine/client/m_options.c +++ b/engine/client/m_options.c @@ -307,9 +307,7 @@ void M_Menu_Options_f (void) MB_CONSOLECMD("FPS Options", "menu_fps\n", "Set model filtering and graphical profile options."), MB_CONSOLECMD("Rendering Options", "menu_render\n", "Set rendering options such as water warp and tinting effects."), MB_CONSOLECMD("Lighting Options", "menu_lighting\n", "Set options for level lighting and dynamic lights."), -#ifdef GLQUAKE MB_CONSOLECMD("Texture Options", "menu_textures\n", "Set options for texture detail and effects."), -#endif #ifndef MINIMAL MB_CONSOLECMD("Particle Options", "menu_particles\n", "Set particle effect options."), #endif @@ -1068,8 +1066,10 @@ void FPS_Preset_f (void) if (!stricmp("dp", arg)) { +#ifdef HAVE_SERVER if (sv.state) Cbuf_InsertText("echo Be sure to restart your server\n", RESTRICT_LOCAL, false); +#endif Cbuf_InsertText( //these are for smc+derived mods "sv_listen_dp 1\n" //awkward, but forces the server to load the effectinfo.txt in advance. @@ -1078,6 +1078,9 @@ void FPS_Preset_f (void) "dpcompat_noretouchground 1\n" //don't call touch functions on entities that already appear onground. this also changes the order that the onground flag is set relative to touch functions. "cl_nopred 1\n" //DP doesn't predict by default, and DP mods have a nasty habit of clearing .solid values during prethinks, which screws up prediction. so play safe. "r_dynamic 0\nr_shadow_realtime_dlight 1\n" //fte has separate cvars for everything. which kinda surprises people and makes stuff twice as bright as it should be. + "r_coronas_intensity 0.25\n" + "con_logcenterprint 0\n" //kinda annoying.... + "scr_fov_mode 4\n" //for fairer framerate comparisons //general compat stuff "dpcompat_console 1\n" // @@ -1305,7 +1308,6 @@ void M_Menu_Render_f (void) MC_AddFrameEnd(menu, y); } -#ifdef GLQUAKE void M_Menu_Textures_f (void) { static const char *texturefilternames[] = @@ -1384,7 +1386,9 @@ void M_Menu_Textures_f (void) MB_COMBOCVAR("2D Filter Mode", gl_texturemode2d, texture2dfilternames, texture2dfiltervalues, "Chooses the texture filtering method used for HUD, menus, and other 2D assets."), MB_COMBOCVAR("Anisotropy", gl_texture_anisotropic_filtering, anisotropylevels, anisotropyvalues, NULL), MB_SPACING(4), +#ifdef GLQUAKE MB_CHECKBOXCVAR("Software-style Rendering", r_softwarebanding_cvar, 0), +#endif MB_CHECKBOXCVAR("Specular Highlights", gl_specular, 0), // MB_CHECKBOXCVAR("Detail Textures", gl_detail, 0), MB_CHECKBOXCVAR("offsetmapping", r_glsl_offsetmapping, 0), @@ -1402,7 +1406,6 @@ void M_Menu_Textures_f (void) MC_AddBulk(menu, &resel, bulk, 16, 216, y); MC_AddFrameEnd(menu, y); } -#endif typedef struct { menucombo_t *lightcombo; @@ -3088,11 +3091,13 @@ typedef struct MV_TEXTURE, MV_COLLISION, MV_EVENTS, + MV_NORMALS, } mode; int surfaceidx; int skingroup; int framegroup; int boneidx; + int textype; double framechangetime; double skinchangetime; float pitch; @@ -3337,6 +3342,25 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ #endif + if (mods->mode == MV_NORMALS) + { + shader_t *s; + if (1) + { + s = R_RegisterShader("hitbox_nodepth", SUF_NONE, + "{\n" + "polygonoffset\n" + "{\n" + "map $whiteimage\n" + "blendfunc gl_src_alpha gl_one\n" + "rgbgen vertex\n" + "alphagen vertex\n" + "nodepthtest\n" + "}\n" + "}\n"); + Mod_AddSingleSurface(&ent, mods->surfaceidx, s, true); + } + } if (mods->mode == MV_COLLISION) { shader_t *s; @@ -3359,7 +3383,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ "nodepthtest\n" "}\n" "}\n"); - Mod_AddSingleSurface(&ent, mods->surfaceidx, s); + Mod_AddSingleSurface(&ent, mods->surfaceidx, s, false); } else { @@ -3497,7 +3521,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ else { shader = Mod_ShaderForSkin(ent.model, mods->surfaceidx, mods->skingroup); - Draw_FunString(0, y, va("Skin %i: \"%s\", shader \"%s\"", mods->skingroup, fname, shader?shader->name:"NO SHADER")); + Draw_FunString(0, y, va("Skin %i: \"%s\", material \"%s\"", mods->skingroup, fname, shader?shader->name:"NO SHADER")); } y+=8; } @@ -3508,7 +3532,7 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ qboolean loop = false; if (!Mod_FrameInfoForNum(ent.model, mods->surfaceidx, mods->framegroup, &fname, &numframes, &duration, &loop)) fname = "Unknown Frame"; - Draw_FunString(0, y, va("Frame%i: %s (%i poses, %f/%f secs, %s)", mods->framegroup, fname, numframes, ent.framestate.g[FS_REG].frametime[0], duration, loop?"looped":"unlooped")); + Draw_FunString(0, y, va("Frame%i: %s (%i poses, %f of %f secs, %s)", mods->framegroup, fname, numframes, ent.framestate.g[FS_REG].frametime[0], duration, loop?"looped":"unlooped")); y+=8; } @@ -3593,6 +3617,8 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ , CON_WHITEMASK, CPRINT_TALIGN|CPRINT_LALIGN, font_default, fs); } break; + case MV_NORMALS: + break; case MV_BONES: #ifdef SKELETALMODELS { @@ -3645,10 +3671,62 @@ static void M_ModelViewerDraw(int x, int y, struct menucustom_s *c, struct menu_ shader_t *shader = Mod_ShaderForSkin(ent.model, mods->surfaceidx, mods->skingroup); if (shader) { + char *t; texnums_t *skin = shader->defaulttextures; shader = R_RegisterShader("modelviewertexturepreview", SUF_2D, "{\nprogram default2d\n{\nmap $diffuse\n}\n}\n"); - shader->defaulttextures->base = skin->base; - R2D_Image(0, y, shader->defaulttextures->base->width, shader->defaulttextures->base->height, 0, 0, 1, 1, shader); + + switch(mods->textype) + { + case 1: + t = "Normalmap"; + shader->defaulttextures->base = skin->bump; + break; + case 2: + t = "SpecularMap"; + shader->defaulttextures->base = skin->specular; //specular lighting values. + break; + case 3: + t = "UpperMap"; + shader->defaulttextures->base = skin->upperoverlay; //diffuse texture for the upper body(shirt colour). no alpha channel. added to base.rgb + break; + case 4: + t = "LopwerMap"; + shader->defaulttextures->base = skin->loweroverlay; //diffuse texture for the lower body(trouser colour). no alpha channel. added to base.rgb + break; + case 5: + t = "PalettedMap"; + shader->defaulttextures->base = skin->paletted; //8bit paletted data, just because. + break; + case 6: + t = "FullbrightMap"; + shader->defaulttextures->base = skin->fullbright; + break; + case 7: + t = "ReflectCube"; + shader->defaulttextures->base = skin->reflectcube; + break; + case 8: + t = "ReflectMask"; + shader->defaulttextures->base = skin->reflectmask; + break; + case 9: + t = "DisplacementMap"; + shader->defaulttextures->base = skin->displacement; + break; + default: + mods->textype = 0; + t = "Diffusemap"; + shader->defaulttextures->base = skin->base; + break; + } + if (shader->defaulttextures->base) + { + Draw_FunString(0, y, va("%s: %s (%s)", t, shader->defaulttextures->base->ident, shader->defaulttextures->base->subpath)); + y+=8; + R2D_Image(0, y, shader->defaulttextures->base->width, shader->defaulttextures->base->height, 0, 0, 1, 1, shader); + } + else + Draw_FunString(0, y, va("%s: ", t)); } } break; @@ -3677,7 +3755,8 @@ static qboolean M_ModelViewerKey(struct menucustom_s *c, struct menu_s *m, int k case MV_SHADER: mods->mode = MV_TEXTURE; break; case MV_TEXTURE: mods->mode = MV_COLLISION; break; case MV_COLLISION: mods->mode = MV_EVENTS; break; - case MV_EVENTS: mods->mode = MV_NONE; break; + case MV_EVENTS: mods->mode = MV_NORMALS; break; + case MV_NORMALS: mods->mode = MV_NONE; break; } } else if (key == 'r') @@ -3694,7 +3773,11 @@ static qboolean M_ModelViewerKey(struct menucustom_s *c, struct menu_s *m, int k } #endif else if (key == '[') + { mods->boneidx--; + if (mods->boneidx < 0) + mods->boneidx = 0; + } else if (key == ']') mods->boneidx++; else if (key == K_UPARROW || key == K_KP_UPARROW || key == K_GP_DPAD_UP) @@ -3705,6 +3788,14 @@ static qboolean M_ModelViewerKey(struct menucustom_s *c, struct menu_s *m, int k mods->yaw -= 5; else if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT) mods->yaw += 5; + else if (key == 't') + { + if (mods->mode == MV_TEXTURE) + mods->textype += 1; + else + mods->textype = 0; + mods->mode = MV_TEXTURE; + } else if (key == K_END) { mods->skingroup = max(0, mods->skingroup-1); @@ -3821,7 +3912,8 @@ static int QDECL CompleteModelViewerList (const char *name, qofs_t flags, time_t || !strcmp(ext, ".iqm") || !strcmp(ext, ".dpm") || !strcmp(ext, ".zym") || !strcmp(ext, ".psk") || !strcmp(ext, ".md5mesh") || !strcmp(ext, ".md5anim") || !strcmp(ext, ".bsp") || !strcmp(ext, ".map") || !strcmp(ext, ".hmp") - || !strcmp(ext, ".spr") || !strcmp(ext, ".sp2") || !strcmp(ext, ".spr32")) + || !strcmp(ext, ".spr") || !strcmp(ext, ".sp2") || !strcmp(ext, ".spr32") + || !strcmp(ext, ".gltf") || !strcmp(ext, ".glb") || !strcmp(ext, ".ase") || !strcmp(ext, ".lwo") || !strcmp(ext, ".obj")) { ctx->cb(name, NULL, NULL, ctx); } diff --git a/engine/client/menu.c b/engine/client/menu.c index 3b1d86226..89daed93a 100644 --- a/engine/client/menu.c +++ b/engine/client/menu.c @@ -1260,9 +1260,7 @@ void M_Init_Internal (void) Cmd_AddCommand ("menu_fps", M_Menu_FPS_f); Cmd_AddCommand ("menu_render" , M_Menu_Render_f); Cmd_AddCommand ("menu_lighting", M_Menu_Lighting_f); -#ifdef GLQUAKE Cmd_AddCommand ("menu_textures", M_Menu_Textures_f); -#endif Cmd_AddCommand ("menu_teamplay", M_Menu_Teamplay_f); Cmd_AddCommand ("menu_teamplay_locations", M_Menu_Teamplay_Locations_f); Cmd_AddCommand ("menu_teamplay_needs", M_Menu_Teamplay_Needs_f); diff --git a/engine/client/merged.h b/engine/client/merged.h index 88417d04c..f1bd4ae97 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -198,7 +198,7 @@ extern int Mod_GetFrameCount (struct model_s *model); extern qboolean Mod_GetTag (struct model_s *model, int tagnum, framestate_t *framestate, float *transforms); extern int Mod_TagNumForName (struct model_s *model, const char *name); -void Mod_AddSingleSurface(struct entity_s *ent, int surfaceidx, shader_t *shader); +void Mod_AddSingleSurface(struct entity_s *ent, int surfaceidx, shader_t *shader, qboolean normals); int Mod_GetNumBones(struct model_s *model, qboolean allowtags); int Mod_GetBoneRelations(struct model_s *model, int firstbone, int lastbone, framestate_t *fstate, float *result); int Mod_GetBoneParent(struct model_s *model, int bonenum); diff --git a/engine/client/net_master.c b/engine/client/net_master.c index 3d1b09663..2582b61c8 100644 --- a/engine/client/net_master.c +++ b/engine/client/net_master.c @@ -73,7 +73,7 @@ qboolean Master_MasterProtocolIsEnabled(enum masterprotocol_e protocol) #define MAX_MASTER_ADDRESSES 4 //each master might have multiple dns addresses, typically both ipv4+ipv6. we want to report to both address families so we work with remote single-stack hosts. -#ifndef CLIENTONLY +#ifdef HAVE_SERVER static void QDECL Net_Masterlist_Callback(struct cvar_s *var, char *oldvalue); static void SV_SetMaster_f (void); #else @@ -247,7 +247,11 @@ void SV_Master_SingleHeartbeat(net_masterlist_t *master) switch(master->protocol) { case MP_QUAKEWORLD: - if (sv_listen_qw.value) + if ((svs.gametype == GT_PROGS || svs.gametype == GT_Q1QVM +#ifdef VM_LUA + || svs.gametype == GT_LUA +#endif + ) && sv_listen_qw.value && na->type == NA_IP) { if (!madeqwstring) { @@ -271,7 +275,7 @@ void SV_Master_SingleHeartbeat(net_masterlist_t *master) break; #ifdef Q2SERVER case MP_QUAKE2: - if (svs.gametype == GT_QUAKE2 && sv_listen_qw.value) //sv_listen==sv_listen_qw, yes, weird. + if (svs.gametype == GT_QUAKE2 && sv_listen_qw.value && na->type == NA_IP) //sv_listen==sv_listen_qw, yes, weird. { char *str = "\377\377\377\377heartbeat\n%s\n%s"; char info[8192]; @@ -363,6 +367,7 @@ struct thr_res }; void SV_Master_Worker_Resolved(void *ctx, void *data, size_t a, size_t b) { + char adr[256]; int i; struct thr_res *work = data; netadr_t *na; @@ -380,10 +385,29 @@ void SV_Master_Worker_Resolved(void *ctx, void *data, size_t a, size_t b) *na = work->na[i]; master->needsresolve = false; + switch (master->protocol) + { +#ifdef Q2SERVER + case MP_QUAKE2: +#endif + case MP_QUAKEWORLD: + //these masters have no ipv6 results query, so don't sent ipv6 heartbeats + //(its possible that a router will convert to ipv4, but such a router is probably natted and its not really worth it) + if (na->type != NA_IP) + na->type = NA_INVALID; + break; + default: + //these masters should do ipv4+ipv6, but not others. + if (na->type != NA_IP && na->type != NA_IPV6) + na->type = NA_INVALID; + break; //protocol + } + if (na->type == NA_INVALID) memset(na, 0, sizeof(*na)); else { + Con_DPrintf ("Resolved master \"%s\" to %s\n", master->cv.string, NET_AdrToString(adr, sizeof(adr), na)); //fix up default ports if not specified if (!na->port) { @@ -395,7 +419,7 @@ void SV_Master_Worker_Resolved(void *ctx, void *data, size_t a, size_t b) #endif case MP_DPMASTER: na->port = BigShort (27950); break; #ifdef Q2SERVER - case MP_QUAKE2: na->port = BigShort (27000); break; //FIXME: verify + case MP_QUAKE2: na->port = BigShort (27900); break; //FIXME: verify #endif #ifdef Q3SERVER case MP_QUAKE3: na->port = BigShort (27950); break; @@ -541,6 +565,7 @@ void SV_Master_ClearAll(void) } } +#ifndef NOLEGACY /* ==================== SV_SetMaster_f @@ -580,6 +605,7 @@ static void SV_SetMaster_f (void) svs.last_heartbeat = -99999; } +#endif void SV_Master_ReResolve(void) { @@ -2543,18 +2569,23 @@ void MasterInfo_Request(master_t *mast) case MP_QUAKE3: { char *str; - str = va("%c%c%c%cgetservers %u empty full\n", 255, 255, 255, 255, 68); + if (mast->adr.type == NA_IPV6) + str = va("%c%c%c%cgetserversExt %u empty full ipv6\n", 255, 255, 255, 255, 68); + else + str = va("%c%c%c%cgetservers %u empty full\n", 255, 255, 255, 255, 68); NET_SendPollPacket (strlen(str), str, mast->adr); } break; #endif #ifdef Q2CLIENT case MP_QUAKE2: - NET_SendPollPacket (6, "query", mast->adr); + if (mast->adr.type == NA_IP) //qw masters have no ipx/ipv6 reporting, so its a bit pointless + NET_SendPollPacket (6, "query", mast->adr); break; #endif case MP_QUAKEWORLD: - NET_SendPollPacket (3, "c\n", mast->adr); + if (mast->adr.type == NA_IP) //qw masters have no ipx/ipv6 reporting, so its a bit pointless + NET_SendPollPacket (3, "c\n", mast->adr); break; #ifdef NQPROT case MP_NETQUAKE: @@ -2573,9 +2604,9 @@ void MasterInfo_Request(master_t *mast) //for compat with dp, we use the nq netchan version. which is stupid, but whatever //we ask for ipv6 addresses from ipv6 masters (assuming it resolved okay) if (mast->adr.type == NA_IPV6) - str = va("%c%c%c%cgetserversExt %s %u empty full ipv6"/*\x0A\n"*/, 255, 255, 255, 255, game, com_protocolversion.ival); + str = va("%c%c%c%cgetserversExt %s %u empty full ipv6", 255, 255, 255, 255, game, com_protocolversion.ival); else - str = va("%c%c%c%cgetservers %s %u empty full"/*\x0A\n"*/, 255, 255, 255, 255, game, com_protocolversion.ival); + str = va("%c%c%c%cgetservers %s %u empty full", 255, 255, 255, 255, game, com_protocolversion.ival); NET_SendPollPacket (strlen(str), str, mast->adr); } } @@ -3619,7 +3650,7 @@ void Net_Master_Init(void) int i; for (i = 0; net_masterlist[i].cv.name; i++) Cvar_Register(&net_masterlist[i].cv, "master servers"); -#ifdef HAVE_SERVER +#if defined(HAVE_SERVER) && !defined(NOLEGACY) Cmd_AddCommand ("setmaster", SV_SetMaster_f); #endif diff --git a/engine/client/p_script.c b/engine/client/p_script.c index 6badb3f72..7dc90e3b5 100644 --- a/engine/client/p_script.c +++ b/engine/client/p_script.c @@ -261,7 +261,7 @@ typedef struct part_type_s { float rotationmin, rotationrand; float scaledelta; - int countextra; + float countextra; float count; float countrand; float countspacing; //for trails. @@ -2406,7 +2406,7 @@ qboolean PScript_Query(int typenum, int body, char *outstr, int outstrlen) } if (ptype->count || ptype->countrand || ptype->countextra || all) - Q_strncatz(outstr, va("count %g %g %i\n", ptype->count, ptype->countrand, ptype->countextra), outstrlen); + Q_strncatz(outstr, va("count %g %g %g\n", ptype->count, ptype->countrand, ptype->countextra), outstrlen); if (ptype->rainfrequency != 1 || all) Q_strncatz(outstr, va("rainfrequency %g\n", ptype->rainfrequency), outstrlen); @@ -2829,6 +2829,7 @@ static void FinishEffectinfoParticleType(part_type_t *ptype, qboolean blooddecal { //fte's textured particles are *0.25 for some reason. //but fte also uses radiuses, while dp uses total size so we only need to double it here.. + ptype->looks.stretch = 1;//affects billboarding in dp ptype->scale *= 2*ptype->looks.stretch; ptype->scalerand *= 2*ptype->looks.stretch; ptype->scaledelta *= 2*2*ptype->looks.stretch; //fixme: this feels wrong, the results look correct though. hrmph. @@ -2838,9 +2839,12 @@ static void FinishEffectinfoParticleType(part_type_t *ptype, qboolean blooddecal ptype->clipbounce = -2; if (ptype->looks.type == PT_TEXTUREDSPARK) { + ptype->scale *= 2; + ptype->scalerand *= 2; + ptype->scaledelta *= 2; ptype->looks.stretch *= 0.04; - if (ptype->looks.stretch < 0) - ptype->looks.stretch = 0.000001; +// if (ptype->looks.stretch < 0) +// ptype->looks.stretch = 0.000001; } if (ptype->die == 9999) //internal: means unspecified. @@ -5572,31 +5576,18 @@ static void P_ParticleTrailSpawn (vec3_t startpos, vec3_t end, part_type_t *ptyp len = VectorNormalize (vec); // use ptype step to calc step vector and step size - if (ptype->countspacing) - { - step = ptype->countspacing; //particles per qu - step /= r_part_density.value; //scaled... - if (ptype->countextra) - { - count = ptype->countextra; - if (step>0) - count += len/step; - step = len/count; - } - } - else - { - step = ptype->count * r_part_density.value * timeinterval; - step += ptype->countextra; //particles per frame - step += ptype->countoverflow; - count = (int)step; - ptype->countoverflow = step-count; //the part that we're forgetting, to add to the next frame... - if (count<=0) - return; - else - step = len/count; //particles per second - } + //(fractional) extra count + step = (cl.paused&&(ptype->die||ptype->randdie))?0:ptype->countextra; + step += ptype->count * r_part_density.value * timeinterval; + + //round it with overflow tracking + step += ptype->countoverflow; + count = step; + ptype->countoverflow = step-count; + + step = ptype->countspacing; //particles per qu + step /= r_part_density.value; //scaled... if (ptype->flags & PT_AVERAGETRAIL) { @@ -5610,7 +5601,36 @@ static void P_ParticleTrailSpawn (vec3_t startpos, vec3_t end, part_type_t *ptyp VectorScale(vec, step, vstep); - // add offset + // store last stop here for lack of a better solution besides vectors + if (ts) + { + ts->state2.laststop = stop = ts->state2.laststop + len; //when to stop + len = ts->state1.lastdist; + } + else + { + stop = len; + len = 0; + } + + if (step && len < stop) + { + if (count && len) + { + //recalculate step to cover the entire distance. + count += (stop-len) / step; + step = (stop-len)/count; + } + else + count += (stop-len) / step; + } + else + { + step = 0; + VectorClear(vstep); + } + +// add offset // VectorAdd(start, ptype->orgbias, start); // spawn mode precalculations @@ -5628,22 +5648,6 @@ static void P_ParticleTrailSpawn (vec3_t startpos, vec3_t end, part_type_t *ptyp VectorVectors(vec, right, up); } - // store last stop here for lack of a better solution besides vectors - if (ts) - { - ts->state2.laststop = stop = ts->state2.laststop + len; //when to stop - len = ts->state1.lastdist; - } - else - { - stop = len; - len = 0; - } - -// len = ts->lastdist/step; -// len = (len - (int)len)*step; -// VectorMA (start, -len, vec, start); - if (ptype->flags & PT_NOSPREADFIRST) nrfirst = len + step*1.5; else @@ -5656,16 +5660,6 @@ static void P_ParticleTrailSpawn (vec3_t startpos, vec3_t end, part_type_t *ptyp b = bfirst = NULL; - if (len < stop) - count = (stop-len) / step; - else - { - count = 0; - step = 0; - VectorClear(vstep); - } -// count += ptype->countextra; - while (count-->0)//len < stop) { len += step; @@ -6316,8 +6310,10 @@ static void R_AddTSparkParticle(scenetris_t *t, particle_t *p, plooks_t *type) VectorSubtract(r_refdef.vieworg, o2, v); CrossProduct(v, p->vel, cr); VectorNormalize(cr); - VectorMA(o2, -p->scale/2, cr, cl_strisvertv[cl_numstrisvert+0]); - VectorMA(o2, p->scale/2, cr, cl_strisvertv[cl_numstrisvert+1]); + + halfscale = fabs(p->scale); //gah, I hate dp. + VectorMA(o2, -halfscale/2, cr, cl_strisvertv[cl_numstrisvert+0]); + VectorMA(o2, halfscale/2, cr, cl_strisvertv[cl_numstrisvert+1]); VectorMA(p->org, length, movedir, o2); } @@ -6802,11 +6798,21 @@ static void PScript_DrawParticleTypes (void) if (r_plooksdirty) { int i, j; + { + particleengine_t *tmp = fallback; fallback = NULL; - pe_default = PScript_FindParticleType("PE_DEFAULT"); - pe_size2 = PScript_FindParticleType("PE_SIZE2"); - pe_size3 = PScript_FindParticleType("PE_SIZE3"); - pe_defaulttrail = PScript_FindParticleType("PE_DEFAULTTRAIL"); + pe_default = PScript_FindParticleType("PE_DEFAULT"); + pe_size2 = PScript_FindParticleType("PE_SIZE2"); + pe_size3 = PScript_FindParticleType("PE_SIZE3"); + pe_defaulttrail = PScript_FindParticleType("PE_DEFAULTTRAIL"); + + if (pe_default == P_INVALID) + { + //pe_default = PScript_FindParticleType("SVC_PARTICLE"); + } + + fallback = tmp; + } for (i = 0; i < numparticletypes; i++) { diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index 384ba0f5e..2ef722586 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -1713,8 +1713,8 @@ void QCBUILTIN PF_R_AddTrisoup_Simple(pubprogfuncs_t *prinst, struct globalvars_ qboolean twod = qcflags & DRAWFLAG_2D; unsigned int beflags; unsigned int numverts; - qcvertex_t *vert; - unsigned int *idx; + const qcvertex_t *fte_restrict vert; + const unsigned int *fte_restrict idx; unsigned int i, j, first; if ((qcflags & 3) == DRAWFLAG_ADD) @@ -1728,10 +1728,14 @@ void QCBUILTIN PF_R_AddTrisoup_Simple(pubprogfuncs_t *prinst, struct globalvars_ if (qcflags & DRAWFLAG_LINES) beflags |= BEF_LINES; - if (twod) + if (1)//twod) shader = R_RegisterPic(PR_GetStringOfs(prinst, OFS_PARM0), NULL); else + { shader = R_RegisterSkin(PR_GetStringOfs(prinst, OFS_PARM0), NULL); + if (!shader->defaulttextures->base && (shader->flags & SHADER_HASDIFFUSE)) + R_BuildDefaultTexnums(NULL, shader, 0); + } if (R2D_Flush && (R2D_Flush != CSQC_PolyFlush || csqc_poly_shader != shader || csqc_poly_flags != beflags || csqc_poly_2d != twod)) R2D_Flush(); @@ -1748,13 +1752,13 @@ void QCBUILTIN PF_R_AddTrisoup_Simple(pubprogfuncs_t *prinst, struct globalvars_ PR_BIError(prinst, "PF_R_AddTrisoup: invalid vertexes pointer\n"); return; } - vert = (qcvertex_t*)(prinst->stringtable + vertsptr); + vert = (const qcvertex_t*)(prinst->stringtable + vertsptr); if (indexesptr <= 0 || indexesptr+numindexes*sizeof(int) > prinst->stringtablesize) { PR_BIError(prinst, "PF_R_AddTrisoup: invalid indexes pointer\n"); return; } - idx = (int*)(prinst->stringtable + indexesptr); + idx = (const int*)(prinst->stringtable + indexesptr); first = cl_numstrisvert - csqc_poly_origvert; if (first + numindexes > MAX_INDICIES) diff --git a/engine/client/r_partset.c b/engine/client/r_partset.c index 56b06e278..817bfb579 100644 --- a/engine/client/r_partset.c +++ b/engine/client/r_partset.c @@ -1522,6 +1522,22 @@ char *particle_set_high = "assoc gunshotsmoke\n" "}\n" +//simple slight trail, to show movement more than anything else. +"r_part tr_spike\n" +"{\n" +"texture \"particles/fteparticlefont.tga\"\n" +"step 1.1\n" +"scale 4\n" +"die .4\n" +"rgb 20 20 20\n" +"alpha 1\n" +"blend add\n" +"spawnmode spiral 512\n" +"spawnvel 10\n" +"}\n" +"r_trail \"progs/spike.mdl\" tr_spike\n" +"r_trail \"progs/s_spike.mdl\" tr_spike\n" + //////////////////////////////////////////////// //explosion diff --git a/engine/client/render.h b/engine/client/render.h index 4523f984e..ffaa819c2 100644 --- a/engine/client/render.h +++ b/engine/client/render.h @@ -443,6 +443,7 @@ enum imageflags #define R_LoadTextureFB(id,w,h,d,f) Image_GetTexture(id, NULL, f, d, NULL, w, h, TF_TRANS8_FULLBRIGHT) #define R_LoadTexture(id,w,h,fmt,d,fl) Image_GetTexture(id, NULL, fl, d, NULL, w, h, fmt) +image_t *Image_TextureIsValid(qintptr_t address); image_t *Image_FindTexture (const char *identifier, const char *subpath, unsigned int flags); image_t *Image_CreateTexture(const char *identifier, const char *subpath, unsigned int flags); image_t *QDECL Image_GetTexture (const char *identifier, const char *subpath, unsigned int flags, void *fallbackdata, void *fallbackpalette, int fallbackwidth, int fallbackheight, uploadfmt_t fallbackfmt); @@ -583,7 +584,7 @@ void WritePCXfile (const char *filename, enum fs_relative fsroot, qbyte *data, i qbyte *ReadPCXFile(qbyte *buf, int length, int *width, int *height); qbyte *ReadTargaFile(qbyte *buf, int length, int *width, int *height, uploadfmt_t *format, qboolean greyonly, uploadfmt_t forceformat); qbyte *ReadJPEGFile(qbyte *infile, int length, int *width, int *height); -qbyte *ReadPNGFile(qbyte *buf, int length, int *width, int *height, const char *name); +qbyte *ReadPNGFile(const char *fname, qbyte *buf, int length, int *width, int *height, uploadfmt_t *format); qbyte *ReadPCXPalette(qbyte *buf, int len, qbyte *out); void Image_ResampleTexture (unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight); void Image_ResampleTexture8 (unsigned char *in, int inwidth, int inheight, unsigned char *out, int outwidth, int outheight); @@ -646,7 +647,7 @@ extern cvar_t gl_poly; extern cvar_t gl_affinemodels; extern cvar_t r_renderscale; extern cvar_t gl_nohwblend; -extern cvar_t r_coronas, r_coronas_occlusion, r_coronas_mindist, r_coronas_fadedist, r_flashblend, r_flashblendscale; +extern cvar_t r_coronas, r_coronas_intensity, r_coronas_occlusion, r_coronas_mindist, r_coronas_fadedist, r_flashblend, r_flashblendscale; extern cvar_t r_lightstylesmooth; extern cvar_t r_lightstylesmooth_limit; extern cvar_t r_lightstylespeed; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index b7420d1aa..de6783c5b 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -143,6 +143,7 @@ cvar_t r_skin_overlays = CVARF ("r_skin_overlays", "1", cvar_t r_globalskin_first = CVARFD ("r_globalskin_first", "100", CVAR_RENDERERLATCH, "Specifies the first .skin value that is a global skin. Entities within this range will use the shader/image called 'gfx/skinSKIN.lmp' instead of their regular skin. See also: r_globalskin_count."); cvar_t r_globalskin_count = CVARFD ("r_globalskin_count", "10", CVAR_RENDERERLATCH, "Specifies how many globalskins there are."); cvar_t r_coronas = CVARFD ("r_coronas", "0", CVAR_ARCHIVE, "Draw coronas on realtime lights. Overrides glquake-esque flashblends."); +cvar_t r_coronas_intensity = CVARFD ("r_coronas_intensity", "1", CVAR_ARCHIVE, "Alternative intensity multiplier for coronas."); cvar_t r_coronas_occlusion = CVARFD ("r_coronas_occlusion", "", CVAR_ARCHIVE, "Specifies that coronas should be occluded more carefully.\n0: No occlusion, at all.\n1: BSP occlusion only (simple tracelines).\n2: non-bsp occlusion also (complex tracelines).\n3: Depthbuffer reads (forces synchronisation).\n4: occlusion queries."); cvar_t r_coronas_mindist = CVARFD ("r_coronas_mindist", "128", CVAR_ARCHIVE, "Coronas closer than this will be invisible, preventing near clip plane issues."); cvar_t r_coronas_fadedist = CVARFD ("r_coronas_fadedist", "256", CVAR_ARCHIVE, "Coronas will fade out over this distance."); @@ -230,7 +231,7 @@ cvar_t scr_conalpha = CVARC ("scr_conalpha", "0.7", Cvar_Limiter_ZeroToOne_Callback); cvar_t scr_consize = CVAR ("scr_consize", "0.5"); cvar_t scr_conspeed = CVAR ("scr_conspeed", "2000"); -cvar_t scr_fov_mode = CVARFD ("scr_fov_mode", "0", CVAR_ARCHIVE, "Controls what the fov cvar actually controls:\n0: largest axis (ultra-wide monitors means less height will be visible).\n1: smallest axis (ultra-wide monitors will distort at the edges).\n2: horizontal axis.\n3: vertical axis."); +cvar_t scr_fov_mode = CVARFD ("scr_fov_mode", "0", CVAR_ARCHIVE, "Controls what the fov cvar actually controls:\n0: largest axis (ultra-wide monitors means less height will be visible).\n1: smallest axis (ultra-wide monitors will distort at the edges).\n2: horizontal axis.\n3: vertical axis.\n4: horizontally-padded 4:3"); cvar_t scr_fov = CVARFCD("fov", "90", CVAR_ARCHIVE, SCR_Fov_Callback, "field of vision, 1-170 degrees, standard fov is 90, nquake defaults to 108."); cvar_t scr_fov_viewmodel = CVARFD("r_viewmodel_fov", "", CVAR_ARCHIVE, @@ -870,6 +871,7 @@ void Renderer_Init(void) Cvar_Register(&r_stainfadeammount, GRAPHICALNICETIES); Cvar_Register(&r_lightprepass_cvar, GLRENDEREROPTIONS); Cvar_Register (&r_coronas, GRAPHICALNICETIES); + Cvar_Register (&r_coronas_intensity, GRAPHICALNICETIES); Cvar_Register (&r_coronas_occlusion, GRAPHICALNICETIES); Cvar_Register (&r_coronas_mindist, GRAPHICALNICETIES); Cvar_Register (&r_coronas_fadedist, GRAPHICALNICETIES); diff --git a/engine/client/screen.h b/engine/client/screen.h index 13d9a3fb0..e93db8eca 100644 --- a/engine/client/screen.h +++ b/engine/client/screen.h @@ -104,10 +104,14 @@ typedef enum uploadfmt PTI_L8_SRGB, //8bit format. luminance gets flooded to all RGB channels. might be supported using swizzles. PTI_L8A8_SRGB, //16bit format. L=luminance. note: this cannot be implemented as a swizzle as there's no way to get srgb on red without it on green. //small formats. - PTI_R8, //used for paletted data + PTI_P8, //used for paletted data. Loaded as R8, but separate purely due to mipmap generation. should probably make a mipgen enum. + PTI_R8, //used for greyscale data (that doesn't need to get expanded to rgb). PTI_RG8, //might be useful for normalmaps PTI_R8_SNORM, PTI_RG8_SNORM, //might be useful for normalmaps + //big formats + PTI_R16, + PTI_RGBA16, //floating point formats PTI_R16F, PTI_R32F, @@ -188,8 +192,8 @@ typedef enum uploadfmt //non-native formats (generally requiring weird palettes that are not supported by hardware) TF_BGR24_FLIP, /*bgr byte order, no alpha channel nor pad, and bottom up*/ - TF_MIP4_R8, /*8bit 4-mip greyscale image*/ - TF_MIP4_SOLID8, /*8bit 4-mip image in default palette*/ + TF_MIP4_P8, /*8bit 4-mip image in default palette, that will be loaded as an R8 texture.*/ + TF_MIP4_SOLID8, /*8bit 4-mip image in default palette, that will be expanded to an RGB texture.*/ TF_MIP4_8PAL24, /*8bit 4-mip image with included palette*/ TF_MIP4_8PAL24_T255,/*8bit 4-mip image with included palette where index 255 is alpha 0*/ TF_SOLID8, /*8bit quake-palette image*/ @@ -227,11 +231,9 @@ typedef enum uploadfmt TF_BGRX32 = PTI_BGRX8, /*rgb byte order, with extra wasted byte after blue*/ TF_RGB24 = PTI_RGB8, /*rgb byte order, no alpha channel nor pad, and regular top down*/ TF_BGR24 = PTI_BGR8, /*bgr byte order, no alpha channel nor pad, and regular top down*/ - TF_LUM8 = PTI_L8, - TF_R8 = PTI_R8 //these are emulated formats. this 'case' value allows drivers to easily ignore them -#define PTI_EMULATED TF_INVALID:case TF_BGR24_FLIP:case TF_MIP4_R8:case TF_MIP4_SOLID8:case TF_MIP4_8PAL24:case TF_MIP4_8PAL24_T255:case TF_SOLID8:case TF_TRANS8:case TF_TRANS8_FULLBRIGHT:case TF_HEIGHT8:case TF_HEIGHT8PAL:case TF_H2_T7G1:case TF_H2_TRANS8_0:case TF_H2_T4A4:case TF_8PAL24:case TF_8PAL32:case PTI_LLLX8:case PTI_LLLA8 +#define PTI_EMULATED TF_INVALID:case TF_BGR24_FLIP:case TF_MIP4_P8:case TF_MIP4_SOLID8:case TF_MIP4_8PAL24:case TF_MIP4_8PAL24_T255:case TF_SOLID8:case TF_TRANS8:case TF_TRANS8_FULLBRIGHT:case TF_HEIGHT8:case TF_HEIGHT8PAL:case TF_H2_T7G1:case TF_H2_TRANS8_0:case TF_H2_T4A4:case TF_8PAL24:case TF_8PAL32:case PTI_LLLX8:case PTI_LLLA8 } uploadfmt_t; qboolean SCR_ScreenShot (char *filename, enum fs_relative fsroot, void **buffer, int numbuffers, int bytestride, int width, int height, enum uploadfmt fmt, qboolean writemeta); diff --git a/engine/client/sys_win.c b/engine/client/sys_win.c index 8d1e5c476..1cad7092b 100644 --- a/engine/client/sys_win.c +++ b/engine/client/sys_win.c @@ -1593,6 +1593,9 @@ void Sys_Init (void) vinfo.dwOSVersionInfoSize = sizeof(vinfo); +#if _MSC_VER >= 1600 //msvc2010 runtime does not work on 9x any more. get rid of the deprecation warnings in later versions. + WinNT = true; +#else if (!GetVersionEx (&vinfo)) Sys_Error ("Couldn't get OS info"); @@ -1606,6 +1609,7 @@ void Sys_Init (void) WinNT = true; else WinNT = false; +#endif qwinvermaj = vinfo.dwMajorVersion; qwinvermin = vinfo.dwMinorVersion; @@ -3767,7 +3771,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin WinNT = vinfo.dwPlatformId == VER_PLATFORM_WIN32_NT; } -#if defined(_DEBUG) && defined(MULTITHREAD) +#if defined(_DEBUG) && defined(_MSC_VER) && defined(MULTITHREAD) Sys_SetThreadName(-1, "main thread"); #endif diff --git a/engine/client/view.c b/engine/client/view.c index c173e591a..16757689b 100644 --- a/engine/client/view.c +++ b/engine/client/view.c @@ -1257,6 +1257,13 @@ restart: *y = CalcFov(afov, w, h); } break; + + case 4: //wide 4:3 + *y = tan(scr_fov.value * M_PI / 360.0) * (3.0 / 4.0); + *x = *y * (float)w / (float)h; + *x = atan(*x) * (360.0 / M_PI); + *y = atan(*y) * (360.0 / M_PI); + break; } } void V_ApplyAFov(playerview_t *pv) diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 184164338..52ea0c931 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -2700,13 +2700,14 @@ A complete command line has been parsed, so try to execute it FIXME: lookupnoadd the token to speed search? ============ */ -void Cmd_ExecuteString (const char *text, int level) +static void Cmd_ExecuteStringGlobalsAreEvil (const char *text, int level) { //WARNING: PF_checkcommand should match the order. cmd_function_t *cmd; cmdalias_t *a; char dest[8192]; + Cmd_ExecLevel = level; while (*text == ' ' || *text == '\n') text++; @@ -2764,12 +2765,7 @@ void Cmd_ExecuteString (const char *text, int level) Cmd_ForwardToServer (); } else - { - int olev = Cmd_ExecLevel; - Cmd_ExecLevel = level; cmd->function (); - Cmd_ExecLevel = olev; - } return; } } @@ -2875,7 +2871,6 @@ void Cmd_ExecuteString (const char *text, int level) { if (!strcmp(cmd_argv[0], tpcmds[level])) { - int olev = Cmd_ExecLevel; if (cmd->restriction && cmd->restriction > 0) { //warning, these commands would normally be considered to be run at restrict_local, but they're running at a much lower level //which means that if there's ANY restriction on them then they'll fail. @@ -2888,7 +2883,6 @@ void Cmd_ExecuteString (const char *text, int level) Cmd_ForwardToServer (); else cmd->function(); - Cmd_ExecLevel = olev; return; } } @@ -2904,13 +2898,7 @@ void Cmd_ExecuteString (const char *text, int level) else if (!cmd->function) Cmd_ForwardToServer (); else - { - int olev = Cmd_ExecLevel; - Cmd_ExecLevel = level; cmd->function (); - Cmd_ExecLevel = olev; - } - return; } @@ -2962,7 +2950,14 @@ void Cmd_ExecuteString (const char *text, int level) if ((cl_warncmd.value && level <= RESTRICT_LOCAL) || developer.value) Con_TPrintf ("Unknown command \"%s\"\n", Cmd_Argv(0)); } - +void Cmd_ExecuteString (const char *text, int level) +{ //inserted a small wrapper due to all the returns in the original function. + //a number of things check for seats if nothing else, and security says is safer to do this than to be in doubt. + int olev = Cmd_ExecLevel; + Cmd_ExecuteStringGlobalsAreEvil(text, level); + Cmd_ExecLevel = level; + Cmd_ExecLevel = olev; +} /* diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index dcca07ead..cde4cb530 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -1224,6 +1224,7 @@ static const float *Alias_GetBoneInformation(galiasinfo_t *inf, framestate_t *fr size_t numgroups; size_t bone, endbone; + lerps[0].skeltype = SKEL_IDENTITY; //just in case. #ifdef SKELETALOBJECTS if (framestate->bonestate && framestate->bonecount >= inf->numbones) { @@ -1613,7 +1614,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in if (!inf->numanimations) { #ifdef SKELETALMODELS - if (inf->ofs_skel_xyz && !inf->ofs_skel_weight) + if (inf->ofs_skel_xyz) {} else #endif @@ -2015,11 +2016,12 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, in #ifndef SERVERONLY //used by the modelviewer. -void Mod_AddSingleSurface(entity_t *ent, int surfaceidx, shader_t *shader) +void Mod_AddSingleSurface(entity_t *ent, int surfaceidx, shader_t *shader, qboolean normals) { galiasinfo_t *mod; scenetris_t *t; vecV_t *posedata = NULL; + vec3_t *normdata = NULL; int surfnum = 0, i; #ifdef SKELETALMODELS int cursurfnum = -1; @@ -2040,6 +2042,7 @@ void Mod_AddSingleSurface(entity_t *ent, int surfaceidx, shader_t *shader) continue; #ifdef SKELETALMODELS + normdata = mod->ofs_skel_norm; if (mod->numbones) { if (!mod->ofs_skel_idx) @@ -2081,63 +2084,116 @@ void Mod_AddSingleSurface(entity_t *ent, int surfaceidx, shader_t *shader) } #endif - if (cl_numstris == cl_maxstris) - { - cl_maxstris+=8; - cl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris); - } - t = &cl_stris[cl_numstris++]; - t->shader = shader; - t->flags = 0;//BEF_LINES; - t->firstidx = cl_numstrisidx; - t->firstvert = cl_numstrisvert; - if (t->flags&BEF_LINES) - t->numidx = mod->numindexes*2; - else - t->numidx = mod->numindexes; - t->numvert = mod->numverts; - if (cl_numstrisidx+t->numidx > cl_maxstrisidx) - { - cl_maxstrisidx=cl_numstrisidx+t->numidx; - cl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx); - } - if (cl_numstrisvert+mod->numverts > cl_maxstrisvert) - { - cl_maxstrisvert=cl_numstrisvert+mod->numverts; - cl_strisvertv = BZ_Realloc(cl_strisvertv, sizeof(*cl_strisvertv)*cl_maxstrisvert); - cl_strisvertt = BZ_Realloc(cl_strisvertt, sizeof(*cl_strisvertt)*cl_maxstrisvert); - cl_strisvertc = BZ_Realloc(cl_strisvertc, sizeof(*cl_strisvertc)*cl_maxstrisvert); - } - for (i = 0; i < mod->numverts; i++) - { - VectorMA(ent->origin, posedata[i][0], ent->axis[0], cl_strisvertv[t->firstvert+i]); - VectorMA(cl_strisvertv[t->firstvert+i], posedata[i][1], ent->axis[1], cl_strisvertv[t->firstvert+i]); - VectorMA(cl_strisvertv[t->firstvert+i], posedata[i][2], ent->axis[2], cl_strisvertv[t->firstvert+i]); -// VectorAdd(ent->origin, posedata[i], cl_strisvertv[t->firstvert+i]); - - Vector2Set(cl_strisvertt[t->firstvert+i], 0.5, 0.5); - Vector4Set(cl_strisvertc[t->firstvert+i], (mod->contents?1:0), 1, 1, 0.1); - } - if (t->flags&BEF_LINES) - { - for (i = 0; i < mod->numindexes; i+=3) + if (normals && normdata) + { //pegs, one on each vertex. + vec3_t push; + if (cl_numstris == cl_maxstris) { - cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+0]; - cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+1]; - cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+1]; - cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+2]; - cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+2]; - cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+0]; + cl_maxstris+=8; + cl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris); } + t = &cl_stris[cl_numstris++]; + t->shader = shader; + t->flags = BEF_LINES; + t->firstidx = cl_numstrisidx; + t->firstvert = cl_numstrisvert; + t->numidx = t->numvert = mod->numverts*2; + + if (cl_numstrisidx+t->numidx > cl_maxstrisidx) + { + cl_maxstrisidx=cl_numstrisidx+t->numidx; + cl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx); + } + if (cl_numstrisvert+t->numvert > cl_maxstrisvert) + { + cl_maxstrisvert=cl_numstrisvert+t->numvert; + cl_strisvertv = BZ_Realloc(cl_strisvertv, sizeof(*cl_strisvertv)*cl_maxstrisvert); + cl_strisvertt = BZ_Realloc(cl_strisvertt, sizeof(*cl_strisvertt)*cl_maxstrisvert); + cl_strisvertc = BZ_Realloc(cl_strisvertc, sizeof(*cl_strisvertc)*cl_maxstrisvert); + } + for (i = 0; i < mod->numverts; i++) + { + VectorMA(ent->origin, posedata[i][0], ent->axis[0], cl_strisvertv[t->firstvert+i*2+0]); + VectorMA(cl_strisvertv[t->firstvert+i*2+0], posedata[i][1], ent->axis[1], cl_strisvertv[t->firstvert+i*2+0]); + VectorMA(cl_strisvertv[t->firstvert+i*2+0], posedata[i][2], ent->axis[2], cl_strisvertv[t->firstvert+i*2+0]); + + VectorMA(posedata[i], 0.1, normdata[i], push); + VectorMA(ent->origin, push[0], ent->axis[0], cl_strisvertv[t->firstvert+i*2+1]); + VectorMA(cl_strisvertv[t->firstvert+i*2+1], push[1], ent->axis[1], cl_strisvertv[t->firstvert+i*2+1]); + VectorMA(cl_strisvertv[t->firstvert+i*2+1], push[2], ent->axis[2], cl_strisvertv[t->firstvert+i*2+1]); + + Vector2Set(cl_strisvertt[t->firstvert+i*2+0], 0.0, 0.0); + Vector2Set(cl_strisvertt[t->firstvert+i*2+1], 1.0, 1.0); + Vector4Set(cl_strisvertc[t->firstvert+i*2+0], 0, 0, 1, 1); + Vector4Set(cl_strisvertc[t->firstvert+i*2+1], 0, 0, 1, 1); + + cl_strisidx[cl_numstrisidx+i*2+0] = i*2+0; + cl_strisidx[cl_numstrisidx+i*2+1] = i*2+1; + } + cl_numstrisidx += i*2; + cl_numstrisvert += i*2; } else { - for (i = 0; i < mod->numindexes; i++) - cl_strisidx[cl_numstrisidx+i] = mod->ofs_indexes[i]; - cl_numstrisidx += mod->numindexes; + if (cl_numstris == cl_maxstris) + { + cl_maxstris+=8; + cl_stris = BZ_Realloc(cl_stris, sizeof(*cl_stris)*cl_maxstris); + } + t = &cl_stris[cl_numstris++]; + t->shader = shader; + t->flags = 0;//BEF_LINES; + t->firstidx = cl_numstrisidx; + t->firstvert = cl_numstrisvert; + if (t->flags&BEF_LINES) + t->numidx = mod->numindexes*2; + else + t->numidx = mod->numindexes; + t->numvert = mod->numverts; + + if (cl_numstrisidx+t->numidx > cl_maxstrisidx) + { + cl_maxstrisidx=cl_numstrisidx+t->numidx; + cl_strisidx = BZ_Realloc(cl_strisidx, sizeof(*cl_strisidx)*cl_maxstrisidx); + } + if (cl_numstrisvert+mod->numverts > cl_maxstrisvert) + { + cl_maxstrisvert=cl_numstrisvert+mod->numverts; + cl_strisvertv = BZ_Realloc(cl_strisvertv, sizeof(*cl_strisvertv)*cl_maxstrisvert); + cl_strisvertt = BZ_Realloc(cl_strisvertt, sizeof(*cl_strisvertt)*cl_maxstrisvert); + cl_strisvertc = BZ_Realloc(cl_strisvertc, sizeof(*cl_strisvertc)*cl_maxstrisvert); + } + for (i = 0; i < mod->numverts; i++) + { + VectorMA(ent->origin, posedata[i][0], ent->axis[0], cl_strisvertv[t->firstvert+i]); + VectorMA(cl_strisvertv[t->firstvert+i], posedata[i][1], ent->axis[1], cl_strisvertv[t->firstvert+i]); + VectorMA(cl_strisvertv[t->firstvert+i], posedata[i][2], ent->axis[2], cl_strisvertv[t->firstvert+i]); + // VectorAdd(ent->origin, posedata[i], cl_strisvertv[t->firstvert+i]); + + Vector2Set(cl_strisvertt[t->firstvert+i], 0.5, 0.5); + Vector4Set(cl_strisvertc[t->firstvert+i], (mod->contents?1:0), 1, 1, 0.1); + } + if (t->flags&BEF_LINES) + { + for (i = 0; i < mod->numindexes; i+=3) + { + cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+0]; + cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+1]; + cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+1]; + cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+2]; + cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+2]; + cl_strisidx[cl_numstrisidx++] = mod->ofs_indexes[i+0]; + } + } + else + { + for (i = 0; i < mod->numindexes; i++) + cl_strisidx[cl_numstrisidx+i] = mod->ofs_indexes[i]; + cl_numstrisidx += mod->numindexes; + } + cl_numstrisvert += mod->numverts; } - cl_numstrisvert += mod->numverts; } } #endif diff --git a/engine/common/com_mesh.h b/engine/common/com_mesh.h index 51220cccf..adb89b22e 100644 --- a/engine/common/com_mesh.h +++ b/engine/common/com_mesh.h @@ -170,6 +170,9 @@ typedef struct galiasinfo_s struct galiasinfo_s *nextsurf; #ifdef SKELETALMODELS +// int *bonemap; //some models are horribly complicated, this provides a gpubone->cpubone table, reducing the number of gpu bones needed on a per-mesh basis. +// int mappedbones; + float *baseframeofs; /*non-heirachical*/ int numbones; galiasbone_t *ofsbones; diff --git a/engine/common/common.c b/engine/common/common.c index 9a34ef4c8..f0dd7e259 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -4298,7 +4298,7 @@ skipwhite: data++; while (1) { - if (len >= TOKENSIZE-1) + if (len >= tokenlen-1) { token[len] = '\0'; return (char*)data; @@ -4328,7 +4328,7 @@ skipwhite: // parse a regular word do { - if (len >= TOKENSIZE-1) + if (len >= tokenlen-1) break; token[len] = c; data++; diff --git a/engine/common/common.h b/engine/common/common.h index fed8a6456..65c581ae9 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -97,8 +97,10 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifdef _MSC_VER #if _MSC_VER >= 1310 #define strtoull _strtoui64 + #define strtoll _strtoi64 #else #define strtoull strtoul //hopefully this won't cause too many issues + #define strtoll strtol //hopefully this won't cause too many issues #define DWORD_PTR DWORD //32bit only #define ULONG_PTR ULONG #endif diff --git a/engine/common/config_fteqw.h b/engine/common/config_fteqw.h index f99c87ad4..a3d6631ed 100644 --- a/engine/common/config_fteqw.h +++ b/engine/common/config_fteqw.h @@ -88,6 +88,9 @@ //Image formats #define IMAGEFMT_KTX //Khronos TeXture. common on gles3 devices for etc2 compression #define IMAGEFMT_PKM //file format generally written by etcpack or android's etc1tool. doesn't support mips. +#define IMAGEFMT_PBM //pbm/ppm/pgm/pfm family formats. +#define IMAGEFMT_PSD //baselayer only. +#define IMAGEFMT_HDR //an RGBE format. #define IMAGEFMT_DDS //.dds files embed mipmaps and texture compression. faster to load. //#define IMAGEFMT_BLP //legacy crap #define IMAGEFMT_BMP //windows bmp. yuck. also includes .ico for the luls diff --git a/engine/common/fs.c b/engine/common/fs.c index 51e45bcc2..02f0e57aa 100644 --- a/engine/common/fs.c +++ b/engine/common/fs.c @@ -807,7 +807,9 @@ static int QDECL COM_Dir_List(const char *name, qofs_t size, time_t mtime, void Q_snprintfz(link, sizeof(link), "\\tip\\Change Map\\map\\%s", name+5); colour = "^4"; //disconnects } - else if (!Q_strcasecmp(link, "bsp") || !Q_strcasecmp(link, "spr") || !Q_strcasecmp(link, "mdl") || !Q_strcasecmp(link, "md3") || !Q_strcasecmp(link, "iqm") || !Q_strcasecmp(link, "vvm") || !Q_strcasecmp(link, "psk") || !Q_strcasecmp(link, "dpm") || !Q_strcasecmp(link, "zym") || !Q_strcasecmp(link, "md5mesh") || !Q_strcasecmp(link, "md5anim") || !Q_strcasecmp(link, "gltf") || !Q_strcasecmp(link, "glb") || !Q_strcasecmp(link, "ase") || !Q_strcasecmp(link, "lwo")) + else if (!Q_strcasecmp(link, "bsp") || !Q_strcasecmp(link, "spr") || !Q_strcasecmp(link, "mdl") || !Q_strcasecmp(link, "md3") || !Q_strcasecmp(link, "iqm") || + !Q_strcasecmp(link, "vvm") || !Q_strcasecmp(link, "psk") || !Q_strcasecmp(link, "dpm") || !Q_strcasecmp(link, "zym") || !Q_strcasecmp(link, "md5mesh") || + !Q_strcasecmp(link, "md5anim") || !Q_strcasecmp(link, "gltf") || !Q_strcasecmp(link, "glb") || !Q_strcasecmp(link, "ase") || !Q_strcasecmp(link, "lwo")) Q_snprintfz(link, sizeof(link), "\\tip\\Open in Model Viewer\\modelviewer\\%s", name); else if (!Q_strcasecmp(link, "qc") || !Q_strcasecmp(link, "src") || !Q_strcasecmp(link, "qh") || !Q_strcasecmp(link, "h") || !Q_strcasecmp(link, "c") || !Q_strcasecmp(link, "cfg") || !Q_strcasecmp(link, "rc") @@ -818,7 +820,9 @@ static int QDECL COM_Dir_List(const char *name, qofs_t size, time_t mtime, void || !Q_strcasecmp(link, "vmt") ) Q_snprintfz(link, sizeof(link), "\\tip\\Open in Text Editor\\edit\\%s", name); - else if (!Q_strcasecmp(link, "tga") || !Q_strcasecmp(link, "png") || !Q_strcasecmp(link, "jpg") || !Q_strcasecmp(link, "jpeg") || !Q_strcasecmp(link, "lmp") || !Q_strcasecmp(link, "pcx") || !Q_strcasecmp(link, "bmp") || !Q_strcasecmp(link, "dds") || !Q_strcasecmp(link, "ktx") || !Q_strcasecmp(link, "vtf")) + else if (!Q_strcasecmp(link, "tga") || !Q_strcasecmp(link, "png") || !Q_strcasecmp(link, "jpg") || !Q_strcasecmp(link, "jpeg") || !Q_strcasecmp(link, "lmp") || !Q_strcasecmp(link, "ico") || + !Q_strcasecmp(link, "pcx") || !Q_strcasecmp(link, "bmp") || !Q_strcasecmp(link, "dds") || !Q_strcasecmp(link, "ktx") || !Q_strcasecmp(link, "vtf") || !Q_strcasecmp(link, "psd") || + !Q_strcasecmp(link, "pbm") || !Q_strcasecmp(link, "ppm") || !Q_strcasecmp(link, "pgm") || !Q_strcasecmp(link, "pam") || !Q_strcasecmp(link, "pfm") || !Q_strcasecmp(link, "hdr") ) { //FIXME: image replacements are getting in the way here. Q_snprintfz(link, sizeof(link), "\\tiprawimg\\%s\\tip\\(note: image replacement rules are context-dependant, including base path, sub path, extension, or complete replacement via a shader)", name); @@ -4514,7 +4518,7 @@ static qboolean FS_DirHasAPackage(char *basedir, ftemanifest_t *man) } //false stops the search (and returns that value to FS_DirHasGame) -int FS_DirDoesHaveGame(const char *fname, qofs_t fsize, time_t modtime, void *ctx, searchpathfuncs_t *subdir) +int QDECL FS_DirDoesHaveGame(const char *fname, qofs_t fsize, time_t modtime, void *ctx, searchpathfuncs_t *subdir) { return false; } diff --git a/engine/common/net.h b/engine/common/net.h index ba7c942d3..5c829a545 100644 --- a/engine/common/net.h +++ b/engine/common/net.h @@ -149,8 +149,9 @@ enum addressscope_e { ASCOPE_PROCESS=0, ASCOPE_HOST=1, - ASCOPE_LAN=2, - ASCOPE_NET=3 + ASCOPE_LINK=2, + ASCOPE_LAN=3, + ASCOPE_NET=4 }; enum addressscope_e NET_ClassifyAddress(netadr_t *adr, char **outdesc); diff --git a/engine/common/net_ssl_winsspi.c b/engine/common/net_ssl_winsspi.c index ba810174a..b6877afe1 100644 --- a/engine/common/net_ssl_winsspi.c +++ b/engine/common/net_ssl_winsspi.c @@ -39,8 +39,12 @@ cvar_t *tls_ignorecertificateerrors; #define SCH_CRED_SNI_CREDENTIAL 0x00080000 #endif +#ifndef SEC_I_MESSAGE_FRAGMENT #define SEC_I_MESSAGE_FRAGMENT 0x00090364L +#endif +#ifndef SEC_E_INVALID_PARAMETER #define SEC_E_INVALID_PARAMETER 0x8009035DL +#endif //hungarian ensures we hit no macros. diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index 46b63c212..3787e218a 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -68,7 +68,7 @@ extern ftemanifest_t *fs_manifest; #endif #if defined(_WIN32) || defined(__linux__) && !defined(ANDROID) -#define USE_GETHOSTNAME_LOCALLISTING + #define USE_GETHOSTNAME_LOCALLISTING #endif netadr_t net_local_cl_ipadr; //still used to match local ui requests (quake/gamespy), and to generate ip reports for q3 servers (which is probably pointless). @@ -81,39 +81,28 @@ sizebuf_t net_message; //emscripten can misalign stuff, which is a problem when the leading int is checked directly in a few places. gah. FTE_ALIGN(4) qbyte net_message_buffer[MAX_OVERALLMSGLEN]; #if defined(_WIN32) && defined(HAVE_PACKET) -WSADATA winsockdata; + WSADATA winsockdata; #endif -#ifdef HAVE_IPV6 -#ifdef _WIN32 -int (WINAPI *pgetaddrinfo) ( - const char* nodename, - const char* servname, - const struct addrinfo* hints, - struct addrinfo** res -); -void (WSAAPI *pfreeaddrinfo) (struct addrinfo*); +#if defined(_WIN32) + int (WINAPI *pgetaddrinfo) ( + const char* nodename, + const char* servname, + const struct addrinfo* hints, + struct addrinfo** res + ); + void (WSAAPI *pfreeaddrinfo) (struct addrinfo*); #else -#define pgetaddrinfo getaddrinfo -#define pfreeaddrinfo freeaddrinfo -/*int (*pgetaddrinfo) -( - const char* nodename, - const char* servname, - const struct addrinfo* hints, - struct addrinfo** res -); -void (*pfreeaddrinfo) (struct addrinfo*); -*/ -#endif + #define pgetaddrinfo getaddrinfo + #define pfreeaddrinfo freeaddrinfo #endif #if defined(HAVE_IPV4) && defined(HAVE_SERVER) -#define HAVE_NATPMP + #define HAVE_NATPMP #endif #if defined(HAVE_SERVER) || defined(MASTERONLY) -#define HAVE_HTTPSV + #define HAVE_HTTPSV #endif void NET_GetLocalAddress (int socket, netadr_t *out); @@ -127,6 +116,8 @@ void IPX_CloseSocket (int socket); cvar_t timeout = CVARD("timeout","65", "Connections will time out if no packets are received for this duration of time."); // seconds without any message cvar_t net_hybriddualstack = CVARD("net_hybriddualstack", "1", "Uses hybrid ipv4+ipv6 sockets where possible. Not supported on xp or below."); cvar_t net_fakeloss = CVARFD("net_fakeloss", "0", CVAR_CHEAT, "Simulates packetloss in both receiving and sending, on a scale from 0 to 1."); +static cvar_t net_dns_ipv4 = CVARD("net_dns_ipv4", "1", "If 0, disables dns resolution of names to ipv4 addresses (removing any associated error messages). Also hides ipv4 addresses in address:port listings."); +static cvar_t net_dns_ipv6 = CVARD("net_dns_ipv6", "1", "If 0, disables dns resolution of names to ipv6 addresses (removing any associated error messages). Also hides ipv6 addresses in address:port listings."); cvar_t net_enabled = CVARD("net_enabled", "1", "If 0, disables all network access, including name resolution and socket creation. Does not affect loopback/internal connections."); #if defined(TCPCONNECT) && (defined(HAVE_SERVER) || defined(HAVE_HTTPSV)) #ifdef HAVE_SERVER @@ -1040,10 +1031,32 @@ size_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhin { struct sockaddr_un *sa = (struct sockaddr_un *)sadr; int i; + + //limit to known prefixes. this allows for sandboxing. + const char *allowedprefixes[] = {"@"DISTRIBUTION, "/tmp/"DISTRIBUTION".", "/tmp/qsock.", "@FTE", "@qtv", "@qsock"}; + for (i = 0; i < countof(allowedprefixes); i++) + { + if (!Q_strncasecmp(s, allowedprefixes[i], strlen(allowedprefixes[i]))) + break; + } + if (i == countof(allowedprefixes)) + { + Con_DPrintf(CON_WARNING "\"%s\" is not an accepted prefix for a unix socket. Forcing prefix.\n", s); + i = 0; + sa->sun_path[i++] = 0; + sa->sun_path[i++] = 'q'; + sa->sun_path[i++] = 's'; + sa->sun_path[i++] = 'o'; + sa->sun_path[i++] = 'c'; + sa->sun_path[i++] = 'k'; + } + else i = 0; + sa->sun_family = AF_UNIX; - //this parsing is so annoying because I want to support abstract sockets too. - for (i = 0; *s && i < countof(sa->sun_path); ) + //this parsing is so annoying because I want to support abstract sockets too, which have nulls. + //we're using @ charsto represent nulls, to match 'lsof -U' + for ( ; *s && i < countof(sa->sun_path); ) { if (*s == '@') { @@ -1053,23 +1066,26 @@ size_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhin else if (*s == '\\') { if (s[1] == 0) + { + sa->sun_path[i++] = '\\'; break; //error. + } else if (s[1] == '\\') sa->sun_path[i++] = '\\'; else if (s[1] == '@') sa->sun_path[i++] = '@'; else if (s[1] == 'n') - sa->sun_path[i++] = 'n'; + sa->sun_path[i++] = '\n'; else if (s[1] == 'r') - sa->sun_path[i++] = 'r'; + sa->sun_path[i++] = '\r'; else if (s[1] == 't') - sa->sun_path[i++] = 't'; + sa->sun_path[i++] = '\t'; else if (s[1] == '1') - sa->sun_path[i++] = '1'; + sa->sun_path[i++] = '\1'; else if (s[1] == '2') - sa->sun_path[i++] = '2'; + sa->sun_path[i++] = '\2'; else if (s[1] == '3') - sa->sun_path[i++] = '3'; + sa->sun_path[i++] = '\3'; else sa->sun_path[i++] = '?'; s+=2; @@ -1077,6 +1093,8 @@ size_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhin else sa->sun_path[i++] = *s++; } + if (sa->sun_path[0]) //'pathname sockets should be null terminated' + sa->sun_path[i++] = 0; if (i < countof(sa->sun_path)) sa->sun_path[i] = 'X'; if (addrsize) @@ -1185,6 +1203,10 @@ size_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhin } else { +#if defined(AI_ADDRCONFIG) && !defined(_WIN32) + udp6hint.ai_flags |= AI_ADDRCONFIG; //don't return ipv6 if we can't send to ipv6 hosts +#endif + port = strrchr(s, ':'); if (port) @@ -1218,12 +1240,16 @@ size_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhin switch(pos->ai_family) { case AF_INET6: + if (!net_dns_ipv6.ival) + continue; if (result < addresses) memcpy(&sadr[result++], pos->ai_addr, pos->ai_addrlen); break; #ifdef HAVE_IPV4 case AF_INET: - //ipv4 addresses have a higher priority than ipv6 ones. + if (!net_dns_ipv4.ival) + continue; + //ipv4 addresses have a higher priority than ipv6 ones (too few other quake engines support ipv6). if (result && ((struct sockaddr_in *)&sadr[0])->sin_family == AF_INET6) { if (result < addresses) @@ -1262,7 +1288,7 @@ size_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhin else #endif { -#ifdef HAVE_IPV4 +#if defined(HAVE_IPV4) && !defined(pgetaddrinfo) && !defined(HAVE_IPV6) char copy[128]; char *colon; @@ -1272,6 +1298,8 @@ size_t NET_StringToSockaddr2 (const char *s, int defaultport, netadrtype_t afhin if (strlen(s) >= sizeof(copy)-1) return false; + if (!net_dns_ipv4.ival) + return false; ((struct sockaddr_in *)sadr)->sin_port = htons(defaultport); @@ -2931,71 +2959,15 @@ void FTENET_Generic_Close(ftenet_generic_connection_t *con) int FTENET_GetLocalAddress(int port, qboolean ipx, qboolean ipv4, qboolean ipv6, unsigned int *adrflags, netadr_t *addresses, int maxaddresses) { //in win32, we can look up our own hostname to retrieve a list of local interface addresses. -#ifdef USE_GETHOSTNAME_LOCALLISTING char adrs[MAX_ADR_SIZE]; - int b; -#endif int found = 0; gethostname(adrs, sizeof(adrs)); -#ifdef HAVE_IPV6 - if (pgetaddrinfo) +#ifndef pgetaddrinfo + if (!pgetaddrinfo) { - struct addrinfo hints, *result, *itr; - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = 0; /* Allow IPv4 or IPv6 */ - hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ - hints.ai_flags = 0; - hints.ai_protocol = 0; /* Any protocol */ - - if (pgetaddrinfo(adrs, NULL, &hints, &result) == 0) - { - for (itr = result; itr; itr = itr->ai_next) - { - if ((itr->ai_addr->sa_family == AF_INET && ipv4) - || (itr->ai_addr->sa_family == AF_INET6 && ipv6) -#ifdef HAVE_IPX - || (itr->ai_addr->sa_family == AF_IPX && ipx) -#endif - ) - if (maxaddresses) - { - SockadrToNetadr((struct sockaddr_qstorage*)itr->ai_addr, sizeof(struct sockaddr_qstorage), addresses); - addresses->port = port; - *adrflags++ = 0; - addresses++; - maxaddresses--; - found++; - } - } - pfreeaddrinfo(result); - - /*if none found, fill in the 0.0.0.0 or whatever*/ - if (!found && maxaddresses) - { - memset(addresses, 0, sizeof(*addresses)); - addresses->port = port; - if (ipv6) - addresses->type = NA_IPV6; - else if (ipv4) - addresses->type = NA_IP; - else if (ipx) - addresses->type = NA_IPX; - else - addresses->type = NA_INVALID; - *adrflags++ = 0; - addresses++; - maxaddresses--; - found++; - } - } - } - else -#endif - { - struct hostent *h; - h = gethostbyname(adrs); - b = 0; + struct hostent *h = gethostbyname(adrs); + int b = 0; #ifdef HAVE_IPV4 if(h && h->h_addrtype == AF_INET) { @@ -3033,6 +3005,63 @@ int FTENET_GetLocalAddress(int port, qboolean ipx, qboolean ipv4, qboolean ipv6, } #endif } + else +#endif + { + struct addrinfo hints, *result, *itr; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = 0; /* Allow IPv4 or IPv6 */ + hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ + hints.ai_flags = 0; + hints.ai_protocol = 0; /* Any protocol */ + + if (pgetaddrinfo(adrs, NULL, &hints, &result) == 0) + { + for (itr = result; itr; itr = itr->ai_next) + { + if (0 +#ifdef HAVE_IPV4 + || (itr->ai_addr->sa_family == AF_INET && ipv4) +#endif +#ifdef HAVE_IPV6 + || (itr->ai_addr->sa_family == AF_INET6 && ipv6) +#endif +#ifdef HAVE_IPX + || (itr->ai_addr->sa_family == AF_IPX && ipx) +#endif + ) + if (maxaddresses) + { + SockadrToNetadr((struct sockaddr_qstorage*)itr->ai_addr, sizeof(struct sockaddr_qstorage), addresses); + addresses->port = port; + *adrflags++ = 0; + addresses++; + maxaddresses--; + found++; + } + } + pfreeaddrinfo(result); + + /*if none found, fill in the 0.0.0.0 or whatever*/ + if (!found && maxaddresses) + { + memset(addresses, 0, sizeof(*addresses)); + addresses->port = port; + if (ipv6) + addresses->type = NA_IPV6; + else if (ipv4) + addresses->type = NA_IP; + else if (ipx) + addresses->type = NA_IPX; + else + addresses->type = NA_INVALID; + *adrflags++ = 0; + addresses++; + maxaddresses--; + found++; + } + } + } return found; } @@ -3051,6 +3080,10 @@ int FTENET_GetLocalAddress(int port, qboolean ipx, qboolean ipv4, qboolean ipv6, int fam; int idx = 0; unsigned int time = Sys_Milliseconds(); + + ipv4 = ipv4 && net_dns_ipv4.ival; + ipv6 = ipv6 && net_dns_ipv6.ival; + if (time - iftime > 1000 && iflist) { freeifaddrs(iflist); @@ -3325,7 +3358,6 @@ neterr_t FTENET_Datagram_SendPacket(ftenet_generic_connection_t *con, int length ret = sendto (con->thesocket, data, length, 0, (struct sockaddr*)&addr, size ); if (ret == -1) { - const char *prot; int ecode = neterrno(); // wouldblock is silent if (ecode == NET_EWOULDBLOCK) @@ -3346,26 +3378,21 @@ neterr_t FTENET_Datagram_SendPacket(ftenet_generic_connection_t *con, int length return NETERR_DISCONNECTED; } - //network is unreachable scares the socks off people when IPv6 is non-functional despite ipv4 working fine. - //name the problem protocol in the error message. - switch(to->type) { - case NA_IP: prot = "IPv4"; break; - case NA_IPV6: prot = "IPv6"; break; - case NA_IPX: prot = "IPX"; break; - default: prot = ""; break; - } - + char adr[256]; #ifdef HAVE_CLIENT - if (ecode == NET_EADDRNOTAVAIL) - Con_DPrintf("NET_Send%sPacket Warning: %i\n", prot, ecode); - else + if (ecode == NET_EADDRNOTAVAIL) + Con_DPrintf("NET_SendPacket(%s) Warning: %i\n", NET_AdrToString (adr, sizeof(adr), to), ecode); + else #endif + { #ifdef _WIN32 - Con_TPrintf ("NET_Send%sPacket ERROR: %i\n", prot, ecode); + Con_Printf ("NET_SendPacket(%s) ERROR: %i\n", NET_AdrToString (adr, sizeof(adr), to), ecode); #else - Con_TPrintf ("NET_Send%sPacket ERROR: %s\n", prot, strerror(ecode)); + Con_Printf ("NET_SendPacket(%s) ERROR: %s\n", NET_AdrToString (adr, sizeof(adr), to), strerror(ecode)); #endif + } + } } else if (ret < length) return NETERR_MTU; @@ -3542,6 +3569,7 @@ ftenet_generic_connection_t *FTENET_Datagram_EstablishConnection(qboolean isserv //and we can't re-bind to it while it still exists. //so standard practise is to delete it before the bind. //we do want to make sure the file is actually a socket before we remove it (so people can't abuse stuffcmds) + //FIXME: use lock-files struct stat s; if (stat(sa->sun_path, &s)!=-1) { @@ -7147,7 +7175,7 @@ enum addressscope_e NET_ClassifyAddress(netadr_t *adr, char **outdesc) else if (adr->type == NA_IPV6) { if ((*(int*)adr->address.ip6&BigLong(0xffc00000)) == BigLong(0xfe800000)) //fe80::/10 - scope = ASCOPE_LAN, desc = "link-local"; + scope = ASCOPE_LINK, desc = "link-local"; else if ((*(int*)adr->address.ip6&BigLong(0xfe000000)) == BigLong(0xfc00000)) //fc::/7 scope = ASCOPE_LAN, desc = "ULA/private"; else if (*(int*)adr->address.ip6 == BigLong(0x20010000)) //2001::/32 @@ -7162,7 +7190,7 @@ enum addressscope_e NET_ClassifyAddress(netadr_t *adr, char **outdesc) else if (adr->type == NA_IP) { if ((*(int*)adr->address.ip&BigLong(0xffff0000)) == BigLong(0xA9FE0000)) //169.254.x.x/16 - scope = ASCOPE_LAN, desc = "link-local"; + scope = ASCOPE_LINK, desc = "link-local"; else if ((*(int*)adr->address.ip&BigLong(0xff000000)) == BigLong(0x0a000000)) //10.x.x.x/8 scope = ASCOPE_LAN, desc = "private"; else if ((*(int*)adr->address.ip&BigLong(0xff000000)) == BigLong(0x7f000000)) //127.x.x.x/8 @@ -7191,6 +7219,8 @@ void NET_PrintAddresses(ftenet_connections_t *collection) struct ftenet_generic_connection_s *con[sizeof(addr)/sizeof(addr[0])]; int flags[sizeof(addr)/sizeof(addr[0])]; qboolean warn = true; + static const char *scopes[] = {"process", "local", "link", "lan", "net"}; + char *desc; if (!collection) return; @@ -7201,23 +7231,20 @@ void NET_PrintAddresses(ftenet_connections_t *collection) { if (addr[i].type != NA_INVALID) { - char *scopes[] = {NULL, "local", "lan", "net"}; - char *scope; - char *desc; - scope = scopes[NET_ClassifyAddress(&addr[i], &desc)]; - if (scope) + enum addressscope_e scope = NET_ClassifyAddress(&addr[i], &desc); + if (developer.ival || scope >= ASCOPE_LAN) { warn = false; if (desc) - Con_Printf("%s address (%s): %s (%s)\n", scope, con[i]->name, NET_AdrToString(adrbuf, sizeof(adrbuf), &addr[i]), desc); + Con_Printf("%s address (%s): %s (%s)\n", scopes[scope], con[i]->name, NET_AdrToString(adrbuf, sizeof(adrbuf), &addr[i]), desc); else - Con_Printf("%s address (%s): %s\n", scope, con[i]->name, NET_AdrToString(adrbuf, sizeof(adrbuf), &addr[i])); + Con_Printf("%s address (%s): %s\n", scopes[scope], con[i]->name, NET_AdrToString(adrbuf, sizeof(adrbuf), &addr[i])); } } } if (warn) - Con_Printf("net address: no addresses\n"); + Con_Printf("net address: no public addresses\n"); } void NET_PrintConnectionsStatus(ftenet_connections_t *collection) @@ -7298,7 +7325,7 @@ int TCP_OpenStream (netadr_t *remoteaddr) { //if its a unix socket, attempt to bind it to an unnamed address. linux should generate an ephemerial abstract address (otherwise the server will see an empty address). struct sockaddr_un un; memset(&un, 0, offsetof(struct sockaddr_un, sun_path)); - bind(newsocket, &un, offsetof(struct sockaddr_un, sun_path)); + bind(newsocket, (struct sockaddr*)&un, offsetof(struct sockaddr_un, sun_path)); } else #endif @@ -7815,6 +7842,8 @@ NET_Init void NET_Init (void) { Cvar_Register(&net_enabled, "networking"); + Cvar_Register(&net_dns_ipv4, "networking"); + Cvar_Register(&net_dns_ipv6, "networking"); if (net_enabled.ival) { #if defined(_WIN32) && defined(HAVE_PACKET) @@ -7981,7 +8010,11 @@ void QDECL SV_PortUNIX_Callback(struct cvar_s *var, char *oldvalue) { FTENET_AddToCollection(svs.sockets, var->name, var->string, NA_UNIX, NP_DGRAM); } -cvar_t sv_port_unix = CVARC("sv_port_unix", "/tmp/fte.sock", SV_PortUNIX_Callback); +#ifdef __linux //linux adds abstract sockets, which require no filesystem cleanup. +cvar_t sv_port_unix = CVARC("sv_port_unix", "@qsock.fte", SV_PortUNIX_Callback); +#else +cvar_t sv_port_unix = CVARC("sv_port_unix", "/tmp/qsock.fte", SV_PortUNIX_Callback); +#endif #endif #ifdef HAVE_NATPMP static void QDECL SV_Port_NatPMP_Callback(struct cvar_s *var, char *oldvalue) diff --git a/engine/common/plugin.c b/engine/common/plugin.c index 0045d236e..cc9dff904 100644 --- a/engine/common/plugin.c +++ b/engine/common/plugin.c @@ -2156,12 +2156,12 @@ void Plug_CloseAll_f(void) int QDECL Plug_List_Print(const char *fname, qofs_t fsize, time_t modtime, void *parm, searchpathfuncs_t *spath) { -plugin_t *plug; + plugin_t *plug; char plugname[MAX_QPATH]; //lots of awkward logic so we hide modules for other cpus. size_t nl = strlen(fname); size_t u; - const char *knownarch[] = + static const char *knownarch[] = { "x32", "x64", "amd64", "x86", //various x86 ABIs "arm", "arm64", "armhf", //various arm ABIs diff --git a/engine/common/pmove.h b/engine/common/pmove.h index a59704376..c5b6561ad 100644 --- a/engine/common/pmove.h +++ b/engine/common/pmove.h @@ -36,7 +36,7 @@ typedef enum { #define PMF_JUMP_HELD 1 #define PMF_LADDER 2 //pmove flags. seperate from flags -#define MAX_PHYSENTS 128 +#define MAX_PHYSENTS 2048 typedef struct { vec3_t origin; diff --git a/engine/common/qvm.c b/engine/common/qvm.c index 706c1d25c..f11b5caf5 100644 --- a/engine/common/qvm.c +++ b/engine/common/qvm.c @@ -97,10 +97,10 @@ qboolean QVM_LoadDLL(vm_t *vm, const char *name, qboolean binroot, void **vmMain hVM=NULL; *fname = 0; - Con_DPrintf("Attempting to load native library: %s\n", name); - if (binroot) { + Con_DPrintf("Attempting to load native library: %s\n", name); + if (!hVM && FS_NativePath(dllname_arch, FS_BINARYPATH, fname, sizeof(fname))) hVM = Sys_LoadLibrary(fname, funcs); if (!hVM && FS_NativePath(dllname_anycpu, FS_BINARYPATH, fname, sizeof(fname))) @@ -130,20 +130,22 @@ qboolean QVM_LoadDLL(vm_t *vm, const char *name, qboolean binroot, void **vmMain } else { + Con_DPrintf("Attempting to load (unsafe) native library: %s\n", name); + // run through the search paths iterator = NULL; while (!hVM && COM_IteratePaths(&iterator, gpath, sizeof(gpath), NULL, 0)) { if (!hVM) { - snprintf (fname, sizeof(fname), "%s/%s", gpath, dllname_arch); + snprintf (fname, sizeof(fname), "%s%s", gpath, dllname_arch); Con_DLPrintf(2, "Loading native: %s\n", fname); hVM = Sys_LoadLibrary(fname, funcs); } if (!hVM) { - snprintf (fname, sizeof(fname), "%s/%s", gpath, dllname_anycpu); + snprintf (fname, sizeof(fname), "%s%s", gpath, dllname_anycpu); Con_DLPrintf(2, "Loading native: %s\n", fname); hVM = Sys_LoadLibrary(fname, funcs); } diff --git a/engine/d3d/d3d11_image.c b/engine/d3d/d3d11_image.c index c35f0b897..836bf8f54 100644 --- a/engine/d3d/d3d11_image.c +++ b/engine/d3d/d3d11_image.c @@ -274,6 +274,13 @@ qboolean D3D11_LoadTextureMips(image_t *tex, const struct pendingtextureinfo *mi tdesc.Format = DXGI_FORMAT_BC7_UNORM_SRGB; break; + case PTI_R16: + tdesc.Format = DXGI_FORMAT_R16_UNORM; + break; + case PTI_RGBA16: + tdesc.Format = DXGI_FORMAT_R16G16B16A16_UNORM; + break; + case PTI_R16F: tdesc.Format = DXGI_FORMAT_R16_FLOAT; break; @@ -287,6 +294,7 @@ qboolean D3D11_LoadTextureMips(image_t *tex, const struct pendingtextureinfo *mi tdesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; break; case PTI_L8: //UNSUPPORTED + case PTI_P8: //R8, but different usage. case PTI_R8: tdesc.Format = DXGI_FORMAT_R8_UNORM; break; diff --git a/engine/droid/AndroidManifest.xml b/engine/droid/AndroidManifest.xml index cf6e31f27..0eb1101a4 100644 --- a/engine/droid/AndroidManifest.xml +++ b/engine/droid/AndroidManifest.xml @@ -62,6 +62,9 @@ + + + diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index ac1d49579..5962e07ad 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -1081,7 +1081,7 @@ static shader_t *GL_ChooseSkin(galiasinfo_t *inf, model_t *model, int surfnum, e cm->texnum.paletted = R_LoadTexture(va("paletted$%x$%x$%i$%i$%i$%s", tc, bc, cm->skinnum, subframe, pc, cm->name), - scaled_width, scaled_height, PTI_R8, pixels8, IF_NEAREST|IF_NOMIPMAP); + scaled_width, scaled_height, PTI_P8, pixels8, IF_NEAREST|IF_NOMIPMAP); } diff --git a/engine/gl/gl_draw.c b/engine/gl/gl_draw.c index 7513971e8..7b76794d2 100644 --- a/engine/gl/gl_draw.c +++ b/engine/gl/gl_draw.c @@ -218,6 +218,7 @@ void GL_SetupFormats(void) glfmt(PTI_A2BGR10, GL_RGB10_A2, GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV); if (ver >= 3.0 || GL_CheckExtension("GL_ARB_texture_rg")) { + glfmtc(PTI_P8, GL_R8, GL_RED, GL_RED, GL_UNSIGNED_BYTE, tc_ru); glfmtc(PTI_R8, GL_R8, GL_RED, GL_RED, GL_UNSIGNED_BYTE, tc_ru); glfmtc(PTI_RG8, GL_RG8, GL_RG, GL_RG, GL_UNSIGNED_BYTE, tc_rs); } @@ -229,6 +230,9 @@ void GL_SetupFormats(void) if (ver >= 3.0) { + glfmtc(PTI_R16, GL_R16, GL_RED, GL_RED, GL_UNSIGNED_SHORT, 0); + glfmtc(PTI_RGBA16, GL_RGBA16, GL_RGBA, GL_RGBA, GL_UNSIGNED_SHORT, 0); + glfmtc(PTI_R16F, GL_R16F, GL_RED, GL_RED, GL_HALF_FLOAT, 0); glfmtc(PTI_R32F, GL_R32F, GL_RED, GL_RED, GL_FLOAT, 0); diff --git a/engine/gl/gl_heightmap.c b/engine/gl/gl_heightmap.c index a301e27b5..624eb0043 100644 --- a/engine/gl/gl_heightmap.c +++ b/engine/gl/gl_heightmap.c @@ -6817,6 +6817,7 @@ void QCBUILTIN PF_brush_create(pubprogfuncs_t *prinst, struct globalvars_s *pr_g brush.numplanes = numfaces; brush.planes = planes; brush.faces = faces; + brush.patch = NULL; if (numfaces) { nb = Terr_Brush_Insert(mod, hm, &brush); @@ -7573,7 +7574,7 @@ qboolean Terr_ReformEntitiesLump(model_t *mod, heightmap_t *hm, char *entities) p = 4; //we just managed to read an entire plane instead of 3 points. break; } - entities = COM_ParseTokenOut(entities, brushpunct, token, sizeof(token), NULL); + entities = COM_ParseTokenOut(entities, "()", token, sizeof(token), NULL); } if (p < 3) { diff --git a/engine/gl/gl_model.c b/engine/gl/gl_model.c index 010f41127..5a4405feb 100644 --- a/engine/gl/gl_model.c +++ b/engine/gl/gl_model.c @@ -294,6 +294,8 @@ static void Mod_BlockTextureColour_f (void) #if defined(MULTITHREAD) #ifdef _WIN32 #include +#elif defined(__linux__) +#include #endif static void *relightthread[8]; static unsigned int relightthreads; @@ -349,7 +351,13 @@ void Mod_Think (void) else relightthreads--; #elif defined(__GNUC__) + #ifdef __linux__ + relightthreads = sysconf(_SC_NPROCESSORS_ONLN)-1; + if (relightthreads < 1) + relightthreads = 1; + #else relightthreads = 2; //erm, lets hope... + #endif #else /*can't do atomics*/ relightthreads = 1; diff --git a/engine/gl/gl_rlight.c b/engine/gl/gl_rlight.c index 260cadbf3..46f81d3cd 100644 --- a/engine/gl/gl_rlight.c +++ b/engine/gl/gl_rlight.c @@ -388,7 +388,7 @@ void R_RenderDlights (void) cscale = l->coronascale; intensity = l->corona;// * 0.25; if (coronastyle) - intensity *= r_coronas.value; + intensity *= r_coronas.value * r_coronas_intensity.value; else intensity *= r_flashblend.value; if (intensity <= 0 || cscale <= 0) diff --git a/engine/gl/gl_rmain.c b/engine/gl/gl_rmain.c index e5287592f..4f57f15fa 100644 --- a/engine/gl/gl_rmain.c +++ b/engine/gl/gl_rmain.c @@ -756,6 +756,7 @@ void R_RenderScene (void) { GL_ForceDepthWritable(); qglClear (GL_DEPTH_BUFFER_BIT); + r_framecount++; } TRACE(("dbg: calling R_SetupGL\n")); @@ -1367,7 +1368,7 @@ void R_Clear (qboolean fbo) //for performance, we clear the depth at the same time we clear colour, so we can skip clearing depth here the first time around each frame. //but for multiple scenes, we do need to clear depth still. //fbos always get cleared depth, just in case (colour fbos may contain junk, but hey). - if (fbo && r_clear.ival) + if ((fbo && r_clear.ival) || r_refdef.stereomethod==STEREO_RED_BLUE||r_refdef.stereomethod==STEREO_RED_GREEN) qglClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); else qglClear (GL_DEPTH_BUFFER_BIT); @@ -1744,6 +1745,8 @@ qboolean R_RenderScene_Cubemap(void) GL_MTBind(0, GL_TEXTURE_CUBE_MAP_ARB, scenepp_postproc_cube); qglCopyTexSubImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + i, 0, 0, 0, 0, vid.fbpheight - (prect.y + cmapsize), cmapsize, cmapsize); } + + r_framecount++; } if (usefbo) diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index b379a06f5..e9805e7a8 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -5723,7 +5723,7 @@ void QDECL R_BuildLegacyTexnums(shader_t *shader, const char *fallbackname, cons if (!TEXVALID(tex->paletted) && *mapname) tex->paletted = R_LoadHiResTexture(va("%s_pal", mapname), NULL, imageflags|IF_NEAREST); if (!TEXVALID(tex->paletted)) - tex->paletted = Image_GetTexture(va("%s_pal", imagename), subpath, imageflags|IF_NEAREST|IF_NOSRGB, mipdata[0], palette, width, height, (basefmt==TF_MIP4_SOLID8)?TF_MIP4_R8:TF_R8); + tex->paletted = Image_GetTexture(va("%s_pal", imagename), subpath, imageflags|IF_NEAREST|IF_NOSRGB, mipdata[0], palette, width, height, (basefmt==TF_MIP4_SOLID8)?TF_MIP4_P8:PTI_P8); } imageflags |= IF_LOWPRIORITY; diff --git a/engine/gl/gl_vidlinuxglx.c b/engine/gl/gl_vidlinuxglx.c index 17be27331..6e426c072 100644 --- a/engine/gl/gl_vidlinuxglx.c +++ b/engine/gl/gl_vidlinuxglx.c @@ -709,7 +709,7 @@ static qboolean XRandR_Init(void) xrandr.outputs = NULL; //enable by default once this is properly tested, and supports hwgamma. - if (!X11_CheckFeature("xrandr", false)) + if (!X11_CheckFeature("xrandr", true)) return false; if (!xrandr.lib) @@ -877,6 +877,7 @@ static void XRandR_Shutdown(void) } static qboolean XRandR_FindOutput(const char *name) { + XRROutputInfo *primary = NULL; int i; xrandr.output = NULL; if (!xrandr.outputs) @@ -892,18 +893,18 @@ static qboolean XRandR_FindOutput(const char *name) xrandr.crtcinfo = NULL; for (i = 0; i < xrandr.res->noutput; i++) { - if (xrandr.outputs[i]->connection != RR_Connected) - continue; - if (!xrandr.output || !strncmp(xrandr.outputs[i]->name, name, xrandr.outputs[i]->nameLen)) - { - if (xrandr.outputs[i]->ncrtc) //should be able to use this one - { - xrandr.output = xrandr.outputs[i]; - if (!strncmp(xrandr.outputs[i]->name, name, xrandr.outputs[i]->nameLen)) - break; - } + if (xrandr.outputs[i]->connection != RR_Connected || !xrandr.outputs[i]->ncrtc) + continue; //not usable... + if (!primary || (xrandr.outputs[i]->npreferred && !primary->npreferred)) + primary = xrandr.outputs[i]; + if (*name && !strncmp(xrandr.outputs[i]->name, name, xrandr.outputs[i]->nameLen)) + { //this is the one they asked for + xrandr.output = xrandr.outputs[i]; + break; } } + if (!xrandr.output) + xrandr.output = primary; if (xrandr.output) { xrandr.crtc = xrandr.output->crtcs[0]; @@ -3652,7 +3653,7 @@ static qboolean X_CheckWMFullscreenAvailable(void) return success; } -static Window X_CreateWindow(qboolean override, XVisualInfo *visinfo, int x, int y, unsigned int width, unsigned int height, qboolean fullscreen) +static Window X_CreateWindow(rendererstate_t *info, qboolean override, XVisualInfo *visinfo, int x, int y, unsigned int width, unsigned int height, qboolean fullscreen) { Window wnd, parent; XSetWindowAttributes attr; @@ -3677,14 +3678,23 @@ static Window X_CreateWindow(qboolean override, XVisualInfo *visinfo, int x, int } memset(&szhints, 0, sizeof(szhints)); - szhints.flags = PMinSize; + szhints.flags = PMinSize|PPosition|PSize; szhints.min_width = 320; szhints.min_height = 200; - szhints.x = x; - szhints.y = y; - szhints.width = width; - szhints.height = height; + if (!fullscreen) + { +#ifdef USE_XRANDR + int dx, dy, dw, dh; + if (*info->devicename && XRandr_PickScreen(info->devicename, &dx, &dy, &dw, &dh)) + { + x += dx + (dw-width)/2; + y += dy + (dh-height)/2; + } + else +#endif + szhints.flags &= ~PPosition; + } if (sys_parentwindow && !fullscreen) { @@ -3695,6 +3705,11 @@ static Window X_CreateWindow(qboolean override, XVisualInfo *visinfo, int x, int else parent = vid_root; + szhints.x = x; + szhints.y = y; + szhints.width = width; + szhints.height = height; + wnd = x11.pXCreateWindow(vid_dpy, parent, x, y, width, height, 0, visinfo->depth, InputOutput, visinfo->visual, mask, &attr); @@ -3889,11 +3904,11 @@ static qboolean X11VID_Init (rendererstate_t *info, unsigned char *palette, int vid.activeapp = false; if (fullscreenflags & FULLSCREEN_LEGACY) { - vid_decoywindow = X_CreateWindow(false, visinfo, x, y, 320, 200, false); - vid_window = X_CreateWindow(true, visinfo, x, y, width, height, fullscreen); + vid_decoywindow = X_CreateWindow(info, false, visinfo, x, y, 320, 200, false); + vid_window = X_CreateWindow(info, true, visinfo, x, y, width, height, fullscreen); } else - vid_window = X_CreateWindow(false, visinfo, x, y, width, height, fullscreen); + vid_window = X_CreateWindow(info, false, visinfo, x, y, width, height, fullscreen); vid_x_eventmask |= X_InitUnicode(); x11.pXSelectInput(vid_dpy, vid_window, vid_x_eventmask); diff --git a/engine/gl/glsupp.h b/engine/gl/glsupp.h index 4aeb8147a..ed34100f5 100644 --- a/engine/gl/glsupp.h +++ b/engine/gl/glsupp.h @@ -683,6 +683,7 @@ typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei #define GL_RG 0x8227 #define GL_RGB9_E5 0x8C3D /*opengl 3.0*/ #define GL_R8 0x8229 /*opengl 3.0*/ +#define GL_R16 0x822A /*opengl 3.0*/ #define GL_RG8 0x822B /*opengl 3.0*/ #endif #ifndef GL_RG8_SNORM diff --git a/engine/gl/r_bishaders.h b/engine/gl/r_bishaders.h index 21e13fc61..446523412 100644 --- a/engine/gl/r_bishaders.h +++ b/engine/gl/r_bishaders.h @@ -2982,7 +2982,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#include \"sys/skeletal.h\"\n" "affine varying vec2 tc;\n" -"varying vec3 light;\n" +"varying vec4 light;\n" "#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n" "varying vec3 eyevector;\n" "#endif\n" @@ -2998,6 +2998,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "{\n" "vec3 n, s, t, w;\n" "gl_Position = skeletaltransform_wnst(w,n,s,t);\n" +"n = normalize(n);\n" "#if defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n" "vec3 eyeminusvertex = e_eyepos - w.xyz;\n" "eyevector.x = dot(eyeminusvertex, s.xyz);\n" @@ -3015,7 +3016,11 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "float d = dot(n,e_light_dir);\n" "if (d < 0.0) //vertex shader. this might get ugly, but I don't really want to make it per vertex.\n" "d = 0.0; //this avoids the dark side going below the ambient level.\n" -"light = e_light_ambient + (d*e_light_mul);\n" +"light.rgb = e_light_ambient + (d*e_light_mul);\n" +"light.a = 1.0;\n" +"#ifdef VC\n" +"light *= v_colour;\n" +"#endif\n" //FIXME: Software rendering imitation should possibly push out normals by half a pixel or something to approximate software's over-estimation of distant model sizes (small models are drawn using JUST their verticies using the nearest pixel, which results in larger meshes) @@ -3044,8 +3049,8 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "out vec3 t_normal[];\n" "affine in vec2 tc[];\n" "affine out vec2 t_tc[];\n" -"in vec3 light[];\n" -"out vec3 t_light[];\n" +"in vec4 light[];\n" +"out vec4 t_light[];\n" "#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n" "in vec3 eyevector[];\n" "out vec3 t_eyevector[];\n" @@ -3093,8 +3098,8 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "in vec3 t_normal[];\n" "affine in vec2 t_tc[];\n" "affine out vec2 tc;\n" -"in vec3 t_light[];\n" -"out vec3 light;\n" +"in vec4 t_light[];\n" +"out vec4 light;\n" "#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n" "in vec3 t_eyevector[];\n" "out vec3 eyevector;\n" @@ -3157,7 +3162,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "#endif\n" "affine varying vec2 tc;\n" -"varying vec3 light;\n" +"varying vec4 light;\n" "#if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK)\n" "varying vec3 eyevector;\n" "#endif\n" @@ -3214,8 +3219,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb;\n" "#endif\n" -"col.rgb *= light;\n" -"col *= e_colourident;\n" +"col *= light * e_colourident;\n" "#ifdef FULLBRIGHT\n" "vec4 fb = texture2D(s_fullbright, tc);\n" @@ -6913,7 +6917,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "ts *= (texture2D(s_lightmap, lm0) * e_lmscale).rgb;\n" "#endif\n" -"gl_FragColor = fog4(vec4(ts, USEALPHA) * e_colourident);\n" +"gl_FragColor = fog4blend(vec4(ts, USEALPHA) * e_colourident);\n" "}\n" "#endif\n" }, @@ -10452,9 +10456,10 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND {QR_OPENGL, 110, "terrain", "!!permu FOG\n" //t0-t3 are the diffusemaps, t4 is the blend factors -"!!samps 5\n" -"!!samps =PCF 6\n" -"!!samps =CUBE 7\n" +"!!samps 4\n" +"!!samps mix=4\n" +"!!samps =PCF shadowmap\n" +"!!samps =CUBE projectionmap\n" //light levels @@ -10547,7 +10552,7 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "void main (void)\n" "{\n" "vec4 r;\n" -"vec4 m = texture2D(s_t4, lm);\n" +"vec4 m = texture2D(s_mix, lm);\n" "r = texture2D(s_t0, tc)*m.r;\n" "r += texture2D(s_t1, tc)*m.g;\n" @@ -10581,13 +10586,13 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "colorscale *= 1.0-(dot(spot,spot));\n" "#endif\n" "#ifdef PCF\n" -"colorscale *= ShadowmapFilter(s_t5, vtexprojcoord);\n" +"colorscale *= ShadowmapFilter(s_shadowmap, vtexprojcoord);\n" "#endif\n" "r.rgb *= colorscale * l_lightcolour;\n" "#ifdef CUBE\n" -"r.rgb *= textureCube(s_t6, vtexprojcoord.xyz).rgb;\n" +"r.rgb *= textureCube(s_projectionmap, vtexprojcoord.xyz).rgb;\n" "#endif\n" "gl_FragColor = fog4additive(r);\n" @@ -10908,7 +10913,9 @@ YOU SHOULD NOT EDIT THIS FILE BY HAND "!!cvarf r_glsl_offsetmapping_scale\n" "!!cvardf r_glsl_pcf\n" "!!cvardf r_tessellation_level=5\n" -"!!samps shadowmap diffuse normalmap specular upper lower reflectcube reflectmask projectionmap\n" +"!!samps diffuse normalmap specular upper lower reflectcube reflectmask\n" +"!!samps =PCF shadowmap\n" +"!!samps =CUBE projectionmap\n" "#include \"sys/defs.h\"\n" diff --git a/engine/http/ftpserver.c b/engine/http/ftpserver.c index a832c5bd7..a63daff5f 100644 --- a/engine/http/ftpserver.c +++ b/engine/http/ftpserver.c @@ -363,6 +363,20 @@ iwboolean FTP_V4StringToAdr (const char *s, struct sockaddr_in *addr) return true; } + +#if defined(_WIN32) && !defined(_WIN64) && !defined(WEBSVONLY) + int (WINAPI *pgetaddrinfo) ( + const char* nodename, + const char* servname, + const struct addrinfo* hints, + struct addrinfo** res + ); + void (WSAAPI *pfreeaddrinfo) (struct addrinfo*); +#else +#define qgetaddrinfo getaddrinfo +#define qfreeaddrinfo freeaddrinfo +#endif + iwboolean FTP_HostToSockaddr(int prot, char *host, int port, struct sockaddr_qstorage *addr, size_t *addrsize) { iwboolean r = false; @@ -383,7 +397,7 @@ iwboolean FTP_HostToSockaddr(int prot, char *host, int port, struct sockaddr_qst break; } Q_snprintfz(service, sizeof(service), "%i", port); - if (getaddrinfo(host, service, &hint, &res)) + if (qgetaddrinfo(host, service, &hint, &res)) return false; if (res && res->ai_addr && res->ai_addrlen <= sizeof(*addr)) { @@ -391,7 +405,7 @@ iwboolean FTP_HostToSockaddr(int prot, char *host, int port, struct sockaddr_qst memcpy(addr, res->ai_addr, res->ai_addrlen); r = true; } - freeaddrinfo(res); + qfreeaddrinfo(res); return r; #if 0 host = va("[%s]", host); diff --git a/engine/partcfgs/high.cfg b/engine/partcfgs/high.cfg index 5bec067e4..67419d777 100644 --- a/engine/partcfgs/high.cfg +++ b/engine/partcfgs/high.cfg @@ -223,6 +223,22 @@ r_part +te_superspike assoc gunshotsmoke } +//simple slight trail, to show movement more than anything else. +r_part tr_spike +{ + texture "particles/fteparticlefont.tga" + step 1.1 + scale 4 + die .4 + rgb 20 20 20 + alpha 1 + blend add + spawnmode spiral 512 + spawnvel 10 +} +r_trail "progs/spike.mdl" tr_spike +r_trail "progs/s_spike.mdl" tr_spike + //////////////////////////////////////////////// //explosion diff --git a/engine/qclib/execloop.h b/engine/qclib/execloop.h index 844baea9a..f6c2654f2 100644 --- a/engine/qclib/execloop.h +++ b/engine/qclib/execloop.h @@ -445,7 +445,7 @@ reeval: ddef32_t *d = ED_GlobalAtOfs32(progfuncs, st->a); #endif fdef_t *f = ED_FieldAtOfs(progfuncs, OPB->_int + progfuncs->funcs.fieldadjust); - if (PR_ExecRunWarning(&progfuncs->funcs, st-pr_statements, "assignment to read-only entity %i in %s (%s.%s)\n", OPA->edict, PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), d?PR_StringToNative(&progfuncs->funcs, d->s_name):NULL, f?f->name:NULL)) + if (PR_ExecRunWarning(&progfuncs->funcs, st-pr_statements, "assignment to read-only entity %i in %s (%s.%s)\n", OPA->edict, PR_StringToNative(&progfuncs->funcs, pr_xfunction->s_name), d?PR_StringToNative(&progfuncs->funcs, d->s_name):"??", f?f->name:"??")) return pr_xstatement; OPC->_int = ~0; break; diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index 4f532de3a..39527dc96 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -6454,17 +6454,6 @@ static QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the f if (arg >= MAX_PARMS+MAX_EXTRA_PARMS) QCC_PR_ParseErrorPrintSRef (ERR_TOOMANYTOTALPARAMETERS, func, "More than %i parameters", MAX_PARMS+MAX_EXTRA_PARMS); - else if (extraparms && arg >= MAX_PARMS && !t->vargcount) - { - //vararg builtins cannot accept more than 8 args. they can't tell if they got more, and wouldn't know where to read them. - QCC_PR_ParseWarning (WARN_TOOMANYPARAMETERSVARARGS, "More than %i parameters on varargs function", MAX_PARMS); - QCC_PR_ParsePrintSRef(WARN_TOOMANYPARAMETERSVARARGS, func); - } - else if (!extraparms && arg >= t->num_parms && !p) - { - QCC_PR_ParseWarning (WARN_TOOMANYPARAMETERSFORFUNC, "too many parameters on call to %s", funcname); - QCC_PR_ParsePrintSRef(WARN_TOOMANYPARAMETERSFORFUNC, func); - } if (QCC_PR_CheckToken("#")) { @@ -6481,8 +6470,20 @@ static QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the f e = QCC_DefToRef(¶mbuf[arg], func.cast->params[arg].defltvalue); } else + e = QCC_PR_RefExpression(¶mbuf[arg], TOP_PRIORITY, EXPR_DISALLOW_COMMA); - e = QCC_PR_RefExpression(¶mbuf[arg], TOP_PRIORITY, EXPR_DISALLOW_COMMA); + if (extraparms && arg >= MAX_PARMS && !t->vargcount) + { + //vararg builtins cannot accept more than 8 args. they can't tell if they got more, and wouldn't know where to read them. + QCC_PR_ParseWarning (WARN_TOOMANYPARAMETERSVARARGS, "More than %i parameters on varargs function", MAX_PARMS); + QCC_PR_ParsePrintSRef(WARN_TOOMANYPARAMETERSVARARGS, func); + } + else if (!extraparms && arg >= t->num_parms && !p) + { + char buf[256]; + QCC_PR_ParseWarning (WARN_TOOMANYPARAMETERSFORFUNC, "too many parameters on call to %s, argument %s will be ignored", funcname, QCC_GetRefName(e, buf, sizeof(buf))); + QCC_PR_ParsePrintSRef(WARN_TOOMANYPARAMETERSFORFUNC, func); + } //with vectorcalls, we store the vector into the args as individual floats //this allows better reuse of vector constants. @@ -6600,7 +6601,10 @@ static QCC_sref_t QCC_PR_ParseFunctionCall (QCC_ref_t *funcref) //warning, the f } else*/ { - QCC_PR_ParseWarning (WARN_TOOFEWPARAMS, "too few parameters on call to %s", QCC_GetSRefName(func)); + if (func.cast->params[arg].paramname) + QCC_PR_ParseWarning (WARN_TOOFEWPARAMS, "too few parameters on call to %s, %s will be UNDEFINED", QCC_GetSRefName(func), func.cast->params[arg].paramname); + else + QCC_PR_ParseWarning (WARN_TOOFEWPARAMS, "too few parameters on call to %s", QCC_GetSRefName(func)); QCC_PR_ParsePrintSRef (WARN_TOOFEWPARAMS, func); } } @@ -14098,7 +14102,7 @@ QCC_def_t *QCC_PR_GetDef (QCC_type_t *type, const char *name, struct QCC_functio { //this is a hack. droptofloor was wrongly declared in vanilla qc, which causes problems with replacement extensions.qc. //yes, this is a selfish lazy hack for this, there's probably a better way, but at least we spit out a warning still. - QCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, "%s builtin was wrongly defined as %s. ignoring later definition",name, TypeName(type, typebuf1, sizeof(typebuf1))); + QCC_PR_ParseWarning (WARN_COMPATIBILITYHACK, "%s builtin was wrongly redefined as %s. ignoring later definition",name, TypeName(type, typebuf1, sizeof(typebuf1))); QCC_PR_ParsePrintDef(WARN_COMPATIBILITYHACK, def); } else diff --git a/engine/qclib/qccgui.c b/engine/qclib/qccgui.c index 1f9d9f9e0..ef7ab657a 100644 --- a/engine/qclib/qccgui.c +++ b/engine/qclib/qccgui.c @@ -2596,7 +2596,7 @@ static void UpdateEditorTitle(editor_t *editor) switch(editor->savefmt) { case UTF8_RAW: - encoding = "utf-8(raw)"; + encoding = "utf-8"; break; case UTF8_BOM: encoding = "utf-8(bom)"; diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index c4eac98ab..1ba9535d4 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -4725,7 +4725,7 @@ pbool QCC_main (int argc, const char **argv) //as part of the quake engine MAX_STRINGS = 1<<21; MAX_GLOBALS = 1<<17; MAX_FIELDS = 1<<13; - MAX_STATEMENTS = 1<<21; + MAX_STATEMENTS = 1<<20; MAX_FUNCTIONS = 1<<15; maxtypeinfos = 1<<16; MAX_CONSTANTS = 1<<12; diff --git a/engine/server/pr_q1qvm.c b/engine/server/pr_q1qvm.c index 7fcff9e45..b5e216b9e 100755 --- a/engine/server/pr_q1qvm.c +++ b/engine/server/pr_q1qvm.c @@ -364,16 +364,18 @@ typedef enum { emufield(maxspeed, F_FLOAT) \ emufield(movement, F_VECTOR) \ emufield(vw_index, F_FLOAT) \ - emufield(isBot, F_INT) -// emufield(items2, F_FLOAT) -// emufield(brokenankle, F_FLOAT) -// emufield(mod_admin, F_INT) -// emufield(hideentity, F_INT) -// emufield(trackent, F_INT) -// emufield(hideplayers, F_INT) -// emufield(visclients, F_INT) + emufield(isBot, F_INT) \ + emufield(items2, F_FLOAT) \ + emufield(trackent, F_INT) /*network another player instead, but not entity because of an mvdsv bug. used during bloodfest.*/ +// emufield(mod_admin, F_INT) /*enable 'cmd ban' etc when &2*/ +// emufield(hideentity, F_INT) /*backward nodrawtoclient, used by race mode spectators*/ +// emufield(hideplayers, F_INT) /*force other clients as invisible, for race mode*/ +// emufield(visclients, F_INT) /*bitfield of clients that can see this entity (borked with playerslots>32). used for 'cmd tpmsg foo', and bots.*/ +// emufield(teleported, F_INT) /*teleport angle twisting*/ +// emufield(brokenankle, F_FLOAT) /*not actually in mvdsv after all*/ -struct + +static struct { #define emufield(n,t) int n; emufields @@ -2554,6 +2556,10 @@ void Q1QVM_PostThink(void) if (fofs.vw_index) sv_player->xv->vw_index = ((float*)sv_player->v)[fofs.vw_index]; + if (fofs.items2) + sv_player->xv->items2 = ((float*)sv_player->v)[fofs.items2]; + if (fofs.trackent) + host_client->viewent = ((int*)sv_player->v)[fofs.trackent]; } void Q1QVM_StartFrame(qboolean botsarespecialsnowflakes) diff --git a/engine/server/savegame.c b/engine/server/savegame.c index 7aae3b9a9..1e8b13f84 100644 --- a/engine/server/savegame.c +++ b/engine/server/savegame.c @@ -78,7 +78,7 @@ void SV_SavegameComment (char *text, size_t textsize) text[textsize-1] = '\0'; } -pbool SV_ExtendedSaveData(pubprogfuncs_t *progfuncs, void *loadctx, const char **ptr) +pbool PDECL SV_ExtendedSaveData(pubprogfuncs_t *progfuncs, void *loadctx, const char **ptr) { char token[8192]; com_tokentype_t tt; diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index fc2bb5369..4dfca3e65 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -67,7 +67,7 @@ static int needcleanup; //int fatbytes; -void SV_ExpandNackFrames(client_t *client, int require) +void SV_ExpandNackFrames(client_t *client, int require, client_frame_t **currentframeptr) { client_frame_t *newframes; char *ptr; @@ -98,6 +98,10 @@ void SV_ExpandNackFrames(client_t *client, int require) newframes[i].senttime = realtime; } Z_Free(client->frameunion.frames); + + //if you're calling this then its because you're currently generating new frame data, and its a problem if that changes from under you. fix it up for the caller (so they can't forget to do so) + *currentframeptr = newframes+(*currentframeptr-client->frameunion.frames); + client->frameunion.frames = newframes; } @@ -336,7 +340,7 @@ void SV_EmitCSQCUpdate(client_t *client, sizebuf_t *msg, qbyte svcnumber) if (lognum > maxlog) { - SV_ExpandNackFrames(client, lognum+1); + SV_ExpandNackFrames(client, lognum+1, &frame); break; } resend[lognum].entnum = entnum; @@ -389,7 +393,7 @@ void SV_EmitCSQCUpdate(client_t *client, sizebuf_t *msg, qbyte svcnumber) if (lognum > maxlog) { - SV_ExpandNackFrames(client, lognum+1); + SV_ExpandNackFrames(client, lognum+1, &frame); break; } resend[lognum].entnum = entnum; @@ -423,7 +427,7 @@ void SV_EmitCSQCUpdate(client_t *client, sizebuf_t *msg, qbyte svcnumber) if (lognum > maxlog) { - SV_ExpandNackFrames(client, lognum+1); + SV_ExpandNackFrames(client, lognum+1, &frame); break; } resend[lognum].entnum = entnum; @@ -459,7 +463,7 @@ void SV_EmitCSQCUpdate(client_t *client, sizebuf_t *msg, qbyte svcnumber) if (lognum > maxlog) { - SV_ExpandNackFrames(client, lognum+1); + SV_ExpandNackFrames(client, lognum+1, &frame); break; } resend[lognum].entnum = entnum; @@ -1512,7 +1516,7 @@ qboolean SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizeb } if (outno >= outmax) { //expand the frames. may need some copying... - SV_ExpandNackFrames(client, outno+1); + SV_ExpandNackFrames(client, outno+1, &frame); break; } @@ -1985,7 +1989,7 @@ void SVDP_EmitEntitiesUpdate (client_t *client, client_frame_t *frame, packet_en break; /*give up if it gets full. FIXME: bone data is HUGE.*/ if (outno >= outmax) { //expand the frames. may need some copying... - SV_ExpandNackFrames(client, outno+1); + SV_ExpandNackFrames(client, outno+1, &frame); break; } diff --git a/engine/server/sv_sys_unix.c b/engine/server/sv_sys_unix.c index e9e2871ec..b1616c231 100644 --- a/engine/server/sv_sys_unix.c +++ b/engine/server/sv_sys_unix.c @@ -1090,8 +1090,15 @@ dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs) dllhandle_t *lib; lib = dlopen (name, RTLD_LAZY); + if (!lib && !strstr(name, ".so")) + lib = dlopen (va("%s.so", name), RTLD_LAZY); if (!lib) + { + const char *err = dlerror(); + //I hate this string check + Con_DLPrintf(strstr(err, "No such file or directory")?2:0,"%s\n", err); return NULL; + } if (funcs) { @@ -1103,6 +1110,7 @@ dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs) } if (funcs[i].name) { + Con_DPrintf("Unable to find symbol \"%s\" in \"%s\"\n", funcs[i].name, name); Sys_CloseLibrary((dllhandle_t*)lib); lib = NULL; } diff --git a/engine/shaders/glsl/defaultskin.glsl b/engine/shaders/glsl/defaultskin.glsl index 095dd1819..c27b41cea 100644 --- a/engine/shaders/glsl/defaultskin.glsl +++ b/engine/shaders/glsl/defaultskin.glsl @@ -37,7 +37,7 @@ #include "sys/skeletal.h" affine varying vec2 tc; -varying vec3 light; +varying vec4 light; #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) varying vec3 eyevector; #endif @@ -53,6 +53,7 @@ void main () { vec3 n, s, t, w; gl_Position = skeletaltransform_wnst(w,n,s,t); + n = normalize(n); #if defined(SPECULAR)||defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) vec3 eyeminusvertex = e_eyepos - w.xyz; eyevector.x = dot(eyeminusvertex, s.xyz); @@ -70,7 +71,11 @@ void main () float d = dot(n,e_light_dir); if (d < 0.0) //vertex shader. this might get ugly, but I don't really want to make it per vertex. d = 0.0; //this avoids the dark side going below the ambient level. - light = e_light_ambient + (d*e_light_mul); + light.rgb = e_light_ambient + (d*e_light_mul); + light.a = 1.0; +#ifdef VC + light *= v_colour; +#endif //FIXME: Software rendering imitation should possibly push out normals by half a pixel or something to approximate software's over-estimation of distant model sizes (small models are drawn using JUST their verticies using the nearest pixel, which results in larger meshes) @@ -99,8 +104,8 @@ in vec3 normal[]; out vec3 t_normal[]; affine in vec2 tc[]; affine out vec2 t_tc[]; -in vec3 light[]; -out vec3 t_light[]; +in vec4 light[]; +out vec4 t_light[]; #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) in vec3 eyevector[]; out vec3 t_eyevector[]; @@ -148,8 +153,8 @@ in vec3 t_vertex[]; in vec3 t_normal[]; affine in vec2 t_tc[]; affine out vec2 tc; -in vec3 t_light[]; -out vec3 light; +in vec4 t_light[]; +out vec4 light; #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) in vec3 t_eyevector[]; out vec3 eyevector; @@ -212,7 +217,7 @@ uniform float cvar_gl_specular; #endif affine varying vec2 tc; -varying vec3 light; +varying vec4 light; #if defined(SPECULAR) || defined(OFFSETMAPPING) || defined(REFLECTCUBEMASK) varying vec3 eyevector; #endif @@ -269,8 +274,7 @@ void main () col.rgb += texture2D(s_reflectmask, tc).rgb * textureCube(s_reflectcube, rtc).rgb; #endif - col.rgb *= light; - col *= e_colourident; + col *= light * e_colourident; #ifdef FULLBRIGHT vec4 fb = texture2D(s_fullbright, tc); diff --git a/engine/shaders/glsl/defaultwarp.glsl b/engine/shaders/glsl/defaultwarp.glsl index eb8b629e1..f2ea5a99a 100644 --- a/engine/shaders/glsl/defaultwarp.glsl +++ b/engine/shaders/glsl/defaultwarp.glsl @@ -44,6 +44,6 @@ void main () ts *= (texture2D(s_lightmap, lm0) * e_lmscale).rgb; #endif - gl_FragColor = fog4(vec4(ts, USEALPHA) * e_colourident); + gl_FragColor = fog4blend(vec4(ts, USEALPHA) * e_colourident); } #endif diff --git a/engine/shaders/glsl/rtlight.glsl b/engine/shaders/glsl/rtlight.glsl index 117e96916..b9eda3059 100644 --- a/engine/shaders/glsl/rtlight.glsl +++ b/engine/shaders/glsl/rtlight.glsl @@ -9,7 +9,9 @@ !!cvarf r_glsl_offsetmapping_scale !!cvardf r_glsl_pcf !!cvardf r_tessellation_level=5 -!!samps shadowmap diffuse normalmap specular upper lower reflectcube reflectmask projectionmap +!!samps diffuse normalmap specular upper lower reflectcube reflectmask +!!samps =PCF shadowmap +!!samps =CUBE projectionmap #include "sys/defs.h" diff --git a/engine/shaders/glsl/terrain.glsl b/engine/shaders/glsl/terrain.glsl index 39c686256..a79f88ee5 100644 --- a/engine/shaders/glsl/terrain.glsl +++ b/engine/shaders/glsl/terrain.glsl @@ -1,8 +1,9 @@ !!permu FOG //t0-t3 are the diffusemaps, t4 is the blend factors -!!samps 5 -!!samps =PCF 6 -!!samps =CUBE 7 +!!samps 4 +!!samps mix=4 +!!samps =PCF shadowmap +!!samps =CUBE projectionmap //light levels @@ -95,7 +96,7 @@ uniform vec4 e_lmscale; void main (void) { vec4 r; - vec4 m = texture2D(s_t4, lm); + vec4 m = texture2D(s_mix, lm); r = texture2D(s_t0, tc)*m.r; r += texture2D(s_t1, tc)*m.g; @@ -129,13 +130,13 @@ void main (void) colorscale *= 1.0-(dot(spot,spot)); #endif #ifdef PCF - colorscale *= ShadowmapFilter(s_t5, vtexprojcoord); + colorscale *= ShadowmapFilter(s_shadowmap, vtexprojcoord); #endif r.rgb *= colorscale * l_lightcolour; #ifdef CUBE - r.rgb *= textureCube(s_t6, vtexprojcoord.xyz).rgb; + r.rgb *= textureCube(s_projectionmap, vtexprojcoord.xyz).rgb; #endif gl_FragColor = fog4additive(r); diff --git a/engine/vk/vk_init.c b/engine/vk/vk_init.c index 81b50c382..ec7fbf309 100644 --- a/engine/vk/vk_init.c +++ b/engine/vk/vk_init.c @@ -1300,12 +1300,15 @@ vk_image_t VK_CreateTexture2DArray(uint32_t width, uint32_t height, uint32_t lay case PTI_ARGB4444: /*format = VK_FORMAT_A4R4G4B4_UNORM_PACK16;*/ break; case PTI_RGBA5551: format = VK_FORMAT_R5G5B5A1_UNORM_PACK16; break; case PTI_ARGB1555: format = VK_FORMAT_A1R5G5B5_UNORM_PACK16; break; + case PTI_R16: format = VK_FORMAT_R16_UNORM; break; + case PTI_RGBA16: format = VK_FORMAT_R16G16B16A16_UNORM; break; //float formats case PTI_R16F: format = VK_FORMAT_R16_SFLOAT; break; case PTI_R32F: format = VK_FORMAT_R32_SFLOAT; break; case PTI_RGBA16F: format = VK_FORMAT_R16G16B16A16_SFLOAT; break; case PTI_RGBA32F: format = VK_FORMAT_R32G32B32A32_SFLOAT; break; //weird formats + case PTI_P8: case PTI_R8: format = VK_FORMAT_R8_UNORM; break; case PTI_RG8: format = VK_FORMAT_R8G8_UNORM; break; case PTI_R8_SNORM: format = VK_FORMAT_R8_SNORM; break; @@ -2395,6 +2398,7 @@ static qboolean VK_R_RenderScene_Cubemap(struct vk_rendertarg *fb) Con_Printf("no flush\n"); VKBE_RT_End(&rtc->face[i]); + r_framecount++; } r_refdef.vrect = vrect; @@ -3887,6 +3891,7 @@ void VK_CheckTextureFormats(void) {PTI_ARGB1555, VK_FORMAT_A1R5G5B5_UNORM_PACK16, VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT}, {PTI_RGBA16F, VK_FORMAT_R16G16B16A16_SFLOAT, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT|VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT|VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT}, {PTI_RGBA32F, VK_FORMAT_R32G32B32A32_SFLOAT, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT|VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT|VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT}, + {PTI_P8, VK_FORMAT_R8_UNORM, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT}, {PTI_R8, VK_FORMAT_R8_UNORM, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT}, {PTI_RG8, VK_FORMAT_R8G8_UNORM, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT}, {PTI_R8_SNORM, VK_FORMAT_R8_SNORM, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT}, diff --git a/engine/web/ftejslib.js b/engine/web/ftejslib.js index 629878ca8..85d5eb1b4 100644 --- a/engine/web/ftejslib.js +++ b/engine/web/ftejslib.js @@ -1143,7 +1143,6 @@ console.log("onerror: " + _url); var gltex = GL.textures[texid]; img.onload = function() { - console.log("Image Callback called!"); var oldtex = GLctx.getParameter(GLctx.TEXTURE_BINDING_2D); //blurgh, try to avoid breaking anything in this unexpected event. GLctx.bindTexture(GLctx.TEXTURE_2D, gltex); GLctx.texImage2D(GLctx.TEXTURE_2D, 0, GLctx.RGBA, GLctx.RGBA, GLctx.UNSIGNED_BYTE, img); diff --git a/fteqtv/cmd.h b/fteqtv/cmd.h index edc8dc5ac..bc6156fbe 100755 --- a/fteqtv/cmd.h +++ b/fteqtv/cmd.h @@ -4,7 +4,7 @@ typedef struct cmdctxt_s cmdctxt_t; struct cmdctxt_s { cluster_t *cluster; - sv_t *qtv; + sv_t *qtv; int streamid; //streamid, which is valid even if qtv is not, for specifying the streamid to use on connects char *arg[MAX_ARGS]; int argc; @@ -16,7 +16,7 @@ struct cmdctxt_s { typedef void (*consolecommand_t) (cmdctxt_t *ctx); -void Cmd_Printf(cmdctxt_t *ctx, char *fmt, ...); +void Cmd_Printf(cmdctxt_t *ctx, char *fmt, ...) PRINTFWARNING(2); #define Cmd_Argc(ctx) ctx->argc #define Cmd_Argv(ctx, num) (((unsigned int)ctx->argc <= (unsigned int)(num))?"": ctx->arg[num]) #define Cmd_IsLocal(ctx) ctx->localcommand diff --git a/fteqtv/control.c b/fteqtv/control.c index 5fca39eed..179079c5a 100644 --- a/fteqtv/control.c +++ b/fteqtv/control.c @@ -291,7 +291,7 @@ void Cluster_Run(cluster_t *cluster, qboolean dowait) if (cluster->inputlength > 0) { Sys_Printf(cluster, "%c", c); - Sys_Printf(cluster, " ", c); + Sys_Printf(cluster, " "); Sys_Printf(cluster, "%c", c); cluster->inputlength--; @@ -466,12 +466,14 @@ int main(int argc, char **argv) cluster = malloc(sizeof(*cluster)); if (cluster) { + int j; memset(cluster, 0, sizeof(*cluster)); - cluster->qwdsocket[0] = INVALID_SOCKET; - cluster->qwdsocket[1] = INVALID_SOCKET; - cluster->tcpsocket[0] = INVALID_SOCKET; - cluster->tcpsocket[1] = INVALID_SOCKET; + for (j = 0; j < SOCKETGROUPS; j++) + { + cluster->qwdsocket[j] = INVALID_SOCKET; + cluster->tcpsocket[j] = INVALID_SOCKET; + } cluster->anticheattime = 1*1000; cluster->tooslowdelay = 100; cluster->qwlistenportnum = 0; @@ -487,18 +489,19 @@ int main(int argc, char **argv) if (!cluster->numservers) { //probably running on a home user's computer - if (cluster->qwdsocket[0] == INVALID_SOCKET && cluster->qwdsocket[1] == INVALID_SOCKET && !cluster->qwlistenportnum) + if (cluster->qwdsocket[SG_IPV4] == INVALID_SOCKET && cluster->qwdsocket[SG_IPV6] == INVALID_SOCKET && !cluster->qwlistenportnum) { cluster->qwlistenportnum = 27599; - NET_InitUDPSocket(cluster, cluster->qwlistenportnum, true); - NET_InitUDPSocket(cluster, cluster->qwlistenportnum, false); + NET_InitUDPSocket(cluster, cluster->qwlistenportnum, SG_IPV6); + NET_InitUDPSocket(cluster, cluster->qwlistenportnum, SG_IPV4); } - if (cluster->tcpsocket[0] == INVALID_SOCKET && cluster->tcpsocket[1] == INVALID_SOCKET && !cluster->tcplistenportnum) + if (cluster->tcpsocket[SG_IPV4] == INVALID_SOCKET && cluster->tcpsocket[SG_IPV6] == INVALID_SOCKET && !cluster->tcplistenportnum) { cluster->tcplistenportnum = 27599; - Net_TCPListen(cluster, cluster->tcplistenportnum, true); - Net_TCPListen(cluster, cluster->tcplistenportnum, false); + Net_TCPListen(cluster, cluster->tcplistenportnum, SG_IPV6); + Net_TCPListen(cluster, cluster->tcplistenportnum, SG_IPV4); } + Net_TCPListen(cluster, 1, SG_UNIX); Sys_Printf(cluster, "\n" "Welcome to FTEQTV\n" diff --git a/fteqtv/forward.c b/fteqtv/forward.c index a40ef0026..dfa88012d 100644 --- a/fteqtv/forward.c +++ b/fteqtv/forward.c @@ -122,7 +122,7 @@ void Fwd_ParseCommands(cluster_t *cluster, oproxy_t *prox) bytes = NET_WebSocketRecv(prox->sock, &prox->websocket, prox->inbuffer+prox->inbuffersize, sizeof(prox->inbuffer)-prox->inbuffersize, NULL); if (bytes < 0) { - if (qerrno != EWOULDBLOCK && qerrno != EAGAIN) //not a problem, so long as we can flush it later. + if (qerrno != NET_EWOULDBLOCK && qerrno != NET_EAGAIN) //not a problem, so long as we can flush it later. { Sys_Printf(cluster, "network error from client proxy\n"); prox->drop = true; //drop them if we get any errors @@ -215,7 +215,7 @@ void Net_TryFlushProxyBuffer(cluster_t *cluster, oproxy_t *prox) prox->flushing = false; break; case -1: - if (qerrno != EWOULDBLOCK && qerrno != EAGAIN) //not a problem, so long as we can flush it later. + if (qerrno != NET_EWOULDBLOCK && qerrno != NET_EAGAIN) //not a problem, so long as we can flush it later. { Sys_Printf(cluster, "network error from client proxy\n"); prox->drop = true; //drop them if we get any errors diff --git a/fteqtv/httpsv.c b/fteqtv/httpsv.c index 9c37f0443..eb5077d7e 100644 --- a/fteqtv/httpsv.c +++ b/fteqtv/httpsv.c @@ -7,7 +7,7 @@ size_t SHA1(unsigned char *digest, size_t maxdigestsize, const unsigned char *st void tobase64(unsigned char *out, int outlen, unsigned char *in, int inlen) { - static tab[64] = + static unsigned char tab[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', @@ -730,7 +730,7 @@ static void HTTPSV_GenerateAdmin(cluster_t *cluster, oproxy_t *dest, int streami static void HTTPSV_GenerateDemoListing(cluster_t *cluster, oproxy_t *dest, char *args) { int i; - char link[256]; + char link[512]; char *s; qboolean plugframe = false; for (s=args; *s && *s != ' ';) diff --git a/fteqtv/menu.c b/fteqtv/menu.c index 42bd78e29..b52d46652 100644 --- a/fteqtv/menu.c +++ b/fteqtv/menu.c @@ -252,7 +252,7 @@ void WriteStringSelection(netmsg_t *b, qboolean selected, const char *str) void Menu_Draw(cluster_t *cluster, viewer_t *viewer) { char buffer[2048]; - char str[64]; + char str[256]; sv_t *sv; int i, min; unsigned char *s; diff --git a/fteqtv/netchan.c b/fteqtv/netchan.c index 980669988..3994cdbd5 100644 --- a/fteqtv/netchan.c +++ b/fteqtv/netchan.c @@ -239,7 +239,7 @@ void NET_SendPacket(cluster_t *cluster, SOCKET sock, int length, void *data, net if (ret < 0) { int er = qerrno; - if (er == EWOULDBLOCK || er == EAGAIN) + if (er == NET_EWOULDBLOCK || er == NET_EAGAIN) return; Sys_Printf(cluster, "udp send error %i (%s)\n", er, strerror(er)); diff --git a/fteqtv/parse.c b/fteqtv/parse.c index a7f858d2a..3d5dc8f78 100644 --- a/fteqtv/parse.c +++ b/fteqtv/parse.c @@ -210,7 +210,7 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma Sys_Printf(tv->cluster, "ParseMessage: PROTOCOL_VERSION_FTE2 (%x) not supported\n", protocol & ~supported); continue; case PROTOCOL_VERSION_HUFFMAN: - Sys_Printf(tv->cluster, "ParseMessage: PROTOCOL_VERSION_HUFFMAN not supported\n", protocol); + Sys_Printf(tv->cluster, "ParseMessage: PROTOCOL_VERSION_HUFFMAN not supported\n"); ParseError(m); return; case PROTOCOL_VERSION_VARLENGTH: @@ -236,7 +236,7 @@ static void ParseServerData(sv_t *tv, netmsg_t *m, int to, unsigned int playerma continue; case PROTOCOL_VERSION_FRAGMENT: protocol = ReadLong(m); - Sys_Printf(tv->cluster, "ParseMessage: PROTOCOL_VERSION_FRAGMENT not supported\n", protocol); + Sys_Printf(tv->cluster, "ParseMessage: PROTOCOL_VERSION_FRAGMENT not supported\n"); ParseError(m); return; default: @@ -989,6 +989,7 @@ static void ParsePacketEntities(sv_t *tv, netmsg_t *m, int deltaframe) tv->physicstime = tv->curtime; if (tv->cluster->chokeonnotupdated) + { for (v = tv->cluster->viewers; v; v = v->next) { if (v->server == tv) @@ -999,6 +1000,7 @@ static void ParsePacketEntities(sv_t *tv, netmsg_t *m, int deltaframe) if (v->server == tv && v->netchan.isnqprotocol) v->maysend = true; } + } if (deltaframe != -1) diff --git a/fteqtv/qtv.h b/fteqtv/qtv.h index 21dd2dbfa..73d4e0f62 100644 --- a/fteqtv/qtv.h +++ b/fteqtv/qtv.h @@ -118,14 +118,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #pragma comment (lib, "wsock32.lib") #endif #define qerrno WSAGetLastError() - #define EWOULDBLOCK WSAEWOULDBLOCK - #define EINPROGRESS WSAEINPROGRESS - #define ECONNREFUSED WSAECONNREFUSED - #define ENOTCONN WSAENOTCONN + #define NET_EWOULDBLOCK WSAEWOULDBLOCK + #define NET_EINPROGRESS WSAEINPROGRESS + #define NET_ECONNREFUSED WSAECONNREFUSED + #define NET_ENOTCONN WSAENOTCONN //we have special functions to properly terminate sprintf buffers in windows. //we assume other systems are designed with even a minor thought to security. - #if !defined(__MINGW32_VERSION) + #if !defined(__MINGW32__) #define unlink _unlink //why do MS have to be so awkward? int snprintf(char *buffer, int buffersize, char *format, ...) PRINTFWARNING(3); int vsnprintf(char *buffer, int buffersize, const char *format, va_list argptr); @@ -189,8 +189,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. //try the cygwin ones #endif -#ifndef EAGAIN -#define EAGAIN EWOULDBLOCK +#ifndef NET_EWOULDBLOCK +#define NET_EWOULDBLOCK EWOULDBLOCK +#define NET_EINPROGRESS EINPROGRESS +#define NET_ECONNREFUSED ECONNREFUSED +#define NET_ENOTCONN ENOTCONN +#endif + +#ifndef NET_EAGAIN +#define NET_EAGAIN NET_EWOULDBLOCK #endif #ifndef IPV6_V6ONLY #define IPV6_V6ONLY 27 @@ -229,6 +236,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. size_t strlcpy(char *dst, const char *src, size_t siz); +#define SHA1 SHA1_quake size_t SHA1(unsigned char *digest, size_t maxdigestsize, const unsigned char *string, size_t stringlen); @@ -236,8 +244,12 @@ size_t SHA1(unsigned char *digest, size_t maxdigestsize, const unsigned char *st #define Sys_Printf QTVSys_Printf #endif +#ifndef STRINGIFY +#define STRINGIFY2(s) #s +#define STRINGIFY(s) STRINGIFY2(s) +#endif #ifdef SVNREVISION -#define QTV_VERSION_STRING SVNREVISION +#define QTV_VERSION_STRING STRINGIFY(SVNREVISION) #else //#include "../engine/common/bothdefs.h" //#define QTV_VERSION_STRING STRINGIFY(FTE_VER_MAJOR)"."STRINGIFY(FTE_VER_MINOR) @@ -748,10 +760,16 @@ typedef struct { int time, smalltime; } availdemo_t; -#define SOCKETGROUPS 2 +enum +{ + SG_IPV4, + SG_IPV6, + SG_UNIX, + SOCKETGROUPS +}; struct cluster_s { - SOCKET qwdsocket[2]; //udp + quakeworld protocols - SOCKET tcpsocket[2]; //tcp listening socket (for mvd and listings and stuff) + SOCKET qwdsocket[SOCKETGROUPS]; //udp + quakeworld protocols + SOCKET tcpsocket[SOCKETGROUPS]; //tcp listening socket (for mvd and listings and stuff) tcpconnect_t *tcpconnects; //'tcpconnect' qizmo-compatible quakeworld-over-tcp connection char commandinput[512]; @@ -881,12 +899,15 @@ void ParseMessage(sv_t *tv, void *buffer, int length, int to, int mask); void BuildServerData(sv_t *tv, netmsg_t *msg, int servercount, viewer_t *spectatorflag); void BuildNQServerData(sv_t *tv, netmsg_t *msg, qboolean mvd, int servercount); void QW_UpdateUDPStuff(cluster_t *qtv); +void QW_TCPConnection(cluster_t *cluster, SOCKET sock, wsrbuf_t ws); unsigned int Sys_Milliseconds(void); void Prox_SendInitialEnts(sv_t *qtv, oproxy_t *prox, netmsg_t *msg); qboolean QTV_ConnectStream(sv_t *qtv, char *serverurl); void QTV_ShutdownStream(sv_t *qtv); qboolean NET_StringToAddr (char *s, netadr_t *sadr, int defaultport); void QTV_Printf(sv_t *qtv, char *format, ...) PRINTFWARNING(2); +void QTV_UpdatedServerInfo(sv_t *tv); +void QTV_CleanupMap(sv_t *qtv); void SendBufferToViewer(viewer_t *v, const char *buffer, int length, qboolean reliable); void QW_PrintfToViewer(viewer_t *v, char *format, ...) PRINTFWARNING(2); @@ -894,6 +915,11 @@ void QW_StuffcmdToViewer(viewer_t *v, char *format, ...) PRINTFWARNING(2); void QW_StreamPrint(cluster_t *cluster, sv_t *server, viewer_t *allbut, char *message); void QW_StreamStuffcmd(cluster_t *cluster, sv_t *server, char *fmt, ...) PRINTFWARNING(3); void QTV_SayCommand(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *fullcommand); //execute a command from a view +void QW_SetViewersServer(cluster_t *cluster, viewer_t *viewer, sv_t *sv); +void QW_SetMenu(viewer_t *v, int menunum); +void QW_SetCommentator(cluster_t *cluster, viewer_t *v, viewer_t *commentator); +void QW_FreeViewer(cluster_t *cluster, viewer_t *viewer); +void QW_ClearViewerState(viewer_t *viewer); void PM_PlayerMove (pmove_t *pmove); @@ -924,14 +950,11 @@ qboolean BSP_Visible(bsp_t *bsp, int leafcount, unsigned short *list); void BSP_SetupForPosition(bsp_t *bsp, float x, float y, float z); const intermission_t *BSP_IntermissionSpot(bsp_t *bsp); -void QW_SetViewersServer(cluster_t *cluster, viewer_t *viewer, sv_t *sv); unsigned short QCRC_Block (void *start, int count); unsigned short QCRC_Value(unsigned short crcvalue); void WriteDeltaUsercmd (netmsg_t *m, const usercmd_t *from, usercmd_t *move); void SendClientCommand(sv_t *qtv, char *fmt, ...) PRINTFWARNING(2); void QTV_Run(sv_t *qtv); -void QW_FreeViewer(cluster_t *cluster, viewer_t *viewer); -void QW_SetMenu(viewer_t *v, int menunum); char *COM_ParseToken (char *data, char *out, int outsize, const char *punctuation); char *Info_ValueForKey (char *s, const char *key, char *buffer, int buffersize); @@ -942,11 +965,13 @@ void Com_BlockFullChecksum (void *buffer, int len, unsigned char *outbuf); void Cluster_BuildAvailableDemoList(cluster_t *cluster); void Sys_Printf(cluster_t *cluster, char *fmt, ...) PRINTFWARNING(2); +//void Sys_mkdir(char *path); +void QTV_mkdir(char *path); void Net_ProxySend(cluster_t *cluster, oproxy_t *prox, void *buffer, int length); oproxy_t *Net_FileProxy(sv_t *qtv, char *filename); sv_t *QTV_NewServerConnection(cluster_t *cluster, int streamid, char *server, char *password, qboolean force, enum autodisconnect_e autodisconnect, qboolean noduplicates, qboolean query); -void Net_TCPListen(cluster_t *cluster, int port, qboolean ipv6); +void Net_TCPListen(cluster_t *cluster, int port, int socketid); qboolean Net_StopFileProxy(sv_t *qtv); @@ -954,18 +979,24 @@ void SV_FindProxies(SOCKET sock, cluster_t *cluster, sv_t *defaultqtv); qboolean SV_ReadPendingProxy(cluster_t *cluster, oproxy_t *pend); void SV_ForwardStream(sv_t *qtv, void *buffer, int length); int SV_SayToUpstream(sv_t *qtv, char *message); +void SV_SayToViewers(sv_t *qtv, char *message); unsigned char *FS_ReadFile(char *gamedir, char *filename, unsigned int *size); void ChooseFavoriteTrack(sv_t *tv); void DemoViewer_Update(sv_t *svtest); +void Fwd_SayToDownstream(sv_t *qtv, char *message); //httpsv.c void HTTPSV_GetMethod(cluster_t *cluster, oproxy_t *pend); void HTTPSV_PostMethod(cluster_t *cluster, oproxy_t *pend, char *postdata); +//menu.c +void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum); +void Menu_Draw(cluster_t *cluster, viewer_t *viewer); + #ifdef __cplusplus } diff --git a/fteqtv/qw.c b/fteqtv/qw.c index 0aae68006..702ddfd34 100644 --- a/fteqtv/qw.c +++ b/fteqtv/qw.c @@ -45,8 +45,6 @@ void QTV_DefaultMovevars(movevars_t *vars) } -void Menu_Enter(cluster_t *cluster, viewer_t *viewer, int buttonnum); - const usercmd_t nullcmd = {0}; #define CM_ANGLE1 (1<<0) @@ -392,7 +390,7 @@ void SendNQSpawnInfoToViewer(cluster_t *cluster, viewer_t *viewer, netmsg_t *msg int SendCurrentUserinfos(sv_t *tv, int cursize, netmsg_t *msg, int i, int thisplayer) { - char name[MAX_QPATH]; + char name[1024]; if (i < 0) return i; @@ -2303,7 +2301,7 @@ void UpdateStats(sv_t *qtv, viewer_t *v) netmsg_t msg; char buf[6]; int i; - const static unsigned int nullstats[MAX_STATS] = {1000}; + static const unsigned int nullstats[MAX_STATS] = {1000}; const unsigned int *stats; @@ -2467,6 +2465,7 @@ void QTV_SayCommand(cluster_t *cluster, sv_t *qtv, viewer_t *v, char *fullcomman while(*args && *args <= ' ') args++; +#pragma message("fixme: These all need testing") if (!strcmp(command, "help")) { QW_PrintfToViewer(v, "Website: "PROXYWEBSITE"\n" @@ -2673,7 +2672,7 @@ I've removed the following from this function as it covered the menu (~Moodles): if (!shownheader) { shownheader = true; - QW_StuffcmdToViewer(v, "menutext 72 %i \"Áãôéöå Çáíåóº\"\n", y); + QW_StuffcmdToViewer(v, "menutext 72 %i \"Áãôéöå Çáíåóº\"\n", y); y+=8; } QW_StuffcmdToViewer(v, "menutext 32 %i \"%30s\" \"stream %i\"\n", y, *sv->map.hostname?sv->map.hostname:sv->server, sv->streamid); @@ -2902,7 +2901,7 @@ tuiadmin: { char buf[256]; - snprintf(buf, sizeof(buf), "[QuakeTV] %s\n", qtv->serveraddress); + snprintf(buf, sizeof(buf), "[QuakeTV] %s\n", qtv->server); // Print a short line with info about the server QW_PrintfToViewer(v, buf); } @@ -4206,7 +4205,6 @@ void SendViewerPackets(cluster_t *cluster, viewer_t *v) void QW_ProcessUDPPacket(cluster_t *cluster, netmsg_t *m, netadr_t from) { char tempbuffer[256]; - int fromsize = sizeof(from); int qport; viewer_t *v; @@ -4331,10 +4329,10 @@ void QW_ProcessUDPPacket(cluster_t *cluster, netmsg_t *m, netadr_t from) if (ReadByte(m) == NQ_NETCHAN_VERSION) { //proquake extensions - int mod = ReadByte(m); - int modver = ReadByte(m); - int flags = ReadByte(m); - int passwd = ReadLong(m); + /*int mod =*/ ReadByte(m); + /*int modver =*/ ReadByte(m); + /*int flags =*/ ReadByte(m); + /*int passwd =*/ ReadLong(m); //fte extension, sent so that dual-protocol servers will not create connections for dual-protocol clients //the nqconnect command disables this (as well as the qw hand shake) if you really want to use nq protocols with fte clients @@ -4462,7 +4460,7 @@ void QW_UpdateUDPStuff(cluster_t *cluster) tc->inbuffersize += read; if (read == 0 || read < 0) { - if (read == 0 || qerrno != EWOULDBLOCK) + if (read == 0 || qerrno != NET_EWOULDBLOCK) { *l = tc->next; closesocket(tc->sock); diff --git a/fteqtv/rcon.c b/fteqtv/rcon.c index 9942dfe37..8dc859142 100644 --- a/fteqtv/rcon.c +++ b/fteqtv/rcon.c @@ -607,14 +607,12 @@ void Cmd_Status(cmdctxt_t *ctx) Cmd_Printf(ctx, " Talking allowed\n"); if (ctx->cluster->nobsp) Cmd_Printf(ctx, " No BSP loading\n"); - if (ctx->cluster->tcpsocket[0] != INVALID_SOCKET || ctx->cluster->tcpsocket[1] != INVALID_SOCKET) - { + if (ctx->cluster->tcpsocket[SG_UNIX] != INVALID_SOCKET) + Cmd_Printf(ctx, " unix socket open\n"); + if (ctx->cluster->tcpsocket[SG_IPV4] != INVALID_SOCKET || ctx->cluster->tcpsocket[SG_IPV6] != INVALID_SOCKET) Cmd_Printf(ctx, " tcp port %i\n", ctx->cluster->tcplistenportnum); - } - if (ctx->cluster->qwdsocket[0] != INVALID_SOCKET || ctx->cluster->qwdsocket[1] != INVALID_SOCKET) - { + if (ctx->cluster->qwdsocket[SG_IPV4] != INVALID_SOCKET || ctx->cluster->qwdsocket[SG_IPV6] != INVALID_SOCKET) Cmd_Printf(ctx, " udp port %i\n", ctx->cluster->qwlistenportnum); - } Cmd_Printf(ctx, " user connections are %sallowed\n", ctx->cluster->nouserconnects?"NOT ":""); Cmd_Printf(ctx, "\n"); diff --git a/fteqtv/source.c b/fteqtv/source.c index 324d44b05..3d5a8d382 100644 --- a/fteqtv/source.c +++ b/fteqtv/source.c @@ -62,6 +62,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #endif +#include +#ifdef UNIXSOCKETS +#include +#endif + #define RECONNECT_TIME (1000*30) #define RECONNECT_TIME_DEMO (1000*5) #define UDPRECONNECT_TIME (1000) @@ -238,13 +243,19 @@ qboolean Net_CompareAddress(netadr_t *s1, netadr_t *s2, int qp1, int qp2) return false; } -void Net_TCPListen(cluster_t *cluster, int port, qboolean ipv6) +void Net_TCPListen(cluster_t *cluster, int port, int socketid) { SOCKET sock; - struct sockaddr_in address4; - struct sockaddr_in6 address6; - struct sockaddr *address; + union + { + struct sockaddr s; + struct sockaddr_in ipv4; + struct sockaddr_in6 ipv6; +#ifdef UNIXSOCKETS + struct sockaddr_un un; +#endif + } address; int prot; int addrsize; int _true = true; @@ -252,55 +263,69 @@ void Net_TCPListen(cluster_t *cluster, int port, qboolean ipv6) unsigned long nonblocking = true; unsigned long v6only = false; + const char *famname; - if (ipv6) + switch(socketid) { - prot = PF_INET6; - memset(&address6, 0, sizeof(address6)); - address6.sin6_family = AF_INET6; - address6.sin6_port = htons((u_short)port); - address = (struct sockaddr *)&address6; +#ifdef UNIXSOCKETS + case SG_UNIX: + prot = AF_UNIX; + memset(&address.un, 0, sizeof(address.un)); + address.un.sun_family = prot; + memcpy(address.un.sun_path, "\0qtv", 4); + addrsize = offsetof(struct sockaddr_un, sun_path[4]); + famname = "unix"; + break; +#endif + case SG_IPV6: + prot = AF_INET6; + memset(&address.ipv6, 0, sizeof(address.ipv6)); + address.ipv6.sin6_family = prot; + address.ipv6.sin6_port = htons((u_short)port); addrsize = sizeof(struct sockaddr_in6); - } - else - { - prot = PF_INET; - address4.sin_family = AF_INET; - address4.sin_addr.s_addr = INADDR_ANY; - address4.sin_port = htons((u_short)port); - address = (struct sockaddr *)&address4; + if (v6only) + famname = "tcp6"; + else + famname = "tcp"; + break; + case SG_IPV4: + prot = AF_INET; + address.ipv4.sin_family = prot; + address.ipv4.sin_addr.s_addr = INADDR_ANY; + address.ipv4.sin_port = htons((u_short)port); addrsize = sizeof(struct sockaddr_in); + famname = "tcp4"; + break; + default: + return; //some kind of error. avoid unintialised warnings. } - if (!ipv6 && !v6only && cluster->tcpsocket[1] != INVALID_SOCKET) - { + if (socketid==SG_IPV4 && !v6only && cluster->tcpsocket[SG_IPV6] != INVALID_SOCKET) + { //if we already have a hybrid ipv6 socket, don't bother with ipv4 too int sz = sizeof(v6only); if (getsockopt(cluster->tcpsocket[1], IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, &sz) == 0 && !v6only) port = 0; } - if (cluster->tcpsocket[ipv6] != INVALID_SOCKET) + if (cluster->tcpsocket[socketid] != INVALID_SOCKET) { - closesocket(cluster->tcpsocket[ipv6]); - cluster->tcpsocket[ipv6] = INVALID_SOCKET; + closesocket(cluster->tcpsocket[socketid]); + cluster->tcpsocket[socketid] = INVALID_SOCKET; - if (v6only) - Sys_Printf(cluster, "closed tcp%i port\n", ipv6?6:4); - else - Sys_Printf(cluster, "closed tcp port\n"); + Sys_Printf(cluster, "closed %s port\n", famname); } if (!port) return; - if ((sock = socket (prot, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) + if ((sock = socket (prot, SOCK_STREAM, 0)) == INVALID_SOCKET) { - cluster->tcpsocket[ipv6] = INVALID_SOCKET; + cluster->tcpsocket[socketid] = INVALID_SOCKET; return; } if (ioctlsocket (sock, FIONBIO, &nonblocking) == -1) { - cluster->tcpsocket[ipv6] = INVALID_SOCKET; + cluster->tcpsocket[socketid] = INVALID_SOCKET; closesocket(sock); return; } @@ -312,11 +337,17 @@ void Net_TCPListen(cluster_t *cluster, int port, qboolean ipv6) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&_true, sizeof(_true)); - if (prot == AF_INET6) + if (socketid == SG_IPV6) + { if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&v6only, sizeof(v6only)) == -1) v6only = true; + if (v6only) + famname = "tcp6"; + else + famname = "tcp"; + } - if (bind (sock, address, addrsize) == -1) + if (bind (sock, &address.s, addrsize) == -1) { printf("socket bind error %i (%s)\n", qerrno, strerror(qerrno)); closesocket(sock); @@ -325,12 +356,9 @@ void Net_TCPListen(cluster_t *cluster, int port, qboolean ipv6) listen(sock, 2); //don't listen for too many clients at once. - if (v6only) - Sys_Printf(cluster, "opened tcp%i port %i\n", ipv6?6:4, port); - else - Sys_Printf(cluster, "opened tcp port %i\n", port); + Sys_Printf(cluster, "opened %s port %i\n", famname, port); - cluster->tcpsocket[ipv6] = sock; + cluster->tcpsocket[socketid] = sock; } char *strchrrev(char *str, char chr) @@ -533,7 +561,7 @@ qboolean Net_ConnectToTCPServer(sv_t *qtv, char *ip) if (connect(qtv->sourcesock, (struct sockaddr *)&qtv->serveraddress.sockaddr, asz) == INVALID_SOCKET) { err = qerrno; - if (err != EINPROGRESS && err != EAGAIN && err != EWOULDBLOCK) //bsd sockets are meant to return EINPROGRESS, but some winsock drivers use EWOULDBLOCK instead. *sigh*... + if (err != NET_EINPROGRESS && err != NET_EAGAIN && err != NET_EWOULDBLOCK) //bsd sockets are meant to return EINPROGRESS, but some winsock drivers use EWOULDBLOCK instead. *sigh*... { closesocket(qtv->sourcesock); qtv->sourcesock = INVALID_SOCKET; @@ -679,7 +707,7 @@ qboolean Net_ConnectToDemoServer(sv_t* qtv, char* ip, char* dir) qtv->filelength = ftell(qtv->sourcefile); //attempt to detect the end of the file - fseek(qtv->sourcefile, 0-sizeof(smallbuffer), SEEK_CUR); + fseek(qtv->sourcefile, 0-(long)sizeof(smallbuffer), SEEK_CUR); fread(smallbuffer, 1, 17, qtv->sourcefile); //0 is the time if (smallbuffer[1] == dem_all || smallbuffer[1] == dem_read) //mvdsv changed it to read... @@ -718,106 +746,108 @@ qboolean Net_ConnectToDemoDirServer(sv_t* qtv, char *ip) Sys_Printf(qtv->cluster, "Windows support coming soon!\n"); return false; #else - DIR *dir; - struct dirent* ent; - - dir = opendir(fullname); - if (dir) { - char demoname[512]; - int current_demo = 0; - int file_count = 0; - int random_number = 1; // always this value if the directory contains one file + DIR *dir; + struct dirent* ent; - // count the files, important for determining a random demo file - while ((ent = readdir(dir)) != NULL) - { - int len; - - // only count files neding in .mvd - len = strlen(ent->d_name); - if (len < 5) - { - continue; - } - if (strcmp(ent->d_name+len-4, ".mvd")) - { - continue; - } - - if (ent->d_type == DT_REG && *(ent->d_name) != '.') - file_count++; // only add non-hidden and regular files - } - - if (file_count == 0) - { - // empty directory - Sys_Printf(qtv->cluster, "Stream %i: Error: Directory has no demos.\n", qtv->streamid); - closedir(dir); - return false; - } - - closedir(dir); dir = opendir(fullname); - - // FIXME: not sure if srand should only be called once somewhere? - // FIXME: this is not really shuffling the demos, but does introduce some variety - if (file_count > 1) + if (dir) { - //srand(time(NULL)); - while ((random_number = rand()%file_count + 1) == qtv->last_random_number); - qtv->last_random_number = random_number; - } + char demoname[512]; + int current_demo = 0; + int file_count = 0; + int random_number = 1; // always this value if the directory contains one file - while (1) { - int len; - - ent = readdir(dir); - if (!ent) + // count the files, important for determining a random demo file + while ((ent = readdir(dir)) != NULL) { - // reached the end of the directory, shouldn't happen - Sys_Printf(qtv->cluster, "Stream %i: Error: Reached end of directory (%s%s)\n", qtv->streamid, qtv->cluster->demodir, ip); + int len; + + // only count files neding in .mvd + len = strlen(ent->d_name); + if (len < 5) + { + continue; + } + if (strcmp(ent->d_name+len-4, ".mvd")) + { + continue; + } + + if (ent->d_type == DT_REG && *(ent->d_name) != '.') + file_count++; // only add non-hidden and regular files + } + + if (file_count == 0) + { + // empty directory + Sys_Printf(qtv->cluster, "Stream %i: Error: Directory has no demos.\n", qtv->streamid); closedir(dir); return false; } - if (ent->d_type != DT_REG || *(ent->d_name) == '.') - { - continue; // ignore hidden and non-regular files - } - - //now make certain that the last four characters are '.mvd' and not something like '.cfg' perhaps - len = strlen(ent->d_name); - if (len < 5) - { - continue; - } - if (strcmp((ent->d_name)+len-4, ".mvd")) - { - continue; - } - - if (++current_demo != random_number) - continue; - - snprintf(demoname, sizeof(demoname), "%s/%s", ip, ent->d_name); - qtv->sourcefile = demoname; closedir(dir); - if (Net_ConnectToDemoServer(qtv, ent->d_name, ip) == true) + dir = opendir(fullname); + + // FIXME: not sure if srand should only be called once somewhere? + // FIXME: this is not really shuffling the demos, but does introduce some variety + if (file_count > 1) { - return true; + //srand(time(NULL)); + while ((random_number = rand()%file_count + 1) == qtv->last_random_number); + qtv->last_random_number = random_number; } - else - { - return false; + + while (1) { + int len; + + ent = readdir(dir); + if (!ent) + { + // reached the end of the directory, shouldn't happen + Sys_Printf(qtv->cluster, "Stream %i: Error: Reached end of directory (%s%s)\n", qtv->streamid, qtv->cluster->demodir, ip); + closedir(dir); + return false; + } + + if (ent->d_type != DT_REG || *(ent->d_name) == '.') + { + continue; // ignore hidden and non-regular files + } + + //now make certain that the last four characters are '.mvd' and not something like '.cfg' perhaps + len = strlen(ent->d_name); + if (len < 5) + { + continue; + } + if (strcmp((ent->d_name)+len-4, ".mvd")) + { + continue; + } + + if (++current_demo != random_number) + continue; + + snprintf(demoname, sizeof(demoname), "%s/%s", ip, ent->d_name); + qtv->sourcefile = fopen(demoname, "rb"); + closedir(dir); + if (Net_ConnectToDemoServer(qtv, ent->d_name, ip) == true) + { + return true; + } + else + { + return false; + } } + closedir(dir); + } + else + { + Sys_Printf(qtv->cluster, "Stream %i: Unable to open directory %s\n", qtv->streamid, qtv->cluster->demodir); + return false; } - closedir(dir); - } - else - { - Sys_Printf(qtv->cluster, "Stream %i: Unable to open directory %s\n", qtv->streamid, qtv->cluster->demodir); - return false; } #endif @@ -935,13 +965,11 @@ qboolean Net_WriteUpstream(sv_t *qtv) if (len < 0) { int err = qerrno; - if (err != EWOULDBLOCK && err != EAGAIN && err != ENOTCONN) + if (err != NET_EWOULDBLOCK && err != NET_EAGAIN && err != NET_ENOTCONN) { - int err; - err = qerrno; - if (qerrno) + if (err) { - Sys_Printf(qtv->cluster, "Stream %i: Error: source socket error %i (%s)\n", qtv->streamid, qerrno, strerror(qerrno)); + Sys_Printf(qtv->cluster, "Stream %i: Error: source socket error %i (%s)\n", qtv->streamid, err, strerror(err)); strcpy(qtv->status, "Network error\n"); } else @@ -1091,7 +1119,7 @@ qboolean Net_ReadStream(sv_t *qtv) errsize = sizeof(err); err = 0; getsockopt(qtv->sourcesock, SOL_SOCKET, SO_ERROR, (char*)&err, &errsize); - if (err == ECONNREFUSED) + if (err == NET_ECONNREFUSED) { Sys_Printf(qtv->cluster, "Stream %i: Error: server %s refused connection\n", qtv->streamid, qtv->server); closesocket(qtv->sourcesock); @@ -1123,7 +1151,7 @@ qboolean Net_ReadStream(sv_t *qtv) err = 0; else err = qerrno; - if (read == 0 || (err != EWOULDBLOCK && err != EAGAIN && err != ENOTCONN)) //ENOTCONN can be returned whilst waiting for a connect to finish. + if (read == 0 || (err != NET_EWOULDBLOCK && err != NET_EAGAIN && err != NET_ENOTCONN)) //ENOTCONN can be returned whilst waiting for a connect to finish. { if (qtv->sourcefile) Sys_Printf(qtv->cluster, "Stream %i: Error: End of file\n", qtv->streamid); diff --git a/plugins/jabber/jabberclient.c b/plugins/jabber/jabberclient.c index c9cd763dc..a0ab5ac0d 100644 --- a/plugins/jabber/jabberclient.c +++ b/plugins/jabber/jabberclient.c @@ -1467,13 +1467,13 @@ static int sasl_oauth2_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize) "POST %s HTTP/1.1\r\n" "Host: %s\r\n" //"Authorization: Basic %s\r\n" - "Content-length: %i\r\n" + "Content-length: %u\r\n" "Content-Type: application/x-www-form-urlencoded\r\n" "Connection: close\r\n" "\r\n", resource, host, - strlen(body)); + (unsigned)strlen(body)); sock = pNet_TCPConnect(host, 443); pNet_SetTLSClient(sock, host); @@ -1541,12 +1541,12 @@ static int sasl_oauth2_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize) "POST %s HTTP/1.1\r\n" "Host: %s\r\n" //"Authorization: Basic %s\r\n" - "Content-length: %i\r\n" + "Content-length: %u\r\n" "Content-Type: application/x-www-form-urlencoded\r\n" "user-agent: fteqw-plugin-xmpp\r\n" "Connection: close\r\n" "\r\n", - resource, host, strlen(body)); + resource, host, (unsigned)strlen(body)); Con_Printf("XMPP: Requesting access token\n"); sock = pNet_TCPConnect(host, 443); @@ -1613,12 +1613,12 @@ static int sasl_oauth2_initial(struct sasl_ctx_s *ctx, char *buf, int bufsize) "POST %s HTTP/1.1\r\n" "Host: %s\r\n" //"Authorization: Basic %s\r\n" - "Content-length: %i\r\n" + "Content-length: %u\r\n" "Content-Type: application/x-www-form-urlencoded\r\n" "user-agent: fteqw-plugin-xmpp\r\n" "Connection: close\r\n" "\r\n", - resource, host, strlen(body)); + resource, host, (unsigned)strlen(body)); Con_Printf("XMPP: Refreshing access token\n"); sock = pNet_TCPConnect(host, 443); diff --git a/plugins/models/gltf.c b/plugins/models/gltf.c new file mode 100644 index 000000000..ac58629dc --- /dev/null +++ b/plugins/models/gltf.c @@ -0,0 +1,2160 @@ +#ifndef GLQUAKE +#define GLQUAKE //this is shit. +#endif +#include "quakedef.h" +#include "../plugin.h" +#include "com_mesh.h" +extern modplugfuncs_t *modfuncs; + +#define GLTFMODELS + +#ifdef GLTFMODELS +typedef struct json_s +{ + const char *bodystart; + const char *bodyend; + + struct json_s *parent; + struct json_s *child; + struct json_s *sibling; + struct json_s **childlink; + qboolean used; //set to say when something actually read/walked it, so we can flag unsupported things gracefully + char name[1]; +} json_t; + +//node destruction +static void JSON_Orphan(json_t *t) +{ + if (t->parent) + { + json_t *p = t->parent, **l = &p->child; + while (*l) + { + if (*l == t) + { + *l = t->sibling; + if (*l) + p->childlink = l; + break; + } + l = &(*l)->sibling; + } + t->parent = NULL; + t->sibling = NULL; + } +} +static void JSON_Destroy(json_t *t) +{ + while(t->child) + JSON_Destroy(t->child); + JSON_Orphan(t); + free(t); +} + +//node creation +static json_t *JSON_CreateNode(json_t *parent, const char *namestart, const char *nameend, const char *bodystart, const char *bodyend) +{ + json_t *j; + qboolean dupbody = false; + if (namestart && !nameend) + nameend = namestart+strlen(namestart); + if (bodystart && !bodyend) + { + dupbody = true; + bodyend = bodystart+strlen(bodystart); + } + j = malloc(sizeof(*j) + nameend-namestart + (dupbody?1+bodyend-bodystart:0)); + memcpy(j->name, namestart, nameend-namestart); + j->name[nameend-namestart] = 0; + j->bodystart = bodystart; + j->bodyend = bodyend; + + j->child = NULL; + j->sibling = NULL; + j->childlink = &j->child; + j->parent = parent; + if (parent) + { + *parent->childlink = j; + parent->childlink = &j->sibling; + j->used = false; + } + else + j->used = true; + + if (dupbody) + { + char *bod = j->name + (nameend-namestart)+1; + j->bodystart = bod; + j->bodyend = j->bodystart + (bodyend-bodystart); + memcpy(bod, bodystart, bodyend-bodystart); + bod[bodyend-bodystart] = 0; + } + return j; +} + +//node parsing +static void JSON_SkipWhite(const char *msg, int *pos, int max) +{ + while (*pos < max && ( + msg[*pos] == ' ' || + msg[*pos] == '\t' || + msg[*pos] == '\r' || + msg[*pos] == '\n' + )) + *pos+=1; +} +static qboolean JSON_ParseString(char const*msg, int *pos, int max, char const**start, char const** end) +{ + if (*pos < max && msg[*pos] == '\"') + { + *pos+=1; + *start = msg+*pos; + while (*pos < max && msg[*pos] != '\"') + *pos+=1; + if (*pos < max && msg[*pos] == '\"') + { + *end = msg+*pos; + *pos+=1; + return true; + } + } + else + { + *start = msg+*pos; + while (*pos < max + && msg[*pos] != ' ' + && msg[*pos] != '\t' + && msg[*pos] != '\r' + && msg[*pos] != '\n' + && msg[*pos] != ':' + && msg[*pos] != ',' + && msg[*pos] != '}' + && msg[*pos] != '{' + && msg[*pos] != '[' + && msg[*pos] != ']') + { + *pos+=1; + } + *end = msg+*pos; + if (*start != *end) + return true; + } + *end = *start; + return false; +} +static json_t *JSON_Parse(json_t *t, const char *namestart, const char *nameend, const char *json, int *jsonpos, int jsonlen) +{ + const char *childstart, *childend; + JSON_SkipWhite(json, jsonpos, jsonlen); + + if (*jsonpos < jsonlen) + { + if (json[*jsonpos] == '{') + { + *jsonpos+=1; + JSON_SkipWhite(json, jsonpos, jsonlen); + + t = JSON_CreateNode(t, namestart, nameend, NULL, NULL); + + while (*jsonpos < jsonlen && json[*jsonpos] == '\"') + { + if (!JSON_ParseString(json, jsonpos, jsonlen, &childstart, &childend)) + break; + JSON_SkipWhite(json, jsonpos, jsonlen); + if (*jsonpos < jsonlen && json[*jsonpos] == ':') + { + *jsonpos+=1; + if (!JSON_Parse(t, childstart, childend, json, jsonpos, jsonlen)) + break; + } + JSON_SkipWhite(json, jsonpos, jsonlen); + + if (*jsonpos < jsonlen && json[*jsonpos] == ',') + { + *jsonpos+=1; + JSON_SkipWhite(json, jsonpos, jsonlen); + continue; + } + break; + } + + if (*jsonpos < jsonlen && json[*jsonpos] == '}') + { + *jsonpos+=1; + return t; + } + JSON_Destroy(t); + } + else if (json[*jsonpos] == '[') + { + char idxname[MAX_QPATH]; + unsigned int idx = 0; + *jsonpos+=1; + JSON_SkipWhite(json, jsonpos, jsonlen); + + t = JSON_CreateNode(t, namestart, nameend, NULL, NULL); + + for(;;) + { + Q_snprintf(idxname, sizeof(idxname), "%u", idx++); + if (!JSON_Parse(t, idxname, NULL, json, jsonpos, jsonlen)) + break; + + if (*jsonpos < jsonlen && json[*jsonpos] == ',') + { + *jsonpos+=1; + JSON_SkipWhite(json, jsonpos, jsonlen); + continue; + } + break; + } + + JSON_SkipWhite(json, jsonpos, jsonlen); + if (*jsonpos < jsonlen && json[*jsonpos] == ']') + { + *jsonpos+=1; + return t; + } + JSON_Destroy(t); + } + else + { + if (JSON_ParseString(json, jsonpos, jsonlen, &childstart, &childend)) + return JSON_CreateNode(t, namestart, nameend, childstart, childend); + } + } + return NULL; +} + +static json_t *JSON_FindChild(json_t *t, const char *child) +{ + if (t) + { + size_t nl; + const char *dot = strchr(child, '.'); + if (dot) + nl = dot-child; + else + nl = strlen(child); + for (t = t->child; t; t = t->sibling) + { + if (!strncmp(t->name, child, nl) && (t->name[nl] == '.' || !t->name[nl])) + { + child+=nl; + t->used = true; + if (*child == '.') + return JSON_FindChild(t, child+1); + if (!*child) + return t; + break; + } + } + } + return NULL; +} +static json_t *JSON_FindIndexedChild(json_t *t, const char *child, unsigned int idx) +{ + char idxname[MAX_QPATH]; + if (child) + Q_snprintf(idxname, sizeof(idxname), "%s.%u", child, idx); + else + Q_snprintf(idxname, sizeof(idxname), "%u", idx); + return JSON_FindChild(t, idxname); +} +static qboolean JSON_Equals(json_t *t, const char *child, const char *expected) +{ + if (child) + t = JSON_FindChild(t, child); + if (t && t->bodyend-t->bodystart == strlen(expected)) + return !strncmp(t->bodystart, expected, t->bodyend-t->bodystart); + return false; +} +#include +static qintptr_t JSON_GetInteger(json_t *t, const char *child, int fallback) +{ + if (child) + t = JSON_FindChild(t, child); + if (t) + { //copy it to another buffer. can probably skip that tbh. + char tmp[MAX_QPATH]; + size_t l = t->bodyend-t->bodystart; + if (l > MAX_QPATH-1) + l = MAX_QPATH-1; + memcpy(tmp, t->bodystart, l); + tmp[l] = 0; + return (qintptr_t)strtoll(tmp, NULL, 0); + } + return fallback; +} +static qintptr_t JSON_GetIndexedInteger(json_t *t, unsigned int idx, int fallback) +{ + char idxname[MAX_QPATH]; + Q_snprintf(idxname, sizeof(idxname), "%u", idx); + return JSON_GetInteger(t, idxname, fallback); +} +static double JSON_GetFloat(json_t *t, const char *child, double fallback) +{ + if (child) + t = JSON_FindChild(t, child); + if (t) + { //copy it to another buffer. can probably skip that tbh. + char tmp[MAX_QPATH]; + size_t l = t->bodyend-t->bodystart; + if (l > MAX_QPATH-1) + l = MAX_QPATH-1; + memcpy(tmp, t->bodystart, l); + tmp[l] = 0; + return atof(tmp); + } + return fallback; +} +static double JSON_GetIndexedFloat(json_t *t, unsigned int idx, double fallback) +{ + char idxname[MAX_QPATH]; + Q_snprintf(idxname, sizeof(idxname), "%u", idx); + return JSON_GetFloat(t, idxname, fallback); +} + +static void JSON_GetPath(json_t *t, qboolean ignoreroot, char *buffer, size_t buffersize) +{ + if (t->parent && (t->parent->parent || !ignoreroot)) + { + JSON_GetPath(t->parent, ignoreroot, buffer, buffersize); + Q_strlcat(buffer, ".", buffersize); + } + Q_strlcat(buffer, t->name, buffersize); +} +static void JSON_WarnUnused(json_t *t, int *warnlimit) +{ + if (!t) + return; + if (t->used) + { + for (t = t->child; t; t = t->sibling) + JSON_WarnUnused(t, warnlimit); + } + else + { + char path[8192]; + *path = 0; + JSON_GetPath(t, false, path, sizeof(path)); + if ((*warnlimit) --> 0) + Con_DPrintf(CON_WARNING"GLTF property %s was not used\n", path); + } +} +static void JSON_FlagAsUsed(json_t *t, const char *child) +{ + if (child) + { + t = JSON_FindChild(t, child); + if (!t) + return; + } + t->used = true; + for (t = t->child; t; t = t->sibling) + JSON_FlagAsUsed(t, NULL); +} +static void JSON_WarnIfChild(json_t *t, const char *child, int *warnlimit) +{ + t = JSON_FindChild(t, child); + if (t) + { + char path[8192]; + *path = 0; + JSON_GetPath(t, false, path, sizeof(path)); + if ((*warnlimit) --> 0) + Con_Printf(CON_WARNING"Standard feature %s is not supported\n", path); + JSON_FlagAsUsed(t, NULL); + } +} + +static unsigned int FromBase64(char c) +{ + if (c >= 'A' && c <= 'Z') + return 0+(c-'A'); + if (c >= 'a' && c <= 'z') + return 26+(c-'a'); + if (c >= '0' && c <= '9') + return 52+(c-'0'); + if (c == '+') + return 62; + if (c == '/') + return 63; + return 64; +} +//fancy parsing of content +static void *JSON_MallocDataURI(json_t *t, size_t *outlen) +{ + size_t bl = t->bodyend-t->bodystart; + if (bl >= 5 && !strncmp(t->bodystart, "data:", 5)) + { + const char *mimestart = t->bodystart+5; + const char *mimeend; + const char *encstart; + const char *encend; + const char *in; + char *out, *r; + + for (mimeend = mimestart; *mimeend && mimeend < t->bodyend; mimeend++) + { + if (*mimeend == ';') //start of encoding + break; + if (*mimeend == ',') //start of data + break; + } + if (*mimeend == ';') + { + for (encend = encstart = mimeend+1; *encend && encend < t->bodyend; encend++) + { + if (*encend == ',') //start of data + break; + } + } + else + encstart = encend = mimeend; + + if (*encend == ',' && encend < t->bodyend) + { + in = encend+1; + if (encend-encstart == 6 && !strncmp(encstart, "base64", 6)) + { + //base64 + r = out = malloc(((t->bodyend-in)*3)/4 + 1); + while (in+3 < t->bodyend) + { + unsigned int c1, c2, c3, c4; + c1 = FromBase64(*in++); + c2 = FromBase64(*in++); + if (c1 >= 64 || c2 >= 64) + break; + *out++ = (c1<<2) | (c2>>4); + c3 = FromBase64(*in++); + if (c3 >= 64) + break; + *out++ = (c2<<4) | (c3>>2); + c4 = FromBase64(*in++); + if (c3 >= 64) + break; + *out++ = (c3<<6) | (c4>>0); + } + *outlen = out-r; + *out = 0; + return r; + } + else if (encend == encstart) + { //url encoding. yuck, sod off. + } + } + } + return NULL; +} + +static size_t JSON_ReadBody(json_t *t, char *out, size_t outsize) +{ + size_t bodysize; + if (!t) + { + if (out) + *out = 0; + return 0; + } + if (out) + { + bodysize = t->bodyend-t->bodystart; + if (bodysize > outsize-1) + bodysize = outsize-1; + memcpy(out, t->bodystart, bodysize); + out[bodysize] = 0; + } + return t->bodyend-t->bodystart; +} + +//glTF 1.0 and 2.0 differ in that 1 uses names and 2 uses indexes. There's also some significant differences with materials. +//we only support 2.0 + +//FTE does not support articulated models. we might be able to convert them to skeletal though. +//we don't support skeletal models either right now. + +//buffers are raw blobs that can come from multiple different sources +struct gltf_buffer +{ + qboolean loaded; + qboolean malloced; + void *data; + size_t length; +}; +typedef struct gltf_s +{ + struct model_s *mod; + unsigned int numsurfaces; + json_t *r; + + int bonemap[MAX_BONES]; //remap skinned bones. I hate that we have to do this. + struct gltfbone_s + { + char name[32]; + int parent; + double amatrix[16]; + double inverse[16]; + struct + { + double rmatrix[16]; //gah + double quat[4], scale[3], trans[3]; //annoying smeg + } rel; + + struct { + struct gltf_accessor *input; + struct gltf_accessor *output; + } *rot, *scale, *translation; + } bones[MAX_BONES]; + unsigned int numbones; + + int warnlimit; //don't spam warnings. this is a loader, not a spammer + + struct gltf_buffer buffers[64]; +} gltf_t; + +static void GLTF_RelativePath(const char *base, const char *relative, char *out, size_t outsize) +{ + size_t t; + const char *sep; + const char *end = base; + if (*relative == '/') + { + relative++; + } + else + { + for (sep = end; *sep; sep++) + { + if (*sep == '/' || *sep == '\\') + end = sep+1; + } + } + while (!strncmp(relative, "../", 3)) + { + if (end > base) + { + end--; + while (end > base) + { + end--; + if (*end == '/' || *end == '\\') + { + relative += 3; + end++; + break; + } + } + } + else + break; + } + outsize--; //for the null + + t = end-base; + if (t > outsize) + t = outsize; + memcpy(out, base, t); + out += t; + outsize -= t; + + t = strlen(relative); + if (t > outsize) + t = outsize; + memcpy(out, relative, t); + out += t; + outsize -= t; + + *out = 0; +} + +static struct gltf_buffer *GLTF_GetBufferData(gltf_t *gltf, int bufferidx) +{ + json_t *b = JSON_FindIndexedChild(gltf->r, "buffers", bufferidx); + json_t *uri = JSON_FindChild(b, "uri"); + size_t length = JSON_GetInteger(b, "byteLength", 0); + struct gltf_buffer *out; + +// JSON_WarnIfChild(b, "name"); +// JSON_WarnIfChild(b, "extensions"); +// JSON_WarnIfChild(b, "extras"); + + if (bufferidx < 0 || bufferidx >= countof(gltf->buffers)) + return NULL; + out = &gltf->buffers[bufferidx]; + + //we may have been through here before... + if (out->loaded) + return out->data?out:NULL; + out->loaded = true; + + if (uri) + { + out->malloced = true; + out->data = JSON_MallocDataURI(uri, &out->length); + if (!out->data) + { + //read a file from disk. + vfsfile_t *f; + char uritext[MAX_QPATH]; + char filename[MAX_QPATH]; + JSON_ReadBody(uri, uritext, sizeof(uritext)); + GLTF_RelativePath(gltf->mod->name, uritext, filename, sizeof(filename)); + f = modfuncs->OpenVFS(filename, "rb", FS_GAME); + if (f) + { + out->length = VFS_GETLEN(f); + out->length = min(out->length, length); + out->data = malloc(length); + VFS_READ(f, out->data, length); + VFS_CLOSE(f); + } + } + } + return out->data?out:NULL; +} +//buffer views are aka VBOs. each has its own VBO data type (vbo/ebo), and can be uploaded as-is. +struct gltf_bufferview +{ + void *data; + size_t length; + int bytestride; +}; +static qboolean GLTF_GetBufferViewData(gltf_t *gltf, int bufferview, struct gltf_bufferview *view) +{ + struct gltf_buffer *buf; + json_t *bv = JSON_FindIndexedChild(gltf->r, "bufferViews", bufferview); + size_t offset; + if (!bv) + return false; + + buf = GLTF_GetBufferData(gltf, JSON_GetInteger(bv, "buffer", 0)); + if (!buf) + return false; + offset = JSON_GetInteger(bv, "byteOffset", 0); + view->data = (char*)buf->data + offset; + view->length = JSON_GetInteger(bv, "byteLength", 0); //required + view->bytestride = JSON_GetInteger(bv, "byteStride", 0); + if (offset + view->length > buf->length) + return false; + + JSON_FlagAsUsed(bv, "target"); //required, but not useful for us. + JSON_FlagAsUsed(bv, "name"); +// JSON_WarnIfChild(bv, "extensions"); +// JSON_WarnIfChild(bv, "extras"); + return true; +} +//accessors are basically VAs blocks that refer inside a bufferview/VBO. +struct gltf_accessor +{ + void *data; + size_t length; + size_t bytestride; + + int componentType; //5120 BYTE, 5121 UNSIGNED_BYTE, 5122 SHORT, 5123 UNSIGNED_SHORT, 5125 UNSIGNED_INT, 5126 FLOAT + qboolean normalized; + int count; + int type; //1,2,3,4 says component count, 256|(4,9,16) for square matricies... + + double mins[16]; + double maxs[16]; +}; +static qboolean GLTF_GetAccessor(gltf_t *gltf, int accessorid, struct gltf_accessor *out) +{ + struct gltf_bufferview bv; + json_t *a, *mins, *maxs; + size_t offset; + int j; + memset(out, 0, sizeof(*out)); + + a = JSON_FindIndexedChild(gltf->r, "accessors", accessorid); + if (!a) + return false; + + if (!GLTF_GetBufferViewData(gltf, JSON_GetInteger(a, "bufferView", 0), &bv)) + return false; + offset = JSON_GetInteger(a, "byteOffset", 0); + if (offset > bv.length) + return false; + out->length = bv.length - offset; + out->bytestride = bv.bytestride; + out->componentType = JSON_GetInteger(a, "componentType", 0); + out->normalized = JSON_GetInteger(a, "normalized", false); + out->count = JSON_GetInteger(a, "count", 0); + if (JSON_Equals(a, "type", "SCALAR")) + out->type = (1<<8) | 1; + else if (JSON_Equals(a, "type", "VEC2")) + out->type = (1<<8) | 2; + else if (JSON_Equals(a, "type", "VEC3")) + out->type = (1<<8) | 3; + else if (JSON_Equals(a, "type", "VEC4")) + out->type = (1<<8) | 4; + else if (JSON_Equals(a, "type", "MAT2")) + out->type = (2<<8) | 2; + else if (JSON_Equals(a, "type", "MAT3")) + out->type = (3<<8) | 3; + else if (JSON_Equals(a, "type", "MAT4")) + out->type = (4<<8) | 4; + else + { + if (gltf->warnlimit --> 0) + Con_Printf(CON_WARNING"%s: glTF2 unsupported type\n", gltf->mod->name); + out->type = 1; + } + + if (!out->bytestride) + { + out->bytestride = (out->type & 0xff) * (out->type>>8); + switch(out->componentType) + { + default: + if (gltf->warnlimit --> 0) + Con_Printf(CON_WARNING"GLTF_GetAccessor: %s: glTF2 unsupported componentType (%i)\n", gltf->mod->name, out->componentType); + case 5120: //BYTE + case 5121: //UNSIGNED_BYTE + break; + case 5122: //SHORT + case 5123: //UNSIGNED_SHORT + out->bytestride *= 2; + break; + case 5125: //UNSIGNED_INT + case 5126: //FLOAT + out->bytestride *= 4; + break; + } + } + + + mins = JSON_FindChild(a, "min"); + maxs = JSON_FindChild(a, "max"); + for (j = 0; j < (out->type>>8)*(out->type&0xff); j++) + { //'must' be set in various situations. + out->mins[j] = JSON_GetIndexedInteger(mins, j, 0); + out->maxs[j] = JSON_GetIndexedInteger(maxs, j, 0); + } + +// JSON_WarnIfChild(a, "sparse"); +// JSON_WarnIfChild(a, "name"); +// JSON_WarnIfChild(a, "extensions"); +// JSON_WarnIfChild(a, "extras"); + + out->data = (char*)bv.data + offset; + return true; +} + +static void GLTF_AccessorToTangents(gltf_t *gltf, vec3_t *norm, vec3_t **sdir, vec3_t **tdir, size_t outverts, struct gltf_accessor *a) +{ //input MUST be a single float4 + //output is two vec3s. wasteful perhaps. + vec3_t *os = modfuncs->ZG_Malloc(&gltf->mod->memgroup, sizeof(*os) * 3 * outverts); + vec3_t *ot = modfuncs->ZG_Malloc(&gltf->mod->memgroup, sizeof(*ot) * 3 * outverts); + char *in = a->data; + + size_t v, c; + *sdir = os; + *tdir = ot; + if ((a->type&0xff) != 4) + return; + switch(a->componentType) + { + default: + if (gltf->warnlimit --> 0) + Con_Printf(CON_WARNING"GLTF_AccessorToTangents: %s: glTF2 unsupported componentType (%i)\n", gltf->mod->name, a->componentType); + case 0: + memset(os, 0, sizeof(*os) * outverts); + memset(ot, 0, sizeof(*ot) * outverts); + break; +// case 5120: //BYTE +// case 5121: //UNSIGNED_BYTE +// case 5122: //SHORT +// case 5123: //UNSIGNED_SHORT +// case 5125: //UNSIGNED_INT + case 5126: //FLOAT + for (v = 0; v < outverts; v++) + { + for (c = 0; c < 3; c++) + os[v][c] = ((float*)in)[c]; + + //bitangent = cross(normal, tangent.xyz) * tangent.w + ot[v][0] = (norm[v][1]*os[v][2] - norm[v][2]*os[v][1]) * ((float*)in)[3]; + ot[v][1] = (norm[v][2]*os[v][0] - norm[v][0]*os[v][2]) * ((float*)in)[3]; + ot[v][2] = (norm[v][0]*os[v][1] - norm[v][1]*os[v][0]) * ((float*)in)[3]; + + in += a->bytestride; + } + break; + } +} +static void *GLTF_AccessorToDataF(gltf_t *gltf, size_t outverts, unsigned int outcomponents, struct gltf_accessor *a) +{ + float *ret = modfuncs->ZG_Malloc(&gltf->mod->memgroup, sizeof(*ret) * outcomponents * outverts), *o; + char *in = a->data; + + int c, ic = a->type&0xff; + if (ic > outcomponents) + ic = outcomponents; + o = ret; + switch(a->componentType) + { + default: + if (gltf->warnlimit --> 0) + Con_Printf(CON_WARNING"GLTF_AccessorToDataF: %s: glTF2 unsupported componentType (%i)\n", gltf->mod->name, a->componentType); + case 0: + memset(ret, 0, sizeof(*ret) * outcomponents * outverts); + break; + case 5120: //BYTE + while(outverts --> 0) + { + for (c = 0; c < ic; c++) + o[c] = max(-1.0, ((char*)in)[c] / 127.0); //negative values are larger, but we want to allow 1.0 + for (; c < outcomponents; c++) + o[c] = 0; + o += outcomponents; + in += a->bytestride; + } + break; + case 5121: //UNSIGNED_BYTE + while(outverts --> 0) + { + for (c = 0; c < ic; c++) + o[c] = ((unsigned char*)in)[c] / 255.0; + for (; c < outcomponents; c++) + o[c] = 0; + o += outcomponents; + in += a->bytestride; + } + break; + case 5122: //SHORT + while(outverts --> 0) + { + for (c = 0; c < ic; c++) + o[c] = max(-1.0, ((signed short*)in)[c] / 32767.0); //negative values are larger, but we want to allow 1.0 + for (; c < outcomponents; c++) + o[c] = 0; + o += outcomponents; + in += a->bytestride; + } + break; + case 5123: //UNSIGNED_SHORT + while(outverts --> 0) + { + for (c = 0; c < ic; c++) + o[c] = ((unsigned short*)in)[c] / 65535.0; + for (; c < outcomponents; c++) + o[c] = 0; + o += outcomponents; + in += a->bytestride; + } + break; + case 5125: //UNSIGNED_INT + while(outverts --> 0) + { + for (c = 0; c < ic; c++) + o[c] = ((unsigned int*)in)[c] / (double)~0u; //stupid format to use. will be lossy. + for (; c < outcomponents; c++) + o[c] = 0; + o += outcomponents; + in += a->bytestride; + } + break; + case 5126: //FLOAT + while(outverts --> 0) + { + for (c = 0; c < ic; c++) + o[c] = ((float*)in)[c]; + for (; c < outcomponents; c++) + o[c] = 0; + o += outcomponents; + in += a->bytestride; + } + break; + } + return ret; +} +static void *GLTF_AccessorToDataUB(gltf_t *gltf, size_t outverts, unsigned int outcomponents, struct gltf_accessor *a) +{ //only used for colour, with fallback to float, so only UNSIGNED_BYTE needs to work. + unsigned char *ret = modfuncs->ZG_Malloc(&gltf->mod->memgroup, sizeof(*ret) * outcomponents * outverts), *o; + char *in = a->data; + + int c, ic = a->type&0xff; + if (ic > outcomponents) + ic = outcomponents; + o = ret; + switch(a->componentType) + { + default: + if (gltf->warnlimit --> 0) + Con_Printf(CON_WARNING"GLTF_AccessorToDataUB: %s: glTF2 unsupported componentType (%i)\n", gltf->mod->name, a->componentType); + case 0: + memset(ret, 0, sizeof(*ret) * outcomponents * outverts); + break; +// case 5120: //BYTE + case 5121: //UNSIGNED_BYTE + while(outverts --> 0) + { + for (c = 0; c < ic; c++) + o[c] = ((unsigned char*)in)[c]; + for (; c < outcomponents; c++) + o[c] = 0; + o += outcomponents; + in += a->bytestride; + } + break; +// case 5122: //SHORT +// case 5123: //UNSIGNED_SHORT +// case 5125: //UNSIGNED_INT +/* case 5126: //FLOAT + while(outverts --> 0) + { + for (c = 0; c < ic; c++) + o[c] = ((float*)in)[c]; + for (; c < outcomponents; c++) + o[c] = 0; + o += outcomponents; + in += a->bytestride; + } + break;*/ + } + return ret; +} +static void *GLTF_AccessorToDataBone(gltf_t *gltf, size_t outverts, struct gltf_accessor *a) +{ //input should only be ubytes||ushorts. + const unsigned int outcomponents = 4; + unsigned char *ret = modfuncs->ZG_Malloc(&gltf->mod->memgroup, sizeof(*ret) * outcomponents * outverts), *o; + char *in = a->data; + + + int c, ic = a->type&0xff; + if (ic > outcomponents) + ic = outcomponents; + o = ret; + switch(a->componentType) + { + default: + if (gltf->warnlimit --> 0) + Con_Printf(CON_WARNING"GLTF_AccessorToDataUB: %s: glTF2 unsupported componentType (%i)\n", gltf->mod->name, a->componentType); + case 0: + memset(ret, 0, sizeof(*ret) * outcomponents * outverts); + break; +// case 5120: //BYTE + case 5121: //UNSIGNED_BYTE + while(outverts --> 0) + { + unsigned char v; + for (c = 0; c < ic; c++) + { + v = ((unsigned char*)in)[c]; + o[c] = gltf->bonemap[v]; + } + for (; c < outcomponents; c++) + o[c] = gltf->bonemap[0]; + o += outcomponents; + in += a->bytestride; + } + break; + case 5122: //SHORT + case 5123: //UNSIGNED_SHORT + while(outverts --> 0) + { + unsigned short v; + for (c = 0; c < ic; c++) + { + v = ((unsigned short*)in)[c]; + if (v > 255) + v = 0; + o[c] = gltf->bonemap[v]; + } + for (; c < outcomponents; c++) + o[c] = gltf->bonemap[0]; + o += outcomponents; + in += a->bytestride; + } + break; +// case 5125: //UNSIGNED_INT +/* case 5126: //FLOAT + while(outverts --> 0) + { + for (c = 0; c < ic; c++) + o[c] = ((float*)in)[c]; + for (; c < outcomponents; c++) + o[c] = 0; + o += outcomponents; + in += a->bytestride; + } + break;*/ + } + return ret; +} +void TransformArrayD(vecV_t *data, size_t vcount, double matrix[]) +{ + while (vcount --> 0) + { + vec3_t t; + VectorCopy((*data), t); + (*data)[0] = DotProduct(t, (matrix+0)) + matrix[0+3]; + (*data)[1] = DotProduct(t, (matrix+4)) + matrix[4+3]; + (*data)[2] = DotProduct(t, (matrix+8)) + matrix[8+3]; + data++; + } +} +void TransformArrayA(vec3_t *data, size_t vcount, double matrix[]) +{ + vec3_t t; + float mag; + while (vcount --> 0) + { + t[0] = DotProduct((*data), (matrix+0)); + t[1] = DotProduct((*data), (matrix+4)); + t[2] = DotProduct((*data), (matrix+8)); + + //scaling is bad for axis. + mag = DotProduct(t,t); + if (mag) + { + mag = 1/sqrt(mag); + VectorScale(t, mag, t); + } + + VectorCopy(t, (*data)); + data++; + } +} +static texid_t GLTF_LoadImage(gltf_t *gltf, int imageidx, unsigned int flags) +{ + size_t size; + texid_t ret = r_nulltex; + json_t *image = JSON_FindIndexedChild(gltf->r, "images", imageidx); + json_t *uri = JSON_FindChild(image, "uri"); + json_t *mimeType = JSON_FindChild(image, "mimeType"); + int bufferView = JSON_GetInteger(image, "bufferView", -1); + char uritext[MAX_QPATH]; + char filename[MAX_QPATH]; + void *mem; + struct gltf_bufferview view; + + //potentially valid mime types: + //image/png + //image/vnd-ms.dds (MSFT_texture_dds) + (void)mimeType; + + *uritext = 0; + if (uri) + { + mem = JSON_MallocDataURI(uri, &size); + if (mem) + { + JSON_GetPath(image, false, uritext, sizeof(uritext)); + ret = modfuncs->GetTexture(uritext, NULL, flags, mem, NULL, size, 0, TF_INVALID); + free(mem); + } + else + { + JSON_ReadBody(uri, uritext, sizeof(uritext)); + GLTF_RelativePath(gltf->mod->name, uritext, filename, sizeof(filename)); + ret = modfuncs->GetTexture(filename, NULL, flags, NULL, NULL, 0, 0, TF_INVALID); + } + } + else if (bufferView >= 0) + { + if (GLTF_GetBufferViewData(gltf, bufferView, &view)) + { + JSON_GetPath(image, false, uritext, sizeof(uritext)); + ret = modfuncs->GetTexture(uritext, NULL, flags, view.data, NULL, view.length, 0, TF_INVALID); + } + } + + return ret; +} +static texid_t GLTF_LoadTexture(gltf_t *gltf, int texture, unsigned int flags) +{ + json_t *tex = JSON_FindIndexedChild(gltf->r, "textures", texture); + json_t *sampler = JSON_FindIndexedChild(gltf->r, "samplers", JSON_GetInteger(tex, "sampler", -1)); + + int magFilter = JSON_GetInteger(sampler, "magFilter", 0); + int minFilter = JSON_GetInteger(sampler, "minFilter", 0); + int wrapS = JSON_GetInteger(sampler, "wrapS", 10497); + int wrapT = JSON_GetInteger(sampler, "wrapT", 10497); + int source; + + JSON_FlagAsUsed(sampler, "name"); + JSON_FlagAsUsed(sampler, "extensions"); + + (void)minFilter; + switch(magFilter) + { + default: + break; + case 9728: //NEAREST + flags |= IF_NOMIPMAP|IF_NEAREST; + break; + case 9729: //LINEAR + flags |= IF_NOMIPMAP|IF_LINEAR; + break; + case 9984: // NEAREST_MIPMAP_NEAREST + case 9986: // NEAREST_MIPMAP_LINEAR + flags |= IF_NEAREST; + break; + case 9985: // LINEAR_MIPMAP_NEAREST + case 9987: // LINEAR_MIPMAP_LINEAR + flags |= IF_LINEAR; + break; + } + + if (wrapS == 33071 || wrapT == 33071) + flags |= IF_CLAMP; + + flags |= IF_NOREPLACE; + + source = JSON_GetInteger(tex, "source", -1); + source = JSON_GetInteger(tex, "extensions.MSFT_texture_dds.source", source); //load a dds instead, if one is available. + return GLTF_LoadImage(gltf, source, flags); +} +static galiasskin_t *GLTF_LoadMaterial(gltf_t *gltf, int material, qboolean vertexcolours) +{ + qboolean doubleSided; + int alphaMode; + //double alphaCutoff; + char shader[8192]; + json_t *mat = JSON_FindIndexedChild(gltf->r, "materials", material); + galiasskin_t *ret; + + json_t *nam, *unlit, *pbrsg, *pbrmr, *blinn; + + nam = JSON_FindChild(mat, "name"); + unlit = JSON_FindChild(mat, "extensions.KHR_materials_unlit"); + pbrsg = JSON_FindChild(mat, "extensions.KHR_materials_pbrSpecularGlossiness"); + pbrmr = JSON_FindChild(mat, "pbrMetallicRoughness"); + blinn = JSON_FindChild(mat, "extensions.KHR_materials_cmnBlinnPhong"); + +/* JSON_WarnIfChild(mat, "name"); + JSON_WarnIfChild(pbrsg, "diffuseFactor"); + JSON_WarnIfChild(pbrsg, "diffuseTexture"); + JSON_WarnIfChild(pbrsg, "specularFactor"); + JSON_WarnIfChild(pbrsg, "glossinessFactor"); + JSON_WarnIfChild(pbrsg, "specularGlossinessTexture"); + JSON_WarnIfChild(mat, "normalTexture"); + JSON_WarnIfChild(mat, "occlusionTexture"); + JSON_WarnIfChild(mat, "emissiveTexture"); + JSON_WarnIfChild(mat, "emissiveFactor"); //0,0,0 +*/ + doubleSided = JSON_GetInteger(mat, "doubleSided", false); + //alphaCutoff = JSON_GetInteger(mat, "alphaCutoff", 0.5); + if (JSON_Equals(mat, "alphaMode", "MASK")) + alphaMode = 1; + else if (JSON_Equals(mat, "alphaMode", "BLEND")) + alphaMode = 2; + else //if (JSON_Equals(mat, "alphaMode", "OPAQUE")) + alphaMode = 0; + + ret = modfuncs->ZG_Malloc(&gltf->mod->memgroup, sizeof(*ret)); + ret->numframes = 1; + ret->skinspeed = 0.1; + ret->frame = modfuncs->ZG_Malloc(&gltf->mod->memgroup, sizeof(*ret->frame)); + + if (nam) + JSON_ReadBody(nam, ret->frame->shadername, sizeof(ret->frame->shadername)); + else if (mat) + JSON_GetPath(mat, false, ret->frame->shadername, sizeof(ret->frame->shadername)); + else + Q_snprintf(ret->frame->shadername, sizeof(ret->frame->shadername), "%i", material); + + if (unlit) + { //if this extension was present, then we don't get ANY lighting info. + int albedo = JSON_GetInteger(pbrmr, "baseColorTexture.index", -1); //.rgba + ret->frame->texnums.base = GLTF_LoadTexture(gltf, albedo, 0); + + Q_snprintf(shader, sizeof(shader), + "{\n" + "surfaceparm nodlight\n" + "%s"//cull + "program default2d\n" //fixme: there's no gpu skeletal stuff with this prog + "{\n" + "map $diffuse\n" + "%s" //blend + "%s" //rgbgen + "}\n" + "fte_basefactor %f %f %f %f\n" + "}\n", + doubleSided?"cullface disable\n":"", + (alphaMode==1)?"alphamask\n":(alphaMode==2)?"blendfunc blend\n":"", + vertexcolours?"rgbgen vertex\nalphagen vertex\n":"", + JSON_GetFloat(pbrmr, "baseColorFactor.0", 1), + JSON_GetFloat(pbrmr, "baseColorFactor.1", 1), + JSON_GetFloat(pbrmr, "baseColorFactor.2", 1), + JSON_GetFloat(pbrmr, "baseColorFactor.3", 1) + ); + } + else if (blinn) + { + Con_DPrintf(CON_WARNING"%s: KHR_materials_cmnBlinnPhong implemented according to draft spec\n", gltf->mod->name); + + ret->frame->texnums.base = GLTF_LoadTexture(gltf, JSON_GetInteger(pbrsg, "diffuseTexture.index", -1), 0); + ret->frame->texnums.specular = GLTF_LoadTexture(gltf, JSON_GetInteger(pbrsg, "specularGlossinessTexture.index", -1), 0); + + //you wouldn't normally want this, but we have separate factors so lack of a texture is technically valid. + if (!ret->frame->texnums.base) + ret->frame->texnums.base = modfuncs->GetTexture("$whiteimage", NULL, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA, NULL, NULL, 0, 0, TF_INVALID); + if (!ret->frame->texnums.specular) + ret->frame->texnums.specular = modfuncs->GetTexture("$whiteimage", NULL, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA, NULL, NULL, 0, 0, TF_INVALID); + + Q_snprintf(shader, sizeof(shader), + "{\n" + "%s"//cull + "program defaultskin#VC\n" + "{\n" + "map $diffuse\n" + "%s" //blend + "%s" //rgbgen + "}\n" + "fte_basefactor %f %f %f %f\n" + "fte_specularfactor %f %f %f %f\n" + "fte_fullbrightfactor %f %f %f 1.0\n" + "}\n", + doubleSided?"cullface disable\n":"", + (alphaMode==1)?"alphamask\n":(alphaMode==2)?"blendfunc blend\n":"", + vertexcolours?"rgbgen vertex\nalphagen vertex\n":"", + JSON_GetFloat(pbrsg, "diffuseFactor.0", 1), + JSON_GetFloat(pbrsg, "diffuseFactor.1", 1), + JSON_GetFloat(pbrsg, "diffuseFactor.2", 1), + JSON_GetFloat(pbrsg, "diffuseFactor.3", 1), + JSON_GetFloat(pbrsg, "specularFactor.0", 1), + JSON_GetFloat(pbrsg, "specularFactor.1", 1), + JSON_GetFloat(pbrsg, "specularFactor.2", 1), + JSON_GetFloat(pbrsg, "shininessFactor", 1), + JSON_GetFloat(mat, "emissiveFactor.0", 1), + JSON_GetFloat(mat, "emissiveFactor.1", 1), + JSON_GetFloat(mat, "emissiveFactor.2", 1) + ); + } + else if (pbrsg) + { //if this extension was used, then we can use rgb gloss instead of metalness stuff. + ret->frame->texnums.base = GLTF_LoadTexture(gltf, JSON_GetInteger(pbrsg, "diffuseTexture.index", -1), 0); + ret->frame->texnums.specular = GLTF_LoadTexture(gltf, JSON_GetInteger(pbrsg, "specularGlossinessTexture.index", -1), 0); + + Q_snprintf(shader, sizeof(shader), + "{\n" + "%s"//cull + "program defaultskin#VC\n" + "{\n" + "map $diffuse\n" + "%s" //blend + "%s" //rgbgen + "}\n" + "fte_basefactor %f %f %f %f\n" + "fte_specularfactor %f %f %f %f\n" + "fte_fullbrightfactor %f %f %f 1.0\n" + "}\n", + doubleSided?"cullface disable\n":"", + (alphaMode==1)?"alphamask\n":(alphaMode==2)?"blendfunc blend\n":"", + vertexcolours?"rgbgen vertex\nalphagen vertex\n":"", + JSON_GetFloat(pbrsg, "diffuseFactor.0", 1), + JSON_GetFloat(pbrsg, "diffuseFactor.1", 1), + JSON_GetFloat(pbrsg, "diffuseFactor.2", 1), + JSON_GetFloat(pbrsg, "diffuseFactor.3", 1), + JSON_GetFloat(pbrsg, "specularFactor.0", 1), + JSON_GetFloat(pbrsg, "specularFactor.1", 1), + JSON_GetFloat(pbrsg, "specularFactor.2", 1), + JSON_GetFloat(pbrsg, "glossinessFactor", 1)*32, //this is fucked. + JSON_GetFloat(mat, "emissiveFactor.0", 1), + JSON_GetFloat(mat, "emissiveFactor.1", 1), + JSON_GetFloat(mat, "emissiveFactor.2", 1) + ); + } + else if (pbrmr) + { //this is the standard lighting model for gltf2 + int albedo = JSON_GetInteger(pbrmr, "baseColorTexture.index", -1); //.rgba + int mrt = JSON_GetInteger(pbrmr, "metallicRoughnessTexture.index", -1); //.r = unused, .g = roughness, .b = metalic, .a = unused + int occ = JSON_GetInteger(mat, "occlusionTexture.index", -1); //.r + + //now work around potential lame exporters. + occ = JSON_GetInteger(mat, "extensions.MSFT_packing_occlusionRoughnessMetallic.occlusionRoughnessMetallicTexture.index", occ); + mrt = JSON_GetInteger(mat, "extensions.MSFT_packing_occlusionRoughnessMetallic.occlusionRoughnessMetallicTexture.index", mrt); + + if (occ != mrt && occ != -1) //if its -1 then the mrt should have an unused channel set to 1. however, this isn't guarenteed... + if (gltf->warnlimit --> 0) + Con_Printf(CON_WARNING"%s: Separate occlusion and metallicRoughness textures are not supported\n", gltf->mod->name); + + //note: extensions.MSFT_packing_normalRoughnessMetallic.normalRoughnessMetallicTexture.index gives rg=normalxy, b=roughness, .a=metalic + //(would still need an ao map, and probably wouldn't work well as bc3 either) + + ret->frame->texnums.base = GLTF_LoadTexture(gltf, albedo, 0); + ret->frame->texnums.specular = GLTF_LoadTexture(gltf, mrt, 0); + + Q_snprintf(shader, sizeof(shader), + "{\n" + "%s"//cull + "program defaultskin#PBR_ORM#VC\n" + "{\n" + "map $diffuse\n" + "%s" //blend + "%s" //rgbgen + "}\n" + "fte_basefactor %f %f %f %f\n" + "fte_specularfactor 1.0 %f %f 1.0\n" + "fte_fullbrightfactor %f %f %f 1.0\n" + "}\n", + doubleSided?"cullface disable\n":"", + (alphaMode==1)?"alphamask\n":(alphaMode==2)?"blendfunc blend\n":"", + vertexcolours?"rgbgen vertex\nalphagen vertex\n":"", + JSON_GetFloat(pbrmr, "baseColorFactor.0", 1), + JSON_GetFloat(pbrmr, "baseColorFactor.1", 1), + JSON_GetFloat(pbrmr, "baseColorFactor.2", 1), + JSON_GetFloat(pbrmr, "baseColorFactor.3", 1), + JSON_GetFloat(pbrmr, "metallicFactor", 1), + JSON_GetFloat(pbrmr, "roughnessFactor", 1), + JSON_GetFloat(mat, "emissiveFactor.0", 1), + JSON_GetFloat(mat, "emissiveFactor.1", 1), + JSON_GetFloat(mat, "emissiveFactor.2", 1) + ); + } + ret->frame->texnums.bump = GLTF_LoadTexture(gltf, JSON_GetInteger(mat, "normalTexture.index", -1), IF_NOSRGB|IF_TRYBUMP); + ret->frame->texnums.fullbright = GLTF_LoadTexture(gltf, JSON_GetInteger(mat, "emissiveTexture.index", -1), 0); + + if (!ret->frame->texnums.base) + ret->frame->texnums.base = modfuncs->GetTexture("$whiteimage", NULL, IF_NOMIPMAP|IF_NOPICMIP|IF_NEAREST|IF_NOGAMMA, NULL, NULL, 0, 0, TF_INVALID); + + ret->frame->defaultshader = memcpy(modfuncs->ZG_Malloc(&gltf->mod->memgroup, strlen(shader)+1), shader, strlen(shader)+1); + + Q_strlcpy(ret->name, ret->frame->shadername, sizeof(ret->name)); + return ret; +} +static qboolean GLTF_ProcessMesh(gltf_t *gltf, int meshidx, int basebone, double pmatrix[]) +{ + model_t *mod = gltf->mod; + json_t *mesh = JSON_FindIndexedChild(gltf->r, "meshes", meshidx); + json_t *prim; + json_t *meshname = JSON_FindChild(mesh, "name"); + + JSON_WarnIfChild(mesh, "weights", &gltf->warnlimit); + JSON_WarnIfChild(mesh, "extensions", &gltf->warnlimit); +// JSON_WarnIfChild(mesh, "extras", &gltf->warnlimit); + + for(prim = JSON_FindIndexedChild(mesh, "primitives", 0); prim; prim = prim->sibling) + { + int mat = JSON_GetInteger(prim, "material", -1); + int mode = JSON_GetInteger(prim, "mode", 4); + json_t *attr = JSON_FindChild(prim, "attributes"); + struct gltf_accessor tc_0, tc_1, norm, tang, vpos, col0, idx, sidx, swgt; + galiasinfo_t *surf; + size_t i, j; + + prim->used = true; + + if (mode != 4) + { + Con_Printf("Primitive mode %i not supported\n", mode); + continue; + } + + JSON_WarnIfChild(prim, "targets", &gltf->warnlimit); //morph targets... + JSON_FindChild(prim, "extensions"); +// JSON_WarnIfChild(prim, "extensions", &gltf->warnlimit); +// JSON_WarnIfChild(prim, "extras", &gltf->warnlimit); + + GLTF_GetAccessor(gltf, JSON_GetInteger(attr, "TEXCOORD_0", -1), &tc_0); //float, ubyte, ushort + GLTF_GetAccessor(gltf, JSON_GetInteger(attr, "TEXCOORD_1", -1), &tc_1); //float, ubyte, ushort + GLTF_GetAccessor(gltf, JSON_GetInteger(attr, "NORMAL", -1), &norm); //float + GLTF_GetAccessor(gltf, JSON_GetInteger(attr, "TANGENT", -1), &tang); //float + GLTF_GetAccessor(gltf, JSON_GetInteger(attr, "POSITION", -1), &vpos); //float + GLTF_GetAccessor(gltf, JSON_GetInteger(attr, "COLOR_0", -1), &col0); //float, ubyte, ushort + GLTF_GetAccessor(gltf, JSON_GetInteger(prim, "indices", -1), &idx); + GLTF_GetAccessor(gltf, JSON_GetInteger(attr, "JOINTS_0", -1), &sidx); //ubyte, ushort + GLTF_GetAccessor(gltf, JSON_GetInteger(attr, "WEIGHTS_0", -1), &swgt); //float, ubyte, ushort + + if (JSON_GetInteger(attr, "JOINTS_1", -1) != -1 || JSON_GetInteger(attr, "WEIGHTS_1", -1) != -1) + if (gltf->warnlimit --> 0) + Con_Printf(CON_WARNING "%s: only 4 bones supported per vert\n", gltf->mod->name); //in case a model tries supplying more. we ought to renormalise the weights in this case. + + if (!vpos.count) + continue; + + surf = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf)); + + surf->surfaceid = meshidx; + surf->contents = FTECONTENTS_BODY; + surf->csurface.flags = 0; + surf->shares_bones = gltf->numsurfaces; + surf->shares_verts = gltf->numsurfaces; + JSON_ReadBody(meshname, surf->surfacename, sizeof(surf->surfacename)); + + surf->numverts = vpos.count; + if (idx.data) + { + surf->numindexes = idx.count; + surf->ofs_indexes = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofs_indexes) * idx.count); + if (idx.componentType == 5123) + { //unsigned shorts + for (i = 0; i < idx.count; i++) + surf->ofs_indexes[i] = *(unsigned short *)((char*)idx.data + i*idx.bytestride); + } + else if (idx.componentType == 5121) + { //unsigned bytes + for (i = 0; i < idx.count; i++) + surf->ofs_indexes[i] = *(unsigned char *)((char*)idx.data + i*idx.bytestride); + } + else if (idx.componentType == 5125) + { //unsigned ints + for (i = 0; i < idx.count; i++) + surf->ofs_indexes[i] = *(unsigned int *)((char*)idx.data + i*idx.bytestride); //FIXME: bounds check. + } + else + continue; + } + else + { + surf->numindexes = surf->numverts; + surf->ofs_indexes = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofs_indexes) * surf->numverts); + for (i = 0; i < surf->numverts; i++) + surf->ofs_indexes[i] = i; + } + + //swap winding order. we cull wrongly. + for (i = 0; i < idx.count; i+=3) + { + index_t t = surf->ofs_indexes[i+0]; + surf->ofs_indexes[i+0] = surf->ofs_indexes[i+2]; + surf->ofs_indexes[i+2] = t; + } + + surf->ofs_skel_xyz = GLTF_AccessorToDataF(gltf, surf->numverts, countof(surf->ofs_skel_xyz[0]), &vpos); + surf->ofs_skel_norm = GLTF_AccessorToDataF(gltf, surf->numverts, countof(surf->ofs_skel_norm[0]), &norm); + GLTF_AccessorToTangents(gltf, surf->ofs_skel_norm, &surf->ofs_skel_svect, &surf->ofs_skel_tvect, surf->numverts, &tang); + surf->ofs_st_array = GLTF_AccessorToDataF(gltf, surf->numverts, countof(surf->ofs_st_array[0]), &tc_0); + if (tc_1.data) + surf->ofs_lmst_array = GLTF_AccessorToDataF(gltf, surf->numverts, countof(surf->ofs_lmst_array[0]), &tc_1); + if (col0.data && col0.componentType == 5121) //UNSIGNED_BYTE + surf->ofs_rgbaub = GLTF_AccessorToDataUB(gltf, surf->numverts, countof(surf->ofs_rgbaub[0]), &col0); + else if (col0.data) + surf->ofs_rgbaf = GLTF_AccessorToDataF(gltf, surf->numverts, countof(surf->ofs_rgbaf[0]), &col0); + if (sidx.data && swgt.data) + { + surf->ofs_skel_idx = GLTF_AccessorToDataBone(gltf,surf->numverts, &sidx); + surf->ofs_skel_weight = GLTF_AccessorToDataF(gltf, surf->numverts, countof(surf->ofs_skel_weight[0]), &swgt); + + for (i = 0; i < surf->numverts; i++) + { + float len = surf->ofs_skel_weight[i][0]+surf->ofs_skel_weight[i][1]+surf->ofs_skel_weight[i][2]+surf->ofs_skel_weight[i][3]; + if (len) + Vector4Scale(surf->ofs_skel_weight[i], 1/len, surf->ofs_skel_weight[i]); + else + Vector4Set(surf->ofs_skel_weight[i], 0.5, 0.5, 0.5, 0.5); + } + } + else + { + surf->ofs_skel_idx = modfuncs->ZG_Malloc(&gltf->mod->memgroup, sizeof(surf->ofs_skel_idx[0]) * surf->numverts); + surf->ofs_skel_weight = modfuncs->ZG_Malloc(&gltf->mod->memgroup, sizeof(surf->ofs_skel_weight[0]) * surf->numverts); + for (i = 0; i < surf->numverts; i++) + { + Vector4Set(surf->ofs_skel_idx[i], basebone, 0, 0, 0); + Vector4Set(surf->ofs_skel_weight[i], 1, 0, 0, 0); + } + } + +// TransformArrayD(surf->ofs_skel_xyz, surf->numverts, pmatrix); +// TransformArrayA(surf->ofs_skel_norm, surf->numverts, pmatrix); +// TransformArrayA(surf->ofs_skel_svect, surf->numverts, pmatrix); + + for (i = 0; i < surf->numverts; i++) + { +// VectorScale(surf->ofs_skel_xyz[i], 32, surf->ofs_skel_xyz[i]); + for (j = 0; j < 3; j++) + { + if (mod->maxs[j] < surf->ofs_skel_xyz[i][j]) + mod->maxs[j] = surf->ofs_skel_xyz[i][j]; + if (mod->mins[j] > surf->ofs_skel_xyz[i][j]) + mod->mins[j] = surf->ofs_skel_xyz[i][j]; + } + } + + surf->numskins = 1; + surf->ofsskins = GLTF_LoadMaterial(gltf, mat, surf->ofs_rgbaub||surf->ofs_rgbaf); + + gltf->numsurfaces++; + surf->nextsurf = mod->meshinfo; + mod->meshinfo = surf; + } + return true; +} + +static void Matrix4D_Multiply(const double *a, const double *b, double *out) +{ + out[0] = a[0] * b[0] + a[4] * b[1] + a[8] * b[2] + a[12] * b[3]; + out[1] = a[1] * b[0] + a[5] * b[1] + a[9] * b[2] + a[13] * b[3]; + out[2] = a[2] * b[0] + a[6] * b[1] + a[10] * b[2] + a[14] * b[3]; + out[3] = a[3] * b[0] + a[7] * b[1] + a[11] * b[2] + a[15] * b[3]; + + out[4] = a[0] * b[4] + a[4] * b[5] + a[8] * b[6] + a[12] * b[7]; + out[5] = a[1] * b[4] + a[5] * b[5] + a[9] * b[6] + a[13] * b[7]; + out[6] = a[2] * b[4] + a[6] * b[5] + a[10] * b[6] + a[14] * b[7]; + out[7] = a[3] * b[4] + a[7] * b[5] + a[11] * b[6] + a[15] * b[7]; + + out[8] = a[0] * b[8] + a[4] * b[9] + a[8] * b[10] + a[12] * b[11]; + out[9] = a[1] * b[8] + a[5] * b[9] + a[9] * b[10] + a[13] * b[11]; + out[10] = a[2] * b[8] + a[6] * b[9] + a[10] * b[10] + a[14] * b[11]; + out[11] = a[3] * b[8] + a[7] * b[9] + a[11] * b[10] + a[15] * b[11]; + + out[12] = a[0] * b[12] + a[4] * b[13] + a[8] * b[14] + a[12] * b[15]; + out[13] = a[1] * b[12] + a[5] * b[13] + a[9] * b[14] + a[13] * b[15]; + out[14] = a[2] * b[12] + a[6] * b[13] + a[10] * b[14] + a[14] * b[15]; + out[15] = a[3] * b[12] + a[7] * b[13] + a[11] * b[14] + a[15] * b[15]; +} + +static void GenMatrixPosQuat4ScaleDouble(const double pos[3], const double quat[4], const double scale[3], double result[16]) +{ + float xx, xy, xz, xw, yy, yz, yw, zz, zw; + float x2, y2, z2; + float s; + x2 = quat[0] + quat[0]; + y2 = quat[1] + quat[1]; + z2 = quat[2] + quat[2]; + + xx = quat[0] * x2; xy = quat[0] * y2; xz = quat[0] * z2; + yy = quat[1] * y2; yz = quat[1] * z2; zz = quat[2] * z2; + xw = quat[3] * x2; yw = quat[3] * y2; zw = quat[3] * z2; + + s = scale[0]; + result[0*4+0] = s*(1.0f - (yy + zz)); + result[1*4+0] = s*(xy + zw); + result[2*4+0] = s*(xz - yw); + result[3*4+0] = 0; + + s = scale[1]; + result[0*4+1] = s*(xy - zw); + result[1*4+1] = s*(1.0f - (xx + zz)); + result[2*4+1] = s*(yz + xw); + result[3*4+1] = 0; + + s = scale[2]; + result[0*4+2] = s*(xz + yw); + result[1*4+2] = s*(yz - xw); + result[2*4+2] = s*(1.0f - (xx + yy)); + result[3*4+2] = 0; + + result[0*4+3] = pos[0]; + result[1*4+3] = pos[1]; + result[2*4+3] = pos[2]; + result[3*4+3] = 1; +} + +static qboolean GLTF_ProcessNode(gltf_t *gltf, int nodeidx, double pmatrix[16], int parentidx, qboolean isjoint) +{ + json_t *c; + json_t *node; + json_t *t; + json_t *skin; + int mesh; + int skinidx; + struct gltfbone_s *b; + if (nodeidx < 0 || nodeidx >= gltf->numbones) + return false; + node = JSON_FindIndexedChild(gltf->r, "nodes", nodeidx); + if (!node) + return false; + + b = &gltf->bones[nodeidx]; + b->parent = parentidx; + + t = JSON_FindChild(node, "matrix"); + if (t) + { + b->rel.rmatrix[0*4+0] = JSON_GetIndexedFloat(t, 0, 1.0); + b->rel.rmatrix[1*4+0] = JSON_GetIndexedFloat(t, 1, 0.0); + b->rel.rmatrix[2*4+0] = JSON_GetIndexedFloat(t, 2, 0.0); + b->rel.rmatrix[3*4+0] = JSON_GetIndexedFloat(t, 3, 0.0); + b->rel.rmatrix[0*4+1] = JSON_GetIndexedFloat(t, 4, 0.0); + b->rel.rmatrix[1*4+1] = JSON_GetIndexedFloat(t, 5, 1.0); + b->rel.rmatrix[2*4+1] = JSON_GetIndexedFloat(t, 6, 0.0); + b->rel.rmatrix[3*4+1] = JSON_GetIndexedFloat(t, 7, 0.0); + b->rel.rmatrix[0*4+2] = JSON_GetIndexedFloat(t, 8, 0.0); + b->rel.rmatrix[1*4+2] = JSON_GetIndexedFloat(t, 9, 0.0); + b->rel.rmatrix[2*4+2] = JSON_GetIndexedFloat(t, 10,1.0); + b->rel.rmatrix[3*4+2] = JSON_GetIndexedFloat(t, 11,0.0); + b->rel.rmatrix[0*4+3] = JSON_GetIndexedFloat(t, 12,0.0); + b->rel.rmatrix[1*4+3] = JSON_GetIndexedFloat(t, 13,0.0); + b->rel.rmatrix[2*4+3] = JSON_GetIndexedFloat(t, 14,0.0); + b->rel.rmatrix[3*4+3] = JSON_GetIndexedFloat(t, 15,1.0); + + Vector4Set(b->rel.quat, 0,0,0,1); + VectorSet(b->rel.scale,1,1,1); + VectorSet(b->rel.trans,0,0,0); + } + else + { + double rot[4]; + double scale[3]; + double trans[3]; + t = JSON_FindChild(node, "rotation"); + rot[0] = JSON_GetIndexedFloat(t, 0, 0.0); + rot[1] = JSON_GetIndexedFloat(t, 1, 0.0); + rot[2] = JSON_GetIndexedFloat(t, 2, 0.0); + rot[3] = JSON_GetIndexedFloat(t, 3, 1.0); + t = JSON_FindChild(node, "scale"); + scale[0] = JSON_GetIndexedFloat(t, 0, 1.0); + scale[1] = JSON_GetIndexedFloat(t, 1, 1.0); + scale[2] = JSON_GetIndexedFloat(t, 2, 1.0); + t = JSON_FindChild(node, "translation"); + trans[0] = JSON_GetIndexedFloat(t, 0, 0.0); + trans[1] = JSON_GetIndexedFloat(t, 1, 0.0); + trans[2] = JSON_GetIndexedFloat(t, 2, 0.0); + + Vector4Copy(rot, b->rel.quat); + VectorCopy(scale, b->rel.scale); + VectorCopy(trans, b->rel.trans); + + //T * R * S + GenMatrixPosQuat4ScaleDouble(trans, rot, scale, b->rel.rmatrix); +/* + memset(mmatrix, 0, sizeof(mmatrix)); + mmatrix[0] = 1; + mmatrix[5] = 1; + (void)rot,(void)scale; + mmatrix[10] = 1; + mmatrix[15] = 1; + mmatrix[3] = trans[0]; + mmatrix[7] = trans[1]; + mmatrix[11] = trans[2]; +*/ + } + Matrix4D_Multiply(b->rel.rmatrix, pmatrix, b->amatrix); + + skinidx = JSON_GetInteger(node, "skin", -1); + if (skinidx >= 0) + { +// double identity[16]; + int j; + json_t *joints; + struct gltf_accessor inverse; + float *inversef; + + skin = JSON_FindIndexedChild(gltf->r, "skins", skinidx); + + joints = JSON_FindChild(skin, "joints"); + GLTF_GetAccessor(gltf, JSON_GetInteger(skin, "inverseBindMatrices", -1), &inverse); + inversef = inverse.data; + if (inverse.componentType != 5126/*FLOAT*/ || inverse.type != ((4<<8) | 4)/*mat4x4*/) + inverse.count = 0; + for (j = 0; j < countof(gltf->bonemap); j++, inversef+=inverse.bytestride/sizeof(float)) + { + int b = JSON_GetIndexedInteger(joints, j, -1); + if (b < 0) + break; + gltf->bonemap[j] = b; + if (j < inverse.count) + { + gltf->bones[b].inverse[0] = inversef[0*4+0]; + gltf->bones[b].inverse[1] = inversef[1*4+0]; + gltf->bones[b].inverse[2] = inversef[2*4+0]; + gltf->bones[b].inverse[3] = inversef[3*4+0]; + + gltf->bones[b].inverse[4] = inversef[0*4+1]; + gltf->bones[b].inverse[5] = inversef[1*4+1]; + gltf->bones[b].inverse[6] = inversef[2*4+1]; + gltf->bones[b].inverse[7] = inversef[3*4+1]; + + gltf->bones[b].inverse[8] = inversef[0*4+2]; + gltf->bones[b].inverse[9] = inversef[1*4+2]; + gltf->bones[b].inverse[10]= inversef[2*4+2]; + gltf->bones[b].inverse[11]= inversef[3*4+2]; + + gltf->bones[b].inverse[12]= inversef[0*4+3]; + gltf->bones[b].inverse[13]= inversef[1*4+3]; + gltf->bones[b].inverse[14]= inversef[2*4+3]; + gltf->bones[b].inverse[15]= inversef[3*4+3]; + } + else + { + gltf->bones[b].inverse[0] = 1; + gltf->bones[b].inverse[1] = 0; + gltf->bones[b].inverse[2] = 0; + gltf->bones[b].inverse[3] = 0; + + gltf->bones[b].inverse[4] = 0; + gltf->bones[b].inverse[5] = 1; + gltf->bones[b].inverse[6] = 0; + gltf->bones[b].inverse[7] = 0; + + gltf->bones[b].inverse[8] = 0; + gltf->bones[b].inverse[9] = 0; + gltf->bones[b].inverse[10]= 1; + gltf->bones[b].inverse[11]= 0; + + gltf->bones[b].inverse[12]= 0; + gltf->bones[b].inverse[13]= 0; + gltf->bones[b].inverse[14]= 0; + gltf->bones[b].inverse[15]= 1; + } + } + +// GLTF_ProcessNode(gltf, JSON_GetInteger(skin, "skeleton", -1), identity, nodeidx, true); + + JSON_FlagAsUsed(node, "name"); + } + + mesh = JSON_GetInteger(node, "mesh", -1); + if (mesh >= 0) + GLTF_ProcessMesh(gltf, mesh, nodeidx, b->amatrix); + + for(c = JSON_FindIndexedChild(node, "children", 0); c; c = c->sibling) + { + c->used = true; + GLTF_ProcessNode(gltf, JSON_GetInteger(c, NULL, -1), b->amatrix, nodeidx, isjoint); + } + + JSON_FlagAsUsed(node, "camera"); + + JSON_WarnIfChild(node, "weights", &gltf->warnlimit); //default value for morph weight animations + JSON_WarnIfChild(node, "extensions", &gltf->warnlimit); +// JSON_WarnIfChild(node, "extras", &gltf->warnlimit); + + return true; +} + +struct gltf_animsampler +{ + struct gltf_accessor input; + struct gltf_accessor output; +}; +static struct gltf_animsampler GLTF_AnimationSampler(gltf_t *gltf, json_t *samplers, int sampleridx, int elems) +{ + struct gltf_animsampler r; + json_t *sampler = JSON_FindIndexedChild(samplers, NULL, sampleridx); + + GLTF_GetAccessor(gltf, JSON_GetInteger(sampler, "input", -1), &r.input); + GLTF_GetAccessor(gltf, JSON_GetInteger(sampler, "output", -1), &r.output); + + if (!r.input.data || !r.output.data || r.input.count != r.output.count) + memset(&r, 0, sizeof(r)); + return r; +} + +static float Anim_GetTime(struct gltf_accessor *in, int index) +{ + //read the input sampler (to get timestamps) + switch(in->componentType) + { + case 5120: //BYTE + return max(-1, (*(signed char*)((qbyte*)in->data + in->bytestride*index)) / 127.0); + case 5121: //UNSIGNED_BYTE + return (*(unsigned char*)((qbyte*)in->data + in->bytestride*index)) / 255.0; + case 5122: //SHORT + return max(-1, (*(signed short*)((qbyte*)in->data + in->bytestride*index)) / 32767.0); + case 5123: //UNSIGNED_SHORT + return (*(unsigned short*)((qbyte*)in->data + in->bytestride*index)) / 65535.0; + case 5125: //UNSIGNED_INT + return (*(unsigned int*)((qbyte*)in->data + in->bytestride*index)) / (double)~0u; + case 5126: //FLOAT + return *(float*)((qbyte*)in->data + in->bytestride*index); + default: + Con_Printf("Unsupported input component type\n"); + return 0; + } +} +static void Anim_GetVal(struct gltf_accessor *in, int index, float *result, int elems) +{ + //read the input sampler (to get timestamps) + switch(in->componentType) + { + case 5120: //BYTE + while (elems --> 0) + result[elems] = max(-1, ((signed char*)((qbyte*)in->data + in->bytestride*index))[elems] / 127.0); + break; + case 5121: //UNSIGNED_BYTE + while (elems --> 0) + result[elems] = ((unsigned char*)((qbyte*)in->data + in->bytestride*index))[elems] / 255.0; + break; + case 5122: //SHORT + while (elems --> 0) + result[elems] = max(-1, ((signed short*)((qbyte*)in->data + in->bytestride*index))[elems] / 32767.0); + break; + case 5123: //UNSIGNED_SHORT + while (elems --> 0) + result[elems] = ((unsigned short*)((qbyte*)in->data + in->bytestride*index))[elems] / 65535.0; + break; + case 5125: //UNSIGNED_INT + while (elems --> 0) + result[elems] = ((unsigned int*)((qbyte*)in->data + in->bytestride*index))[elems] / (double)~0u; + break; + case 5126: //FLOAT + while (elems --> 0) + result[elems] = ((float*)((qbyte*)in->data + in->bytestride*index))[elems]; + break; + default: + Con_Printf("Unsupported output component type\n"); + break; + } +} +static void LerpAnimData(gltf_t *gltf, struct gltf_animsampler *samp, float time, float *result, int elems) +{ + float t1, t2; + float w1, w2; + float v1[4], v2[4]; + int f1 = 0, f2, c; + + struct gltf_accessor *in = &samp->input; + struct gltf_accessor *out = &samp->output; + + t1 = t2 = Anim_GetTime(in, 0); + for (f2 = 1 ; f2 < in->count; f2++) + { + t2 = Anim_GetTime(in, f2); + if (t2 > time) + break; //now have before and after + t1 = t2; + f1 = f2; + } + + //assume linear + if (f1==f2 || t1==t2) + { + Anim_GetVal(out, f1, result, elems); + return; + } + w2 = (time-t1)/(t2-t1); + w1 = 1-w2; + + Anim_GetVal(out, f1, v1, elems); + Anim_GetVal(out, f2, v2, elems); + for (c = 0; c < elems; c++) + result[c] = v1[c]*w1 + w2*v2[c]; +} + +static void GLTF_RemapBone(gltf_t *gltf, int *nextidx, int b) +{ //potentially needs to walk to the root before the child. recursion sucks. + if (gltf->bonemap[b] >= 0) + return; //already got remapped + GLTF_RemapBone(gltf, nextidx, gltf->bones[b].parent); + gltf->bonemap[b] = (*nextidx)++; +} +static void GLTF_RewriteBoneTree(gltf_t *gltf) +{ + galiasinfo_t *surf; + int j, n; + struct gltfbone_s *tmpbones; + + for (j = 0; j < gltf->numbones; j++) + { + if (gltf->bones[j].parent >= j) + break; + } + if (j == gltf->numbones) + { + for (j = 0; j < gltf->numbones; j++) + gltf->bonemap[j] = j; + return; //all are ordered okay + } + + for (j = 0; j < gltf->numbones; j++) + gltf->bonemap[j] = -1; + for ( ; j < MAX_BONES; j++) + gltf->bonemap[j] = 0; + n = 0; + for (j = 0; j < gltf->numbones; j++) + GLTF_RemapBone(gltf, &n, j); + + tmpbones = malloc(sizeof(*tmpbones)*gltf->numbones); + memcpy(tmpbones, gltf->bones, sizeof(*tmpbones)*gltf->numbones); + for (j = 0; j < gltf->numbones; j++) + gltf->bones[gltf->bonemap[j]] = tmpbones[j]; + for (j = 0; j < gltf->numbones; j++) + if (gltf->bones[j].parent >= 0) + gltf->bones[j].parent = gltf->bonemap[gltf->bones[j].parent]; + + for(surf = gltf->mod->meshinfo; surf; surf = surf->nextsurf) + { + for (j = 0; j < surf->numverts; j++) + for (n = 0; n < countof(surf->ofs_skel_idx[j]); n++) + surf->ofs_skel_idx[j][n] = gltf->bonemap[surf->ofs_skel_idx[j][n]]; + } +} + +//okay, so gltf is some weird scene thing. +//mostly there should be some default scene, so we'll just use that. +//we do NOT supported nested nodes right now... +static qboolean GLTF_LoadModel(struct model_s *mod, char *json, size_t jsonsize, void *buffer, size_t buffersize) +{ + gltf_t gltf; + int pos=0, j, k; + json_t *scene, *n, *anim; + double rootmatrix[16]; + double gltfver; + galiasinfo_t *surf; + galiasbone_t *bone; + galiasanimation_t *framegroups = NULL; + unsigned int numframegroups = 0; + float *baseframe; + memset(&gltf, 0, sizeof(gltf)); + gltf.r = JSON_Parse(NULL, mod->name, NULL, json, &pos, jsonsize); + gltf.mod = mod; + gltf.buffers[0].data = buffer; + gltf.buffers[0].length = buffersize; + gltf.warnlimit = 5; + + //asset.version must exist, supposedly. + gltfver = JSON_GetFloat(gltf.r, "asset.version", 2.0); + if (gltfver == 2.0) + { + JSON_FlagAsUsed(gltf.r, "asset.copyright"); + JSON_FlagAsUsed(gltf.r, "asset.generator"); + JSON_WarnIfChild(gltf.r, "asset.minVersion", &gltf.warnlimit); + JSON_WarnIfChild(gltf.r, "asset.extensions", &gltf.warnlimit); + + for(n = JSON_FindIndexedChild(gltf.r, "extensionsRequired", 0); n; n = n->sibling) + { + char extname[256]; + JSON_ReadBody(n, extname, sizeof(extname)); + Con_Printf(CON_ERROR "%s: Required gltf2 extension \"%s\" not supported\n", mod->name, extname); + + JSON_Destroy(gltf.r); + return false; + } + for(n = JSON_FindIndexedChild(gltf.r, "extensionsUsed", 0); n; n = n->sibling) + { //must be a superset of the above. + char extname[256]; + JSON_ReadBody(n, extname, sizeof(extname)); + if (!strcmp(extname, "KHR_materials_pbrSpecularGlossiness")) + ; + else if (!strcmp(extname, "KHR_texture_transform")) + ; + else + Con_Printf(CON_WARNING "%s: gltf2 extension \"%s\" not known\n", mod->name, extname); + } + + //we don't really care about cameras. + JSON_FlagAsUsed(gltf.r, "cameras"); + + scene = JSON_FindIndexedChild(gltf.r, "scenes", JSON_GetInteger(gltf.r, "scene", 0)); + + memset(&rootmatrix, 0, sizeof(rootmatrix)); +#if 1 //transform from gltf to quake. mostly only needed for the base pose. + rootmatrix[2] = rootmatrix[4] = rootmatrix[9] = 1; rootmatrix[15] = 1; +#else + rootmatrix[0] = rootmatrix[5] = rootmatrix[10] = 1; rootmatrix[15] = 1; +#endif + + for (j = 0; j < countof(gltf.bones); j++) + { + n = JSON_FindIndexedChild(gltf.r, "nodes", j); + if (!n) + break; + if (!JSON_ReadBody(JSON_FindChild(n, "name"), gltf.bones[j].name, sizeof(gltf.bones[j].name))) + { + if (n) + JSON_GetPath(n, true, gltf.bones[j].name, sizeof(gltf.bones[j].name)); + else + Q_snprintf(gltf.bones[j].name, sizeof(gltf.bones[j].name), "bone%i", j); + } + gltf.bones[j].parent = -1; + gltf.bones[j].amatrix[0] = gltf.bones[j].amatrix[5] = gltf.bones[j].amatrix[10] = gltf.bones[j].amatrix[15] = 1; + gltf.bones[j].inverse[0] = gltf.bones[j].inverse[5] = gltf.bones[j].inverse[10] = gltf.bones[j].inverse[15] = 1; + gltf.bones[j].rel.rmatrix[0] = gltf.bones[j].rel.rmatrix[5] = gltf.bones[j].rel.rmatrix[10] = gltf.bones[j].rel.rmatrix[15] = 1; + } + gltf.numbones = j; + + JSON_FlagAsUsed(scene, "name"); + JSON_WarnIfChild(scene, "extensions", &gltf.warnlimit); +// JSON_WarnIfChild(scene, "extras"); + for (j = 0; ; j++) + { + n = JSON_FindIndexedChild(scene, "nodes", j); + if (!n) + break; + n->used = true; + if (!GLTF_ProcessNode(&gltf, JSON_GetInteger(n, NULL, -1), rootmatrix, -1, false)) + break; + } + + GLTF_RewriteBoneTree(&gltf); + + bone = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*bone)*gltf.numbones); + baseframe = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(float)*12*gltf.numbones); + for (j = 0; j < gltf.numbones; j++) + { + Q_strlcpy(bone[j].name, gltf.bones[j].name, sizeof(bone[j].name)); + bone[j].parent = gltf.bones[j].parent; + + for(k = 0; k < 12; k++) + { + baseframe[j*12+k] = gltf.bones[j].amatrix[k]; + bone[j].inverse[k] = gltf.bones[j].inverse[k]; + } + } + + for(anim = JSON_FindIndexedChild(gltf.r, "animations", 0); anim; anim = anim->sibling) + numframegroups++; + if (numframegroups) + { + framegroups = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*framegroups)*numframegroups); + for (k = 0; k < numframegroups; k++) + { + galiasanimation_t *fg = &framegroups[k]; + json_t *anim = JSON_FindIndexedChild(gltf.r, "animations", k); + json_t *chan; + json_t *samps = JSON_FindChild(anim, "samplers"); + int f, l; + float maxtime = 0; + struct + { + struct gltf_animsampler rot,scale,trans; + } b[MAX_BONES]; + memset(b, 0, sizeof(b)); + + if (!JSON_ReadBody(JSON_FindChild(anim, "name"), fg->name, sizeof(fg->name))) + { + if (anim) + JSON_GetPath(anim, true, fg->name, sizeof(fg->name)); + else + Q_snprintf(fg->name, sizeof(fg->name), "anim%i", k); + } + fg->loop = true; + fg->skeltype = SKEL_RELATIVE; + for(chan = JSON_FindIndexedChild(anim, "channels", 0); chan; chan = chan->sibling) + { + struct gltf_animsampler s; + json_t *targ = JSON_FindChild(chan, "target"); + int sampler = JSON_GetInteger(chan, "sampler", -1); + int bone = JSON_GetInteger(targ, "node", -2); + json_t *path = JSON_FindChild(targ, "path"); + if (bone == -2) + continue; //'When node isn't defined, channel should be ignored' + if (bone < 0 || bone >= gltf.numbones) + { + if (gltf.warnlimit --> 0) + Con_Printf("%s: invalid node index %i\n", mod->name, bone); + continue; //error... + } + bone = gltf.bonemap[bone]; + s = GLTF_AnimationSampler(&gltf, samps, sampler, 4); + maxtime = max(maxtime, s.input.maxs[0]); + if (JSON_Equals(path, NULL, "rotation")) + b[bone].rot = s; + else if (JSON_Equals(path, NULL, "scale")) + b[bone].scale = s; + else if (JSON_Equals(path, NULL, "translation")) + b[bone].trans = s; + else if (gltf.warnlimit --> 0) + { //these are unsupported + if (JSON_Equals(path, NULL, "weights")) //morph weights + Con_Printf("%s: morph animations are not supported\n", mod->name); + else + Con_Printf("%s: undocumented animation type\n", mod->name); + } + } + + //TODO: make a guess at the number of frames+framerate + //(input samplers have min+max values). + + fg->rate = 30; + fg->numposes = max(1, maxtime*fg->rate); + if (maxtime) + fg->rate = fg->numposes/maxtime; //fix up the rate so we hit the exact end of the animation (so it doesn't have to be quite so exact). + + fg->skeltype = SKEL_RELATIVE; + fg->boneofs = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*fg->boneofs)*12*gltf.numbones*fg->numposes); + + for (f = 0; f < fg->numposes; f++) + { + float *bonematrix = &fg->boneofs[f*gltf.numbones*12]; + float time = f/fg->rate; + for (j = 0; j < gltf.numbones; j++, bonematrix+=12) + { + float scale[3]; + float rot[4]; + float trans[3]; + //eww, weird inheritance crap. + if (b[j].rot.input.data || b[j].scale.input.data || b[j].trans.input.data) + { + VectorCopy(gltf.bones[j].rel.scale, scale); + Vector4Copy(gltf.bones[j].rel.quat, rot); + VectorCopy(gltf.bones[j].rel.trans, trans); + + if (b[j].rot.input.data) + LerpAnimData(&gltf, &b[j].rot, time, rot, 4); + if (b[j].scale.input.data) + LerpAnimData(&gltf, &b[j].scale, time, scale, 3); + if (b[j].trans.input.data) + LerpAnimData(&gltf, &b[j].trans, time, trans, 3); + //figure out the bone matrix... + modfuncs->GenMatrixPosQuat4Scale(trans, rot, scale, bonematrix); + } + else + { //nothing animated, use what we calculated earlier. + for (l = 0; l < 12; l++) + bonematrix[l] = gltf.bones[j].rel.rmatrix[l]; + } + if (gltf.bones[j].parent < 0) + { //rotate any root bones from gltf to quake's orientation. + float fnar[12]; + static float toquake[12]={0,0,1,0,1,0,0,0,0,1,0,0}; + memcpy(fnar, bonematrix, sizeof(fnar)); + modfuncs->ConcatTransforms((void*)toquake, (void*)fnar, (void*)bonematrix); + } + } + } + } + } + + for(surf = mod->meshinfo; surf; surf = surf->nextsurf) + { + surf->shares_bones = 0; + surf->numbones = gltf.numbones; + surf->ofsbones = bone; + surf->baseframeofs = baseframe; + surf->ofsanimations = framegroups; + surf->numanimations = numframegroups; + surf->contents = FTECONTENTS_BODY; + surf->csurface.flags = 0; + surf->geomset = ~0; //invalid set = always visible. FIXME: set this according to scene numbers? + surf->geomid = 0; + } + + JSON_WarnUnused(gltf.r, &gltf.warnlimit); + } + else + Con_Printf("%s: unsupported gltf version (%.2f)\n", mod->name, gltfver); + JSON_Destroy(gltf.r); + + + + mod->type = mod_alias; + return !!mod->meshinfo; +} +qboolean QDECL Mod_LoadGLTFModel (struct model_s *mod, void *buffer, size_t fsize) +{ + //just straight json. + return GLTF_LoadModel(mod, buffer, fsize, NULL, 0); +} +//glb files are some binary header, a lump with json data, and optionally a lump with binary data +qboolean QDECL Mod_LoadGLBModel (struct model_s *mod, void *buffer, size_t fsize) +{ + unsigned char *header = buffer; + unsigned int magic = header[0]|(header[1]<<8)|(header[2]<<16)|(header[3]<<24); + unsigned int version = header[4]|(header[5]<<8)|(header[6]<<16)|(header[7]<<24); + unsigned int length = header[8]|(header[9]<<8)|(header[10]<<16)|(header[11]<<24); + + unsigned int jsonlen = header[12]|(header[13]<<8)|(header[14]<<16)|(header[15]<<24); + unsigned int jsontype = header[16]|(header[17]<<8)|(header[18]<<16)|(header[19]<<24); + char *json = (char*)(header+20); + + unsigned int binlen = header[20+jsonlen]|(header[21+jsonlen]<<8)|(header[22+jsonlen]<<16)|(header[23+jsonlen]<<24); + unsigned int bintype = header[24+jsonlen]|(header[25+jsonlen]<<8)|(header[26+jsonlen]<<16)|(header[27+jsonlen]<<24); + unsigned char *bin = header+28+jsonlen; + + if (fsize < 28) + return false; + if (magic != (('F'<<24)+('T'<<16)+('l'<<8)+'g')) + return false; + if (version != 2) + return false; + if (jsontype != 0x4E4F534A) //'JSON' + return false; + if (length != 28+jsonlen+binlen) + return false; + if (bintype != 0x004E4942) //'BIN\0' + return false; + + return GLTF_LoadModel(mod, json, jsonlen, bin, binlen); +} +#endif + diff --git a/plugins/models/models.c b/plugins/models/models.c new file mode 100644 index 000000000..d8698ab02 --- /dev/null +++ b/plugins/models/models.c @@ -0,0 +1,843 @@ +#ifndef GLQUAKE +#define GLQUAKE //this is shit. +#endif +#include "quakedef.h" +#include "../plugin.h" +#include "com_mesh.h" +modplugfuncs_t *modfuncs; + +//#define ASEMODELS //FIXME: TEST TEST TEST. shold be working iiuc +//#define LWOMODELS //not working +#define GLTFMODELS //FIXME: not yet working properly. + +#ifdef ASEMODELS +struct aseimport_s +{ + char *file; + + struct asematerial_s + { + char name[MAX_QPATH]; + + struct asemesh_s + { + struct asemesh_s *next; + + struct asevert_s + { + vec3_t xyz; + vec3_t norm; + vec2_t st; + } *vert; + unsigned int numverts; + unsigned int maxverts; + + index_t *index; + unsigned int numindexes; + unsigned int maxindexes; + + } *meshes; + } *materials; + unsigned int nummaterials; +}; + +static char *ASE_GetToken(struct aseimport_s *ase, char *token, size_t tokensize) +{ + char *ret = token; + tokensize--; + + while(*ase->file == ' ' || *ase->file == '\t') + ase->file++; + if (*ase->file == '\n') + { + *ret = 0; + return ret; + } + + if (*ase->file == '\"') + { + ase->file += 1; + while(tokensize-- > 0) + { + char i = *ase->file; + if (i == '\n') + break; + ase->file+=1; + if (i == '\"') + break; + + *token++ = i; + } + } + else + { + while(tokensize-- > 0) + { + char i = *ase->file; + if (i == ' ' || i == '\t' || i == '\r' || i == '\n') + break; + ase->file+=1; + + *token++ = i; + } + } + *token = 0; + return ret; +} + +static void ASE_SkipLine(struct aseimport_s *ase) +{ + while (*ase->file) + { + if (*ase->file == '\n') + { + ase->file += 1; + break; + } + ase->file += 1; + } +} + +static void ASE_SkipBlock(struct aseimport_s *ase) +{ + while (*ase->file) + { + if (*ase->file == '\n' || *ase->file == '{') + break; + ase->file += 1; + } + if (*ase->file == '{') + { + char token[1024]; + ASE_SkipLine(ase); + + while (*ase->file) + { + ASE_GetToken(ase, token, sizeof(token)); + if (!strcmp(token, "}")) + break; + else + { + ASE_SkipBlock(ase); + } + ASE_SkipLine(ase); + } + } +} + +static void ASE_Material(struct aseimport_s *ase) +{ + int idx; + char token[1024]; + ASE_GetToken(ase, token, sizeof(token)); + idx = atoi(token); + + if (idx < 0 || idx >= ase->nummaterials) + { + Con_Printf("invalid material index: %s\n", token); + ASE_SkipBlock(ase); + return; + } + + ASE_GetToken(ase, token, sizeof(token)); + ASE_SkipLine(ase); + if (strcmp(token, "{")) + return; + + while (*ase->file) + { + ASE_GetToken(ase, token, sizeof(token)); + if (!strcmp(token, "}")) + break; + else if (!strcmp(token, "*MATERIAL_NAME")) + { + ASE_GetToken(ase, token, sizeof(token)); + Q_strlcpy(ase->materials[idx].name, token, sizeof(ase->materials[idx].name)); + } + else if (!strcmp(token, "*MATERIAL_CLASS") || !strcmp(token, "*MATERIAL_DIFFUSE") || !strcmp(token, "*MATERIAL_SHADING") || !strcmp(token, "*MAP_DIFFUSE")) + ASE_SkipBlock(ase); + else + { + Con_Printf("Unknown top-level identifier: %s\n", token); + ASE_SkipBlock(ase); + } + ASE_SkipLine(ase); + } +} +static void ASE_MaterialList(struct aseimport_s *ase) +{ + char token[1024]; + ASE_GetToken(ase, token, sizeof(token)); + ASE_SkipLine(ase); + if (strcmp(token, "{")) + return; + + while (*ase->file) + { + ASE_GetToken(ase, token, sizeof(token)); + if (!strcmp(token, "}")) + break; + else if (!strcmp(token, "*MATERIAL_COUNT")) + { + ASE_GetToken(ase, token, sizeof(token)); + + if (!ase->materials) + { + ase->nummaterials = atoi(token); + ase->materials = malloc(sizeof(*ase->materials) * ase->nummaterials); + memset(ase->materials, 0, sizeof(*ase->materials) * ase->nummaterials); + } + } + else if (!strcmp(token, "*MATERIAL")) + { + ASE_Material(ase); + } + else + { + Con_Printf("Unknown top-level identifier: %s\n", token); + ASE_SkipBlock(ase); + } + ASE_SkipLine(ase); + } +} + +static void ASE_GeomObject(struct aseimport_s *ase) +{ + size_t materialidx = 0; + size_t numverts = 0; + size_t numtverts = 0; + size_t numtris = 0; + struct + { + vec3_t xyz; + vec3_t norm; + } *verts = NULL; + struct + { + vec3_t st; + } *tverts = NULL; + struct + { + index_t vidx[3]; + index_t tidx[3]; + } *tris = NULL; + size_t idx; + + char token[1024]; + ASE_GetToken(ase, token, sizeof(token)); + ASE_SkipLine(ase); + if (strcmp(token, "{")) + return; + + while (*ase->file) + { + ASE_GetToken(ase, token, sizeof(token)); + if (!strcmp(token, "}")) + break; + else if (!strcmp(token, "*MATERIAL_REF")) + { + ASE_GetToken(ase, token, sizeof(token)); + materialidx = atoi(token); + } + else if (!strcmp(token, "*MESH")) + { + ASE_SkipLine(ase); + + while (*ase->file) + { + ASE_GetToken(ase, token, sizeof(token)); + if (!strcmp(token, "}")) + break; + else if (!strcmp(token, "*TIMEVALUE")) + ASE_SkipBlock(ase); //not useful + else if (!strcmp(token, "*MESH_NUMVERTEX")) + { + ASE_GetToken(ase, token, sizeof(token)); + if (!verts) + { + numverts = atoi(token); + verts = malloc(sizeof(*verts) * numverts); + memset(verts, 0, sizeof(*verts) * numverts); + } + } + else if (!strcmp(token, "*MESH_NUMFACES")) + { + ASE_GetToken(ase, token, sizeof(token)); + if (!tris) + { + numtris = atoi(token); + tris = malloc(sizeof(*tris) * numtris); + memset(tris, 0, sizeof(*tris) * numtris); + } + } + else if (!strcmp(token, "*COMMENT")) + ASE_SkipBlock(ase); //unused + else if (!strcmp(token, "*MESH_VERTEX_LIST")) + { + ASE_SkipLine(ase); + while (*ase->file) + { + ASE_GetToken(ase, token, sizeof(token)); + if (!strcmp(token, "}")) + break; + else if (!strcmp(token, "*MESH_VERTEX")) + { + ASE_GetToken(ase, token, sizeof(token)); + idx = atoi(token); + if (idx >= 0 && idx < numverts) + { + ASE_GetToken(ase, token, sizeof(token)); + verts[idx].xyz[0] = atof(token); + ASE_GetToken(ase, token, sizeof(token)); + verts[idx].xyz[1] = atof(token); + ASE_GetToken(ase, token, sizeof(token)); + verts[idx].xyz[2] = atof(token); + } + } + else + { + Con_Printf("Unknown MESH_VERTEX_LIST identifier: %s\n", token); + ASE_SkipBlock(ase); + } + ASE_SkipLine(ase); + } + } + else if (!strcmp(token, "*MESH_NORMALS")) + { + ASE_SkipLine(ase); + while (*ase->file) + { + ASE_GetToken(ase, token, sizeof(token)); + if (!strcmp(token, "}")) + break; + else if (!strcmp(token, "*MESH_FACENORMAL")) + ASE_SkipBlock(ase); //we don't give a fuck about these. its not usable for us. we'll calculate these if we actually need them, that way we're sure it actually matches the geometry and doesn't bug out. + else if (!strcmp(token, "*MESH_VERTEXNORMAL")) + { + ASE_GetToken(ase, token, sizeof(token)); + idx = atoi(token); + if (idx >= 0 && idx < numverts) + { + ASE_GetToken(ase, token, sizeof(token)); + verts[idx].norm[0] = atof(token); + ASE_GetToken(ase, token, sizeof(token)); + verts[idx].norm[1] = atof(token); + ASE_GetToken(ase, token, sizeof(token)); + verts[idx].norm[2] = atof(token); + } + } + else + { + Con_Printf("Unknown MESH_NORMALS identifier: %s\n", token); + ASE_SkipBlock(ase); + } + ASE_SkipLine(ase); + } + } + else if (!strcmp(token, "*MESH_FACE_LIST")) + { + ASE_SkipLine(ase); + while (*ase->file) + { + ASE_GetToken(ase, token, sizeof(token)); + if (!strcmp(token, "}")) + break; + else if (!strcmp(token, "*MESH_FACE")) + { + ASE_GetToken(ase, token, sizeof(token)); + idx = atoi(token); + if (idx >= 0 && idx < numtris) + { + ASE_GetToken(ase, token, sizeof(token)); + while(*token) + { + if (!strcmp(token, "A:")) + { + ASE_GetToken(ase, token, sizeof(token)); + tris[idx].vidx[0] = atoi(token); + } + else if (!strcmp(token, "B:")) + { + ASE_GetToken(ase, token, sizeof(token)); + tris[idx].vidx[1] = atoi(token); + } + else if (!strcmp(token, "C:")) + { + ASE_GetToken(ase, token, sizeof(token)); + tris[idx].vidx[2] = atoi(token); + } + else if (!strcmp(token, "AB:") || !strcmp(token, "BC:") || !strcmp(token, "CA:") || !strcmp(token, "*MESH_SMOOTHING") || !strcmp(token, "*MESH_MTLID")) + { + ASE_GetToken(ase, token, sizeof(token)); + } + else + { + Con_Printf("Unknown MESH_FACE identifier: %s\n", token); + ASE_GetToken(ase, token, sizeof(token)); + } + + ASE_GetToken(ase, token, sizeof(token)); + } + tris[idx].tidx[0] = atoi(token); + ASE_GetToken(ase, token, sizeof(token)); + tris[idx].tidx[1] = atoi(token); + ASE_GetToken(ase, token, sizeof(token)); + tris[idx].tidx[2] = atoi(token); + } + } + else + { + Con_Printf("Unknown MESH_FACE_LIST identifier: %s\n", token); + ASE_SkipBlock(ase); + } + ASE_SkipLine(ase); + } + } + else if (!strcmp(token, "*MESH_NUMTVERTEX")) + { + ASE_GetToken(ase, token, sizeof(token)); + if (!tverts) + { + numtverts = atoi(token); + tverts = malloc(sizeof(*tverts) * numtverts); + memset(tverts, 0, sizeof(*tverts) * numtverts); + } + } + else if (!strcmp(token, "*MESH_TVERTLIST")) + { + ASE_SkipLine(ase); + while (*ase->file) + { + ASE_GetToken(ase, token, sizeof(token)); + if (!strcmp(token, "}")) + break; + else if (!strcmp(token, "*MESH_TVERT")) + { + ASE_GetToken(ase, token, sizeof(token)); + idx = atoi(token); + if (idx >= 0 && idx < numverts) + { + ASE_GetToken(ase, token, sizeof(token)); + tverts[idx].st[0] = atof(token); + ASE_GetToken(ase, token, sizeof(token)); + tverts[idx].st[1] = atof(token); + ASE_GetToken(ase, token, sizeof(token)); + tverts[idx].st[2] = atof(token); + } + } + else + { + Con_Printf("Unknown MESH_TVERTLIST identifier: %s\n", token); + ASE_SkipBlock(ase); + } + ASE_SkipLine(ase); + } + } + else if (!strcmp(token, "*MESH_NUMTVFACES")) + ASE_SkipBlock(ase); //should be equal to MESH_NUMFACES + else if (!strcmp(token, "*MESH_TFACELIST")) + { + ASE_SkipLine(ase); + while (*ase->file) + { + ASE_GetToken(ase, token, sizeof(token)); + if (!strcmp(token, "}")) + break; + else if (!strcmp(token, "*MESH_TFACE")) + { + ASE_GetToken(ase, token, sizeof(token)); + idx = atoi(token); + if (idx >= 0 && idx < numtris) + { + ASE_GetToken(ase, token, sizeof(token)); + tris[idx].tidx[0] = atoi(token); + ASE_GetToken(ase, token, sizeof(token)); + tris[idx].tidx[1] = atoi(token); + ASE_GetToken(ase, token, sizeof(token)); + tris[idx].tidx[2] = atoi(token); + } + } + else + { + Con_Printf("Unknown MESH_TFACELIST identifier: %s\n", token); + ASE_SkipBlock(ase); + } + ASE_SkipLine(ase); + } + } + else + { + Con_Printf("Unknown top-level identifier: %s\n", token); + ASE_SkipBlock(ase); + } + ASE_SkipLine(ase); + } + } + else if (!strcmp(token, "*NODE_NAME") || !strcmp(token, "*NODE_TM") + || !strcmp(token, "*PROP_MOTIONBLUR")|| !strcmp(token, "*PROP_CASTSHADOW")|| !strcmp(token, "*PROP_RECVSHADOW")) + ASE_SkipBlock(ase); + else + { + Con_Printf("Unknown top-level identifier: %s\n", token); + ASE_SkipBlock(ase); + } + ASE_SkipLine(ase); + } + + //merge into a mesh + if (materialidx >= 0 && materialidx < ase->nummaterials) + { + size_t v, tri, idx; + struct asemesh_s *mesh; + struct asevert_s asevert; + mesh = ase->materials[materialidx].meshes; + + //don't let any single mesh exceed 65k verts. stuff bugs out then. + if (!mesh || mesh->numverts + numtris*3 > 0xffff) + { + mesh = malloc(sizeof(*mesh)); + memset(mesh, 0, sizeof(*mesh)); + mesh->next = ase->materials[materialidx].meshes; + ase->materials[materialidx].meshes = mesh; + } + + //make sure there's going to be enough space + if (mesh->numverts + numtris*3 > mesh->maxverts) + { + mesh->maxverts = (mesh->maxverts+numtris*3)*2; + mesh->vert = realloc(mesh->vert, sizeof(*mesh->vert) * mesh->maxverts); + } + if (mesh->numindexes + numtris*3 > mesh->maxindexes) + { + mesh->maxindexes = (mesh->maxindexes+numtris*3)*2; + mesh->index = realloc(mesh->index, sizeof(*mesh->index) * mesh->maxindexes); + } + + //insert each triangle into the mesh + for (tri = 0; tri < numtris; tri++) + { + for (v = 3; v --> 0; ) + { + VectorCopy(verts[tris[tri].vidx[v]].xyz, asevert.xyz); + VectorCopy(verts[tris[tri].vidx[v]].norm, asevert.norm); + Vector2Copy(tverts[tris[tri].tidx[v]].st, asevert.st); + + for (idx = 0; idx < mesh->numverts; idx++) + { + if (!memcmp(&mesh->vert[idx], &asevert, sizeof(asevert))) + break; + } + if (idx == mesh->numverts) + mesh->vert[mesh->numverts++] = asevert; + mesh->index[mesh->numindexes++] = idx; + } + } + } + + free(verts); + free(tverts); + free(tris); +} + +static void ASE_TopLevel(struct aseimport_s *ase) +{ + char token[1024]; + while (*ase->file) + { + ASE_GetToken(ase, token, sizeof(token)); + if (!strcmp(token, "*3DSMAX_ASCIIEXPORT")) + { + ASE_GetToken(ase, token, sizeof(token)); //version + } + else if (!strcmp(token, "*COMMENT") || !strcmp(token, "*SCENE")) + ASE_SkipBlock(ase); + else if (!strcmp(token, "*MATERIAL_LIST")) + ASE_MaterialList(ase); + else if (!strcmp(token, "*GEOMOBJECT")) + ASE_GeomObject(ase); + else + { + Con_Printf("Unknown top-level identifier: %s\n", token); + ASE_SkipBlock(ase); + } + + ASE_SkipLine(ase); + } +} +static qboolean QDECL Mod_LoadASEModel (struct model_s *mod, void *buffer, size_t fsize) +{ + galiaspose_t *pose; + galiasinfo_t *surf; + size_t i, m; + struct aseimport_s ase; + memset(&ase, 0, sizeof(ase)); + ase.file = buffer; + ASE_TopLevel(&ase); + + //we need to generate engine meshes and clean up any dynamic memory + for (m = 0; m < ase.nummaterials; m++) + { + struct asemesh_s *mesh; + while(ase.materials[m].meshes) + { + mesh = ase.materials[m].meshes; + ase.materials[m].meshes = mesh->next; + + surf = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf)); + surf->nextsurf = mod->meshinfo; + mod->meshinfo = surf; + + surf->numverts = mesh->numverts; + surf->numindexes = mesh->numindexes; + + surf->ofs_indexes = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofs_indexes) * mesh->numindexes); + for (i = 0; i < mesh->numindexes; i++) + surf->ofs_indexes[i] = mesh->index[i]; + + surf->numanimations = 1; + surf->ofsanimations = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofsanimations)); + surf->ofsanimations->poseofs = pose = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*pose)); + surf->ofsanimations->loop = true; + surf->ofsanimations->numposes = 1; + surf->ofsanimations->rate = 10; + + pose->ofsverts = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*pose->ofsverts) * mesh->numverts); + pose->ofsnormals = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*pose->ofsnormals) * mesh->numverts); + surf->ofs_st_array = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofs_st_array) * mesh->numverts); + + for (i = 0; i < mesh->numverts; i++) + { + VectorCopy(mesh->vert[i].xyz, pose->ofsverts[i]); + VectorCopy(mesh->vert[i].norm, pose->ofsnormals[i]); + Vector2Copy(mesh->vert[i].st, surf->ofs_st_array[i]); + } + + surf->numskins = 1; + surf->ofsskins = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofsskins)); + surf->ofsskins->numframes = 1; + surf->ofsskins->skinspeed = 0.1; + surf->ofsskins->frame = modfuncs->ZG_Malloc(&mod->memgroup, sizeof(*surf->ofsskins->frame)); + Q_strlcpy(surf->ofsskins->frame->shadername, ase.materials[m].name, sizeof(surf->ofsskins->frame->shadername)); + Q_strlcpy(surf->ofsskins->name, ase.materials[m].name, sizeof(surf->ofsskins->name)); + + free(mesh->vert); + free(mesh->index); + free(mesh); + } + } + free(ase.materials); + + mod->type = mod_alias; + return !!mod->meshinfo; +} +#endif + + + +#ifdef LWOMODELS +static char *LWO_ReadString(unsigned char **file) +{ //strings are just null terminated. + //however, they may have an extra byte of padding. yay shorts... + //so skip the null and round up the length. + char *ret = *file; + size_t len = strlen(ret); + *file = *file + ((len + 2)&~1); + return ret; +} + +static qboolean QDECL Mod_LoadLWOModel (struct model_s *mod, void *buffer, size_t fsize) +{ + unsigned char *file = buffer, *fileend; + char *tags; + vec3_t *points; + size_t numpoints; + unsigned int tagssize; + size_t subsize; + subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24); + if (strncmp(file, "FORM", 4) || subsize+8 != fsize) + return false; //not an lwo, or corrupt, or something + file += 8; + fileend = file + subsize; + if (strncmp(file, "LWO2", 4)) + return false; + file += 4; + while (file < fileend) + { + if (!strncmp(file, "TAGS", 4)) + { + tagssize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24); + tags = file+8; + file += 8+tagssize; + } + else if (!strncmp(file, "LAYR", 4)) + { + subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24); + //fixme: + file += 8+subsize; + } + else if (!strncmp(file, "PNTS", 4)) + { + subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24); + numpoints = subsize / sizeof(vec3_t); + points = (vec3_t*)(file+8); + file += 8+subsize; + } + else if (!strncmp(file, "BBOX", 4)) + { + subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24); + //we don't really care. + file += 8+subsize; + } + else if (!strncmp(file, "VMAP", 4)) + { + subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24); + if (!strcmp(file, "TXUV")) + { + } +// else if (!strcmp(file, "RGBA") || !strcmp(file, "RGB")) +// { +// } + file += 8+subsize; + } + else if (!strncmp(file, "POLS", 4)) + { + subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24); + file += 8; + if (!strcmp(file, "FACE") || !strcmp(file, "PTCH")) + { + + } + file += subsize; + } + else if (!strncmp(file, "PTAG", 4)) + { + subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24); + //fixme: + file += 8+subsize; + } + else if (!strncmp(file, "VMAD", 4)) + { + subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24); + //fixme: + file += 8+subsize; + } + else if (!strncmp(file, "SURF", 4)) + { + char *surfend; + char *surfname, *sourcename; + subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24); + file += 8; + surfend = file + subsize; + surfname = LWO_ReadString(&file); + sourcename = LWO_ReadString(&file); + Con_Printf("surf=%s source=%s\n", surfname, sourcename); + while (file < surfend) + { + if (!strncmp(file, "COLR", 4)) + { + subsize = file[5] | (file[4]<<8); + //fixme + file += 6+subsize; + } + else if (!strncmp(file, "DIFF", 4)) + { + subsize = file[5] | (file[4]<<8); + //fixme + file += 6+subsize; + } + else if (!strncmp(file, "SMAN", 4)) + { + subsize = file[5] | (file[4]<<8); + //fixme + file += 6+subsize; + } + else if (!strncmp(file, "BLOK", 4)) + { + char *blokend; + subsize = file[5] | (file[4]<<8); + file += 6; + blokend = file + subsize; + while (file < blokend) + { + if (!strncmp(file, "IMAP", 4)) + ; + else if (!strncmp(file, "TMAP", 4)) + ; + else if (!strncmp(file, "PROJ", 4)) + ; + else if (!strncmp(file, "AXIS", 4)) + ; + else if (!strncmp(file, "IMAG", 4)) + ; + else if (!strncmp(file, "WRAP", 4)) + ; + else if (!strncmp(file, "WRPW", 4)) + ; + else if (!strncmp(file, "WRPH", 4)) + ; + else if (!strncmp(file, "VMAP", 4)) + ; + else if (!strncmp(file, "AAST", 4)) + ; + else if (!strncmp(file, "PIXB", 4)) + ; + else + Con_Printf("Unknown BLOK ID: %c%c%c%c\n", file[0], file[1], file[2], file[3]); + subsize = file[5] | (file[4]<<8); + file += 6+subsize; + } + file = blokend; + } + else + { + Con_Printf("Unknown SURF ID: %c%c%c%c\n", file[0], file[1], file[2], file[3]); + subsize = file[5] | (file[4]<<8); + file += 6+subsize; + } + } + file = surfend; + } + else + { + Con_Printf("Unknown ID: %c%c%c%c\n", file[0], file[1], file[2], file[3]); + subsize = file[7] | (file[6]<<8) | (file[5]<<16) | (file[4]<<24); + file += 8+subsize; + continue; + } + } + + return false; +} +#endif + +qboolean QDECL Mod_LoadGLTFModel (struct model_s *mod, void *buffer, size_t fsize); +qboolean QDECL Mod_LoadGLBModel (struct model_s *mod, void *buffer, size_t fsize); + +qintptr_t Plug_Init(qintptr_t *args) +{ + CHECKBUILTIN(Mod_GetPluginModelFuncs); + + if (BUILTINISVALID(Mod_GetPluginModelFuncs)) + { + modfuncs = pMod_GetPluginModelFuncs(sizeof(modplugfuncs_t)); + if (modfuncs && modfuncs->version < MODPLUGFUNCS_VERSION) + modfuncs = NULL; + } + + if (modfuncs) + { +#ifdef ASEMODELS + modfuncs->RegisterModelFormatText("ASE models (ase)", "*3DSMAX_ASCIIEXPORT", Mod_LoadASEModel); +#endif +#ifdef LWOMODELS + modfuncs->RegisterModelFormatMagic("LWO models (lwo)", (('M'<<24)+('R'<<16)+('O'<<8)+'F'), Mod_LoadLWOModel); +#endif +#ifdef GLTFMODELS + modfuncs->RegisterModelFormatText("glTF2 models (glTF)", ".gltf", Mod_LoadGLTFModel); + modfuncs->RegisterModelFormatMagic("glTF2 models (glb)", (('F'<<24)+('T'<<16)+('l'<<8)+'g'), Mod_LoadGLBModel); +#endif + return true; + } + return false; +} + diff --git a/plugins/plugin.h b/plugins/plugin.h index f16aaaf9f..79bcf7aff 100644 --- a/plugins/plugin.h +++ b/plugins/plugin.h @@ -144,12 +144,21 @@ extern "C" { #define QDECL #endif #endif + +#ifndef LIKEPRINTF + #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) + #define LIKEPRINTF(x) __attribute__((format(printf,x,x+1))) + #else + #define LIKEPRINTF(x) + #endif +#endif + extern qintptr_t (QDECL *plugin_syscall)( qintptr_t arg, ... ); void Q_strlncpy(char *d, const char *s, int sizeofd, int lenofs); void Q_strlcpy(char *d, const char *s, int n); void Q_strlcat(char *d, const char *s, int n); -int Q_snprintf(char *buffer, size_t maxlen, const char *format, ...); +int Q_snprintf(char *buffer, size_t maxlen, const char *format, ...) LIKEPRINTF(3); int Q_vsnprintf(char *buffer, size_t maxlen, const char *format, va_list vargs); #endif @@ -366,7 +375,8 @@ EBUILTIN(int, Net_Send, (qhandle_t socket, void *buffer, int len)); EBUILTIN(void, Net_Close, (qhandle_t socket)); EBUILTIN(int, Net_SetTLSClient, (qhandle_t sock, const char *certhostname)); EBUILTIN(int, Net_GetTLSBinding, (qhandle_t sock, char *outdata, int *datalen)); -#define N_WOULDBLOCK 0 +#define N_WOULDBLOCK -1 +#define N_FATALERROR -2 #define NET_CLIENTPORT -1 #define NET_SERVERPORT -2 diff --git a/plugins/xsv/m_x.c b/plugins/xsv/m_x.c index 460137da0..6f2828a6b 100644 --- a/plugins/xsv/m_x.c +++ b/plugins/xsv/m_x.c @@ -6,14 +6,16 @@ //wmaker -display :1 (or xterm or whatever) #include "../plugin.h" +#include "qux.h" #include "../engine.h" -#include "qux.h" +#undef MULTITHREAD -int mousecursor_x, mousecursor_y; +float mousecursor_x, mousecursor_y; +static const int baseport = 6000; static xclient_t *xclients; -static qhandle_t xlistensocket; +static qhandle_t xlistensocket = -1; xwindow_t *xfocusedwindow; @@ -27,6 +29,7 @@ extern xwindow_t *xpgrabbedwindow; int ctrldown, altdown; +#ifndef K_CTRL int K_BACKSPACE; int K_CTRL; int K_ALT; @@ -35,9 +38,176 @@ int K_MOUSE2; int K_MOUSE3; int K_MOUSE4; int K_MOUSE5; +#endif + +int QKeyToScan(int qkey) +{ //X11 uses some variation of hardware scancodes. + //custom keymaps tend to ignore the server and use some other table instead. + //so we need to try to match what most servers expect + switch(qkey) + { +// case K_: return 1; +// case K_: return 2; +// case K_: return 3; +// case K_: return 4; +// case K_: return 5; +// case K_: return 6; +// case K_: return 7; +// case K_: return 8; +// case K_: return 9; + case '1': return 10; + case '2': return 11; + case '3': return 12; + case '4': return 13; + case '5': return 14; + case '6': return 15; + case '7': return 16; + case '8': return 17; + case '9': return 18; + case '-': return 19; + case '+': return 20; + case '=': return 21; + case K_BACKSPACE: return 22; + + case K_TAB: return 23; + case 'q': return 24; + case 'w': return 25; + case 'e': return 26; + case 'r': return 27; + case 't': return 28; + case 'y': return 29; + case 'u': return 30; + case 'i': return 31; + case 'o': return 32; + case 'p': return 33; + case '[': return 34; + case ']': return 35; + case K_ENTER: return 36; + + case K_LCTRL: return 37; + case 'a': return 38; + case 's': return 39; + case 'd': return 40; + case 'f': return 41; + case 'g': return 42; + case 'h': return 43; + case 'j': return 44; + case 'k': return 45; + case 'l': return 46; + case ';': return 47; + case '\'': return 48; + case '`': return 49; + + case K_LSHIFT: return 50; + case '#': return 51; + case 'z': return 52; + case 'x': return 53; + case 'c': return 54; + case 'v': return 55; + case 'b': return 56; + case 'n': return 57; + case 'm': return 58; + case ',': return 59; + case '.': return 60; + case '/': return 61; + case K_RSHIFT: return 62; + + case K_KP_STAR: return 63; + case K_LALT: return 64; + case K_SPACE: return 65; + case K_CAPSLOCK: return 66; + case K_F1: return 67; + case K_F2: return 68; + case K_F3: return 69; + case K_F4: return 70; + case K_F5: return 71; + case K_F6: return 72; + case K_F7: return 73; + case K_F8: return 74; + case K_F9: return 75; + case K_F10: return 76; + + case K_KP_NUMLOCK: return 77; + case K_SCRLCK: return 78; + case K_KP_HOME: return 79; + case K_KP_UPARROW: return 80; + case K_KP_PGUP: return 81; + case K_KP_MINUS: return 82; + case K_KP_LEFTARROW:return 83; + case K_KP_5: return 84; + case K_KP_RIGHTARROW:return 85; + case K_KP_PLUS: return 86; + case K_KP_END: return 87; + case K_KP_DOWNARROW:return 88; + case K_KP_PGDN: return 89; + case K_KP_INS: return 90; + case K_KP_DEL: return 91; + +// case K_L3SHIFT: return 92; +// case K_: return 93; + case '\\': return 94; + case K_F11: return 95; + case K_F12: return 96; +// case K_: return 97; +// case K_KATAKANA: return 98; +// case K_HIRAGANA: return 99; +// case K_HENKAN_MODE: return 100; +// case K_HIRAGANA_KATAKANA:return 101; +// case K_MUHENKAN: return 102; +// case K_: return 103; + case K_KP_ENTER: return 104; + case K_RCTRL: return 105; + case K_KP_SLASH: return 106; + case K_PRINTSCREEN: return 107; +// case K_L3SHIFT: return 108; +// case K_LINEFEED: return 109; + case K_HOME: return 110; + case K_UPARROW: return 111; + case K_PGUP: return 112; + case K_LEFTARROW: return 113; + case K_RIGHTARROW: return 114; + case K_END: return 115; + case K_DOWNARROW: return 116; + case K_PGDN: return 117; + case K_INS: return 118; + case K_DEL: return 119; + +// case K_: return 120; + case K_MM_VOLUME_MUTE:return 121; + case K_VOLDOWN: return 122; + case K_VOLUP: return 123; + case K_POWER: return 124; + case K_KP_EQUALS: return 125; +// case K_PLUSMINUS: return 126; + case K_PAUSE: return 127; +// case K_LAUNCHA: return 128; +// case K_KP_DECIMAL: return 129; +// case K_HANGUL: return 130; +// case K_HANGUL_HANJA:return 131; +// case K_: return 132; + case K_LWIN: return 133; + case K_RWIN: return 134; + case K_APP: return 135; +// case K_CANCEL: return 136; +// case K_REDO: return 137; +// case K_SUNPROPS: return 138; +// case K_UNDO: return 139; +// case K_SUNFRONT: return 140; +// case K_COPY: return 141; +// case K_OPEN: return 142; +// case K_PASTE: return 143; +// case K_FIND: return 144; +// case K_CUT: return 145; +// case K_HELP: return 146; +// case K_MENUKB: return 147; +// case K_CALCULATOR: return 148; + default: return 0; + } +} + void X_SendData(xclient_t *cl, void *data, int datalen) { #ifdef MULTITHREADWIN32 @@ -133,7 +303,7 @@ int X_SendNotificationMasked(xEvent *data, xwindow_t *window, unsigned int mask) continue; } window = child->parent; - + if (window) // for (window = child; window; window = window->parent) { for (nm = window->notificationmask; nm; nm = nm->next) @@ -516,7 +686,6 @@ nextmessage: if (inlen >= sizeof(xConnClientPrefix)) { xConnClientPrefix *prefix = (xConnClientPrefix *)input; - input += sizeof(xConnClientPrefix); cl->stillinitialising = false; if (prefix->byteOrder != 'l') //egad no. horrible. { @@ -539,7 +708,7 @@ nextmessage: #endif return true; } - if (prefix->nbytesAuthProto != 0) //we can't handle this + /*if (prefix->nbytesAuthProto != 0) //we can't handle this { #ifdef MULTITHREADWIN32 LeaveCriticalSection(&cl->delecatesection); @@ -552,9 +721,14 @@ nextmessage: LeaveCriticalSection(&cl->delecatesection); #endif return true; + }*/ + + if (inlen >= sizeof(*prefix) + ((prefix->nbytesAuthProto+3)&~3) + ((prefix->nbytesAuthString+3)&~3)) + { + input += sizeof(*prefix) + ((prefix->nbytesAuthProto+3)&~3) + ((prefix->nbytesAuthString+3)&~3); + X_SendIntialResponse(cl); + goto nextmessage; } - X_SendIntialResponse(cl); - goto nextmessage; } } else if (inlen >= sizeof(xReq)) @@ -763,15 +937,11 @@ void XWindows_TendToClients(void) { xclient_t *cl, *prev=NULL; qhandle_t newclient; -#ifndef MULTITHREADWIN32 - unsigned int _true = 1; - unsigned int _false = 0; -#endif - if (xlistensocket) + if (xlistensocket >= 0) { newclient = pNet_Accept(xlistensocket, NULL, 0); - if ((int)newclient != -1) + if (newclient >= 0) { cl = malloc(sizeof(xclient_t)); memset(cl, 0, sizeof(xclient_t)); @@ -829,27 +999,66 @@ void XWindows_TendToClients(void) } } -void XWindows_Startup(void) //initialise the server socket and do any initial setup as required. +#ifdef UNIXSOCKETS +#include +#include +#include +int XWindows_UnixListen(int x11display) { - char buffer[64]; + char lockfile[256]; + char socketfile[256]; + int lock_fd, ret; - int port = 6000; + Q_snprintf(lockfile, sizeof(lockfile), "/tmp/.X%i-lock", x11display); + Q_snprintf(socketfile, sizeof(socketfile), "/tmp/.X11-unix/X%i", x11display); - pCmd_Argv(1, buffer, sizeof(buffer)); - port += atoi(buffer); + lock_fd = open(lockfile, O_RDONLY | O_CREAT, 0600); + if (lock_fd == -1) + return -1; //can't do it, jim - if (!xlistensocket) + // try to acquire lock + ret = flock(lock_fd, LOCK_EX | LOCK_NB); + if (ret != 0) { - xlistensocket = pNet_TCPListen(NULL, port, 3); + close(lock_fd); + return -1; + } + + // remove socket file + unlink(socketfile); + + return pNet_TCPListen(va("unix://%s", socketfile), baseport+x11display, 3); +} +#endif + +void XWindows_Startup(int x11display) //initialise the server socket and do any initial setup as required. +{ + if (xlistensocket < 0) + { +#ifdef UNIXSOCKETS + if (x11display < 0) + { + while(xlistensocket < 0) + xlistensocket = XWindows_UnixListen(++x11display); + } + else + xlistensocket = XWindows_UnixListen(x11display); +#else + if (x11display < 0) + x11display = 0; + xlistensocket = pNet_TCPListen(NULL, baseport+x11display, 3); +#endif if (xlistensocket < 0) { - xlistensocket = 0; + xlistensocket = -1; Con_Printf("Failed to create tcp listen socket\n"); return; } X_InitRequests(); XS_CreateInitialResources(); + + system(va("DISPLAY=:%i /usr/bin/x-terminal-emulator &", x11display)); } XS_CheckResourceSentinals(); @@ -1055,7 +1264,6 @@ void X_MoveCursorWindow(xwindow_t *ew, int mx, int my, int movemode) xwindow_t *nw = ew; xwindow_t *oc[MAX_WINDOW_CHAIN]; xwindow_t *nc[MAX_WINDOW_CHAIN]; - unsigned int curtime = pSys_Milliseconds(); if (!nw) @@ -1282,14 +1490,13 @@ void X_EvalutateCursorOwner(int movemode) { xEvent ev; xwindow_t *cursorowner, *wnd, *use; - int mx, my; + float mx, my; int wcx; int wcy; extern xwindow_t *xpconfinewindow; { - extern int mousecursor_x, mousecursor_y; mx = mousecursor_x; my = mousecursor_y; } @@ -1762,7 +1969,9 @@ void XWindows_KeyDown(int key) else { ev.u.u.type = KeyPress; - ev.u.u.detail = key; + ev.u.u.detail = QKeyToScan(key); + if (!ev.u.u.detail) + return; //urm, never mind ev.u.keyButtonPointer.state = 0; ev.u.keyButtonPointer.child = x_windowwithfocus; } @@ -1861,7 +2070,9 @@ void XWindows_Keyup(int key) else { ev.u.u.type = KeyRelease; - ev.u.u.detail = key; + ev.u.u.detail = QKeyToScan(key); + if (!ev.u.u.detail) + return; //urm, never mind ev.u.keyButtonPointer.child = x_windowwithfocus; } ev.u.u.sequenceNumber = 0; @@ -1899,7 +2110,7 @@ void XWindows_Keyup(int key) XS_CheckResourceSentinals(); } -int Plug_MenuEvent(int *args) +/*static int X11_MenuEvent(int *args) { mousecursor_x = args[2]; mousecursor_y = args[3]; @@ -1919,21 +2130,22 @@ int Plug_MenuEvent(int *args) } return 0; -} +}*/ -qintptr_t Plug_ExecuteCommand(qintptr_t *args) +static qintptr_t X11_ExecuteCommand(qintptr_t *args) { char cmd[256]; pCmd_Argv(0, cmd, sizeof(cmd)); if (!strcmp("startx", cmd)) { - XWindows_Startup(); + pCmd_Argv(1, cmd, sizeof(cmd)); + XWindows_Startup(*cmd?atoi(cmd):-1); return 1; } return 0; } -qintptr_t Plug_Tick(qintptr_t *args) +static qintptr_t X11_Tick(qintptr_t *args) { XWindows_TendToClients(); return 0; @@ -1943,7 +2155,7 @@ static void *XWindows_Create(const char *medianame) //initialise the server sock { if (!strcmp(medianame, "x11")) { - XWindows_Startup(); + XWindows_Startup(-1); return xscreen; } return NULL; @@ -1963,13 +2175,13 @@ static qboolean VARGS XWindows_DisplayFrame(void *ctx, qboolean nosound, qboolea static void XWindows_Shutdown(void *ctx) { pNet_Close(xlistensocket); - xlistensocket = 0; + xlistensocket = -1; } static qboolean XWindows_SetSize (void *ctx, int width, int height) { qbyte *ns; - if (width < 64 || height < 64 || width > 2048 || height > 2048) + if (width < 64 || height < 64 || width > 16384 || height > 16384) return false; ns = realloc(xscreen, width*4*height); @@ -1980,8 +2192,11 @@ static qboolean XWindows_SetSize (void *ctx, int width, int height) xscreenheight = height; xscreenmodified = true; - //FIXME: resize root window + send notify - + if (rootwindow) + { + X_Resize(rootwindow, 0, 0, width, height); + XW_ExposeWindow(rootwindow, 0, 0, rootwindow->width, rootwindow->height); + } return true; } return false; @@ -2026,9 +2241,9 @@ media_decoder_funcs_t decoderfuncs = qintptr_t Plug_Init(qintptr_t *args) { - if (!Plug_Export("ExecuteCommand", Plug_ExecuteCommand) || -// !Plug_Export("MenuEvent", Plug_MenuEvent) || - !Plug_Export("Tick", Plug_Tick)) + if (!Plug_Export("ExecuteCommand", X11_ExecuteCommand) || +// !Plug_Export("MenuEvent", X11_MenuEvent) || + !Plug_Export("Tick", X11_Tick)) { Con_Printf("XServer plugin failed\n"); return false; @@ -2044,7 +2259,7 @@ qintptr_t Plug_Init(qintptr_t *args) pCmd_AddCommand("startx"); - +#ifndef K_CTRL K_CTRL = pKey_GetKeyCode("ctrl"); K_ALT = pKey_GetKeyCode("alt"); K_MOUSE1 = pKey_GetKeyCode("mouse1"); @@ -2065,5 +2280,6 @@ qintptr_t Plug_Init(qintptr_t *args) K_LEFTARROW = Key_GetKeyCode("leftarrow"); K_RIGHTARROW = Key_GetKeyCode("rightarrow"); */ +#endif return true; } \ No newline at end of file diff --git a/plugins/xsv/qux.h b/plugins/xsv/qux.h index 560845693..18dcd473e 100644 --- a/plugins/xsv/qux.h +++ b/plugins/xsv/qux.h @@ -153,6 +153,7 @@ extern xwindow_t *rootwindow; extern qboolean xrefreshed; //something onscreen changed. int XS_GetResource(int id, void **data); +void *XS_GetResourceType(int id, int requiredtype); void XS_SetProperty(xwindow_t *wnd, Atom atomid, Atom atomtype, char *data, int datalen, int format); int XS_GetProperty(xwindow_t *wnd, Atom atomid, Atom *type, char *output, int maxlen, int offset, int *extrabytes, int *format); void XS_DeleteProperty(xwindow_t *wnd, Atom atomid); @@ -161,8 +162,10 @@ Atom XS_FindAtom(char *name); xgcontext_t *XS_CreateGContext(int id, xclient_t *owner, xresource_t *drawable); int XS_NewResource(void); xwindow_t *XS_CreateWindow(int wid, xclient_t *owner, xwindow_t *parent, short x, short y, short width, short height); +void X_Resize(xwindow_t *wnd, int newx, int newy, int neww, int newh); void XS_SetParent(xwindow_t *wnd, xwindow_t *parent); xpixmap_t *XS_CreatePixmap(int id, xclient_t *owner, int width, int height, int depth); +xfont_t *XS_CreateFont(int id, xclient_t *owner, char *fontname); void XS_CreateInitialResources(void); void XS_DestroyResource(xresource_t *res); void XS_DestroyResourcesOfClient(xclient_t *cl); @@ -192,6 +195,7 @@ extern qbyte *xscreen; extern short xscreenwidth; extern short xscreenheight; +#ifndef K_CTRL extern int K_BACKSPACE; extern int K_CTRL; extern int K_ALT; @@ -200,3 +204,4 @@ extern int K_MOUSE2; extern int K_MOUSE3; extern int K_MOUSE4; extern int K_MOUSE5; +#endif diff --git a/plugins/xsv/x_reqs.c b/plugins/xsv/x_reqs.c index 4768d912f..2da25f602 100644 --- a/plugins/xsv/x_reqs.c +++ b/plugins/xsv/x_reqs.c @@ -337,6 +337,7 @@ void XR_GetProperty (xclient_t *cl, xReq *request) int format; int trailing; xGetPropertyReply *rep = (xGetPropertyReply*)buffer; + Atom proptype; if (XS_GetResource(req->window, (void**)&wnd) != x_window) { //wait a minute, That's not a window!!! @@ -351,10 +352,12 @@ void XR_GetProperty (xclient_t *cl, xReq *request) if (req->longLength > sizeof(buffer) - sizeof(req)/4) req->longLength = sizeof(buffer) - sizeof(req)/4; - datalen = XS_GetProperty(wnd, req->property, &rep->propertyType, (char *)(rep+1), req->longLength*4, req->longOffset*4, &trailing, &format); + + datalen = XS_GetProperty(wnd, req->property, &proptype, (char *)(rep+1), req->longLength*4, req->longOffset*4, &trailing, &format); rep->type = X_Reply; rep->format = format; + rep->propertyType = proptype; rep->sequenceNumber = cl->requestnum; rep->length = (datalen+3)/4; //rep->propertyType = None; @@ -593,7 +596,38 @@ void XR_SetSelectionOwner (xclient_t *cl, xReq *request) } } +void XR_ConvertSelection (xclient_t *cl, xReq *request) +{ + xConvertSelectionReq *req = (void*)request; + xatom_t *atom = XS_GetResourceType(req->selection, x_atom); + xEvent rep; + if (atom && atom->selectionownerwindowid) + { //forward the request to the selection's owner + rep.u.u.type = SelectionRequest; + rep.u.u.detail = 0; + rep.u.u.sequenceNumber = 0; + rep.u.selectionRequest.time = req->time; + rep.u.selectionRequest.owner = atom->selectionownerwindowid; + rep.u.selectionRequest.target = req->target; + rep.u.selectionRequest.property = req->property; + rep.u.selectionRequest.requestor = req->requestor; + rep.u.selectionRequest.selection = req->selection; + X_SendData(atom->selectionownerclient, &rep, sizeof(rep)); + } + else + { //return it back to the sender, or so + rep.u.u.type = SelectionNotify; + rep.u.u.detail = 0; + rep.u.u.sequenceNumber = 0; + rep.u.selectionNotify.time = req->time; + rep.u.selectionNotify.target = req->target; + rep.u.selectionNotify.property = req->property; + rep.u.selectionNotify.requestor = req->requestor; + rep.u.selectionNotify.selection = req->selection; + X_SendData(cl, &rep, sizeof(rep)); + } +} extern int x_windowwithcursor; @@ -1063,7 +1097,7 @@ void XR_ChangeWindowAttributes (xclient_t *cl, xReq *request) void XR_ConfigureWindow (xclient_t *cl, xReq *request) { - int newx, newy, neww, newh, sibid, newbw; + int newx, newy, neww, newh, sibid, newbw, stackmode; xConfigureWindowReq *req = (xConfigureWindowReq *)request; xwindow_t *wnd; @@ -1112,22 +1146,22 @@ void XR_ConfigureWindow (xclient_t *cl, xReq *request) if (req->mask & CWSibling) sibid = *parm++; else - sibid = 0; + sibid = None; if (req->mask & CWStackMode) - *parm++; + stackmode = *parm++; + else + stackmode = Above; if (!wnd->overrideredirect && X_NotifcationMaskPresent(wnd, SubstructureRedirectMask, cl)) { xEvent ev; - - ev.u.u.type = ConfigureRequest; - ev.u.u.detail = 0; + ev.u.u.detail = stackmode; ev.u.u.sequenceNumber = 0; ev.u.configureRequest.parent = wnd->parent->res.id; ev.u.configureRequest.window = wnd->res.id; - ev.u.configureRequest.sibling = wnd->sibling?wnd->sibling->res.id:None; + ev.u.configureRequest.sibling = sibid; ev.u.configureRequest.x = newx; ev.u.configureRequest.y = newy; ev.u.configureRequest.width = neww; @@ -1139,55 +1173,7 @@ void XR_ConfigureWindow (xclient_t *cl, xReq *request) X_SendNotificationMasked(&ev, wnd, SubstructureRedirectMask); } else - { - xEvent ev; - - /* if (wnd->xpos == newx && wnd->ypos == newy) - { - ev.u.u.type = ResizeRequest; - ev.u.u.detail = 0; - ev.u.u.sequenceNumber = 0; - ev.u.resizeRequest.window = wnd->res.id; - ev.u.resizeRequest.width = wnd->width; - ev.u.resizeRequest.height = wnd->height; - - X_SendNotificationMasked(&ev, wnd, StructureNotifyMask); - X_SendNotificationMasked(&ev, wnd, SubstructureNotifyMask); - - return; - }*/ - - wnd->xpos = newx; - wnd->ypos = newy; - - if ((wnd->width != neww || wnd->height != newh) && wnd->buffer) - { - free(wnd->buffer); - wnd->buffer = NULL; - } - wnd->width = neww; - wnd->height = newh; - - if (wnd->mapped) - xrefreshed = true; - - ev.u.u.type = ConfigureNotify; - ev.u.u.detail = 0; - ev.u.u.sequenceNumber = 0; - ev.u.configureNotify.event = wnd->res.id; - ev.u.configureNotify.window = wnd->res.id; - ev.u.configureNotify.aboveSibling = None; - ev.u.configureNotify.x = wnd->xpos; - ev.u.configureNotify.y = wnd->ypos; - ev.u.configureNotify.width = wnd->width; - ev.u.configureNotify.height = wnd->height; - ev.u.configureNotify.borderWidth = 0; - ev.u.configureNotify.override = wnd->overrideredirect; - ev.u.configureNotify.bpad = 0; - - X_SendNotificationMasked(&ev, wnd, StructureNotifyMask); - X_SendNotificationMasked(&ev, wnd, SubstructureNotifyMask); - } + X_Resize(wnd, newx, newy, neww, newh); } void XR_ReparentWindow (xclient_t *cl, xReq *request) @@ -1220,7 +1206,7 @@ void XR_ReparentWindow (xclient_t *cl, xReq *request) ev.u.u.detail = 0; ev.u.reparent.override = wnd->overrideredirect; ev.u.reparent.window = wnd->res.id; - ev.u.reparent.parent = wnd->res.id; + ev.u.reparent.parent = wnd->parent->res.id; ev.u.reparent.x = req->x; ev.u.reparent.y = req->y; @@ -1344,17 +1330,280 @@ void XR_GetWindowAttributes (xclient_t *cl, xReq *request) X_SendData(cl, &rep, sizeof(xGetWindowAttributesReply)); } + +static struct +{ + KeySym keysym[8]; +} keyboardmapping[256] = +{ + {{0}}, + {{0}}, + {{0}}, + {{0}}, + {{0}}, + {{0}}, + {{0}}, + {{0}}, + {{0}}, + {{XK_Escape, NoSymbol, XK_Escape}}, //10 + {{XK_1, XK_exclam, XK_1, XK_exclam, XK_onesuperior, XK_exclamdown, XK_onesuperior}}, //10 //11 + {{XK_2, XK_quotedbl, XK_2, XK_quotedbl, XK_twosuperior}},//, XK_oneeighth, XK_twosuperior}}, //12 + {{XK_3, XK_sterling, XK_3, XK_sterling, XK_threesuperior, XK_sterling, XK_threesuperior}}, //13 + {{XK_4, XK_dollar, XK_4, XK_dollar}},//, XK_EuroSign, XK_onequarter, XK_EuroSign}}, //14 + {{XK_5, XK_percent, XK_5, XK_percent, XK_onehalf}},//, XK_threeeighths, XK_onehalf}}, //15 + {{XK_6, XK_asciicircum, XK_6, XK_asciicircum, XK_threequarters}},//, XK_fiveeighths, XK_threequarters}}, //16 + {{XK_7, XK_ampersand, XK_7, XK_ampersand, XK_braceleft}},//, XK_seveneighths, XK_braceleft}}, //17 + {{XK_8, XK_asterisk, XK_8, XK_asterisk, XK_bracketleft}},//, XK_trademark, XK_bracketleft}}, //18 + {{XK_9, XK_parenleft, XK_9, XK_parenleft, XK_bracketright, XK_plusminus, XK_bracketright}}, //19 + {{XK_0, XK_parenright, XK_0, XK_parenright, XK_braceright, XK_degree, XK_braceright}}, //10 + {{XK_minus, XK_underscore, XK_minus, XK_underscore, XK_backslash, XK_questiondown, XK_backslash}}, //20 + {{XK_equal, XK_plus, XK_equal, XK_plus}},//, XK_dead_cedilla, XK_dead_ogonek, XK_dead_cedilla}}, //21 + {{XK_BackSpace, XK_BackSpace, XK_BackSpace, XK_BackSpace}}, //22 + {{XK_Tab}},//, XK_ISO_Left_Tab, XK_Tab, XK_ISO_Left_Tab}}, //23 + {{XK_q, XK_Q, XK_q, XK_Q, XK_at}},//, XK_Greek_OMEGA, XK_at}}, //24 + {{XK_w, XK_W, XK_w, XK_W}},//, XK_lstroke, XK_Lstroke, XK_lstroke}}, //25 + {{XK_e, XK_E, XK_e, XK_E, XK_e, XK_E, XK_e}}, //26 + {{XK_r, XK_R, XK_r, XK_R, XK_paragraph, XK_registered, XK_paragraph}}, //27 + {{XK_t, XK_T, XK_t, XK_T}},//, XK_tslash, XK_Tslash, XK_tslash}}, //28 + {{XK_y, XK_Y, XK_y, XK_Y}},//, XK_leftarrow, XK_yen, XK_leftarrow}}, //29 + {{XK_u, XK_U, XK_u, XK_U}},//, XK_downarrow, XK_uparrow, XK_downarrow}}, //30 + {{XK_i, XK_I, XK_i, XK_I}},//, XK_rightarrow, XK_idotless, XK_rightarrow}}, //31 + {{XK_o, XK_O, XK_o, XK_O, XK_oslash, XK_Oslash, XK_oslash}}, //32 + {{XK_p, XK_P, XK_p, XK_P, XK_thorn, XK_THORN, XK_thorn}}, //33 + {{XK_bracketleft, XK_braceleft, XK_bracketleft, XK_braceleft}},//, XK_dead_diaeresis, XK_dead_abovering, XK_dead_diaeresis}}, //34 + {{XK_bracketright, XK_braceright, XK_bracketright, XK_braceright}},//, XK_dead_tilde, XK_dead_macron, XK_dead_tilde}}, //35 + {{XK_Return, NoSymbol, XK_Return}}, //36 + {{XK_Control_L, NoSymbol, XK_Control_L}}, //37 + {{XK_a, XK_A, XK_a, XK_A, XK_ae, XK_AE, XK_ae}}, //38 + {{XK_s, XK_S, XK_s, XK_S, XK_ssharp, XK_section, XK_ssharp}}, //39 + {{XK_d, XK_D, XK_d, XK_D, XK_eth, XK_ETH, XK_eth}}, //40 + {{XK_f, XK_F, XK_f, XK_F}},//, XK_dstroke, XK_ordfeminine, XK_dstroke}}, //41 + {{XK_g, XK_G, XK_g, XK_G}},//, XK_eng, XK_ENG, XK_eng}}, //42 + {{XK_h, XK_H, XK_h, XK_H}},//, XK_hstroke, XK_Hstroke, XK_hstroke}}, //43 + {{XK_j, XK_J, XK_j, XK_J}},//, XK_dead_hook, XK_dead_horn, XK_dead_hook}}, //44 + {{XK_k, XK_K, XK_k, XK_K}},//, XK_kra, XK_ampersand, XK_kra}}, //45 + {{XK_l, XK_L, XK_l, XK_L}},//, XK_lstroke, XK_Lstroke, XK_lstroke}}, //46 + {{XK_semicolon, XK_colon, XK_semicolon, XK_colon}},//, XK_dead_acute, XK_dead_doubleacute, XK_dead_acute}}, //47 + {{XK_apostrophe, XK_at, XK_apostrophe, XK_at}},//, XK_dead_circumflex, XK_dead_caron, XK_dead_circumflex}}, //48 + {{XK_grave, XK_notsign, XK_grave, XK_notsign, XK_bar, XK_bar, XK_bar}}, //49 + {{XK_Shift_L, NoSymbol, XK_Shift_L}}, //50 + {{XK_numbersign, XK_asciitilde, XK_numbersign, XK_asciitilde}},//, XK_dead_grave, XK_dead_breve, XK_dead_grave}}, //51 + {{XK_z, XK_Z, XK_z, XK_Z, XK_guillemotleft, XK_less, XK_guillemotleft}}, //52 + {{XK_x, XK_X, XK_x, XK_X, XK_guillemotright, XK_greater, XK_guillemotright}}, //53 + {{XK_c, XK_C, XK_c, XK_C, XK_cent, XK_copyright, XK_cent}}, //54 + {{XK_v, XK_V, XK_v, XK_V}},//, XK_leftdoublequotemark, XK_leftsinglequotemark, XK_leftdoublequotemark}}, //55 + {{XK_b, XK_B, XK_b, XK_B}},//, XK_rightdoublequotemark, XK_rightsinglequotemark, XK_rightdoublequotemark}}, //56 + {{XK_n, XK_N, XK_n, XK_N, XK_n, XK_N, XK_n}}, //57 + {{XK_m, XK_M, XK_m, XK_M, XK_mu, XK_masculine, XK_mu}}, //58 + {{XK_comma, XK_less, XK_comma, XK_less}},//, XK_horizconnector, XK_multiply, XK_horizconnector}}, //59 + {{XK_period, XK_greater, XK_period, XK_greater, XK_periodcentered, XK_division, XK_periodcentered}}, //60 + {{XK_slash, XK_question, XK_slash, XK_question}},//, XK_dead_belowdot, XK_dead_abovedot, XK_dead_belowdot}}, //61 + {{XK_Shift_R, NoSymbol, XK_Shift_R}}, //62 + {{XK_KP_Multiply, XK_KP_Multiply, XK_KP_Multiply, XK_KP_Multiply, XK_KP_Multiply, XK_KP_Multiply}},//, XK_XF86ClearGrab}}, //63 + {{XK_Alt_L, XK_Meta_L, XK_Alt_L, XK_Meta_L}}, //64 + {{XK_space, NoSymbol, XK_space}}, //65 + {{XK_Caps_Lock, NoSymbol, XK_Caps_Lock}}, //66 + {{XK_F1, XK_F1, XK_F1, XK_F1, XK_F1, XK_F1}},//, XK_XF86Switch_VT_1}}, //67 + {{XK_F2, XK_F2, XK_F2, XK_F2, XK_F2, XK_F2}},//, XK_XF86Switch_VT_2}}, //68 + {{XK_F3, XK_F3, XK_F3, XK_F3, XK_F3, XK_F3}},//, XK_XF86Switch_VT_3}}, //69 + {{XK_F4, XK_F4, XK_F4, XK_F4, XK_F4, XK_F4}},//, XK_XF86Switch_VT_4}}, //70 + {{XK_F5, XK_F5, XK_F5, XK_F5, XK_F5, XK_F5}},//, XK_XF86Switch_VT_5}}, //71 + {{XK_F6, XK_F6, XK_F6, XK_F6, XK_F6, XK_F6}},//, XK_XF86Switch_VT_6}}, //72 + {{XK_F7, XK_F7, XK_F7, XK_F7, XK_F7, XK_F7}},//, XK_XF86Switch_VT_7}}, //73 + {{XK_F8, XK_F8, XK_F8, XK_F8, XK_F8, XK_F8}},//, XK_XF86Switch_VT_8}}, //74 + {{XK_F9, XK_F9, XK_F9, XK_F9, XK_F9, XK_F9}},//, XK_XF86Switch_VT_9}}, //75 + {{XK_F10, XK_F10, XK_F10, XK_F10, XK_F10, XK_F10}},//, XK_XF86Switch_VT_10}}, //76 + {{XK_Num_Lock, NoSymbol, XK_Num_Lock}}, //77 + {{XK_Scroll_Lock, NoSymbol, XK_Scroll_Lock}}, //78 + {{XK_KP_Home, XK_KP_7, XK_KP_Home, XK_KP_7}}, //79 + {{XK_KP_Up, XK_KP_8, XK_KP_Up, XK_KP_8}}, //80 + {{XK_KP_Prior, XK_KP_9, XK_KP_Prior, XK_KP_9}}, //81 + {{XK_KP_Subtract, XK_KP_Subtract, XK_KP_Subtract, XK_KP_Subtract, XK_KP_Subtract, XK_KP_Subtract}},//, XK_XF86Prev_VMode}}, //82 + {{XK_KP_Left, XK_KP_4, XK_KP_Left, XK_KP_4}}, //83 + {{XK_KP_Begin, XK_KP_5, XK_KP_Begin, XK_KP_5}}, //84 + {{XK_KP_Right, XK_KP_6, XK_KP_Right, XK_KP_6}}, //85 + {{XK_KP_Add, XK_KP_Add, XK_KP_Add, XK_KP_Add, XK_KP_Add, XK_KP_Add}},//, XK_XF86Next_VMode}}, //86 + {{XK_KP_End, XK_KP_1, XK_KP_End, XK_KP_1}}, //87 + {{XK_KP_Down, XK_KP_2, XK_KP_Down, XK_KP_2}}, //88 + {{XK_KP_Next, XK_KP_3, XK_KP_Next, XK_KP_3}}, //89 + {{XK_KP_Insert, XK_KP_0, XK_KP_Insert, XK_KP_0}}, //90 + {{XK_KP_Delete, XK_KP_Decimal, XK_KP_Delete, XK_KP_Decimal}}, //91 + {{0}},//XK_ISO_Level3_Shift, NoSymbol, XK_ISO_Level3_Shift}}, //92 + {{0}}, //93 + {{XK_backslash, XK_bar, XK_backslash, XK_bar, XK_bar, XK_brokenbar, XK_bar}}, //94 + {{XK_F11, XK_F11, XK_F11, XK_F11, XK_F11, XK_F11}},//, XK_XF86Switch_VT_11}}, //95 + {{XK_F12, XK_F12, XK_F12, XK_F12, XK_F12, XK_F12}},//, XK_XF86Switch_VT_12}}, //96 + {{0}}, //97 + {{XK_Katakana, NoSymbol, XK_Katakana}}, //98 + {{XK_Hiragana, NoSymbol, XK_Hiragana}}, //99 + {{XK_Henkan_Mode, NoSymbol, XK_Henkan_Mode}}, //100 + {{XK_Hiragana_Katakana, NoSymbol, XK_Hiragana_Katakana}}, //101 + {{XK_Muhenkan, NoSymbol, XK_Muhenkan}}, //102 + {{0}}, //103 + {{XK_KP_Enter, NoSymbol, XK_KP_Enter}}, //104 + {{XK_Control_R, NoSymbol, XK_Control_R}}, //105 + {{XK_KP_Divide, XK_KP_Divide, XK_KP_Divide, XK_KP_Divide, XK_KP_Divide, XK_KP_Divide}},//, XK_XF86Ungrab}}, //106 + {{XK_Print, XK_Sys_Req, XK_Print, XK_Sys_Req}}, //107 + {{0}},//XK_ISO_Level3_Shift, XK_Multi_key, XK_ISO_Level3_Shift, XK_Multi_key}}, //108 + {{XK_Linefeed, NoSymbol, XK_Linefeed}}, //109 + {{XK_Home, NoSymbol, XK_Home}}, //110 + {{XK_Up, NoSymbol, XK_Up}}, //111 + {{XK_Prior, NoSymbol, XK_Prior}}, //112 + {{XK_Left, NoSymbol, XK_Left}}, //113 + {{XK_Right, NoSymbol, XK_Right}}, //114 + {{XK_End, NoSymbol, XK_End}}, //115 + {{XK_Down, NoSymbol, XK_Down}}, //116 + {{XK_Next, NoSymbol, XK_Next}}, //117 + {{XK_Insert, NoSymbol, XK_Insert}}, //118 + {{XK_Delete, NoSymbol, XK_Delete}}, //119 +/* + 120 + 121 0x1008ff12 (XF86AudioMute) 0x0000 (NoSymbol) 0x1008ff12 (XF86AudioMute) + 122 0x1008ff11 (XF86AudioLowerVolume) 0x0000 (NoSymbol) 0x1008ff11 (XF86AudioLowerVolume) + 123 0x1008ff13 (XF86AudioRaiseVolume) 0x0000 (NoSymbol) 0x1008ff13 (XF86AudioRaiseVolume) + 124 0x1008ff2a (XF86PowerOff) 0x0000 (NoSymbol) 0x1008ff2a (XF86PowerOff) + 125 0xffbd (KP_Equal) 0x0000 (NoSymbol) 0xffbd (KP_Equal) + 126 0x00b1 (plusminus) 0x0000 (NoSymbol) 0x00b1 (plusminus) + 127 0xff13 (Pause) 0xff6b (Break) 0xff13 (Pause) 0xff6b (Break) + 128 0x1008ff4a (XF86LaunchA) 0x0000 (NoSymbol) 0x1008ff4a (XF86LaunchA) + 129 0xffae (KP_Decimal) 0xffae (KP_Decimal) 0xffae (KP_Decimal) 0xffae (KP_Decimal) + 130 0xff31 (Hangul) 0x0000 (NoSymbol) 0xff31 (Hangul) + 131 0xff34 (Hangul_Hanja) 0x0000 (NoSymbol) 0xff34 (Hangul_Hanja) + 132 + 133 0xffeb (Super_L) 0x0000 (NoSymbol) 0xffeb (Super_L) + 134 0xffec (Super_R) 0x0000 (NoSymbol) 0xffec (Super_R) + 135 0xff67 (Menu) 0x0000 (NoSymbol) 0xff67 (Menu) + 136 0xff69 (Cancel) 0x0000 (NoSymbol) 0xff69 (Cancel) + 137 0xff66 (Redo) 0x0000 (NoSymbol) 0xff66 (Redo) + 138 0x1005ff70 (SunProps) 0x0000 (NoSymbol) 0x1005ff70 (SunProps) + 139 0xff65 (Undo) 0x0000 (NoSymbol) 0xff65 (Undo) + 140 0x1005ff71 (SunFront) 0x0000 (NoSymbol) 0x1005ff71 (SunFront) + 141 0x1008ff57 (XF86Copy) 0x0000 (NoSymbol) 0x1008ff57 (XF86Copy) + 142 0x1008ff6b (XF86Open) 0x0000 (NoSymbol) 0x1008ff6b (XF86Open) + 143 0x1008ff6d (XF86Paste) 0x0000 (NoSymbol) 0x1008ff6d (XF86Paste) + 144 0xff68 (Find) 0x0000 (NoSymbol) 0xff68 (Find) + 145 0x1008ff58 (XF86Cut) 0x0000 (NoSymbol) 0x1008ff58 (XF86Cut) + 146 0xff6a (Help) 0x0000 (NoSymbol) 0xff6a (Help) + 147 0x1008ff65 (XF86MenuKB) 0x0000 (NoSymbol) 0x1008ff65 (XF86MenuKB) + 148 0x1008ff1d (XF86Calculator) 0x0000 (NoSymbol) 0x1008ff1d (XF86Calculator) + 149 + 150 0x1008ff2f (XF86Sleep) 0x0000 (NoSymbol) 0x1008ff2f (XF86Sleep) + 151 0x1008ff2b (XF86WakeUp) 0x0000 (NoSymbol) 0x1008ff2b (XF86WakeUp) + 152 0x1008ff5d (XF86Explorer) 0x0000 (NoSymbol) 0x1008ff5d (XF86Explorer) + 153 0x1008ff7b (XF86Send) 0x0000 (NoSymbol) 0x1008ff7b (XF86Send) + 154 + 155 0x1008ff8a (XF86Xfer) 0x0000 (NoSymbol) 0x1008ff8a (XF86Xfer) + 156 0x1008ff41 (XF86Launch1) 0x0000 (NoSymbol) 0x1008ff41 (XF86Launch1) + 157 0x1008ff42 (XF86Launch2) 0x0000 (NoSymbol) 0x1008ff42 (XF86Launch2) + 158 0x1008ff2e (XF86WWW) 0x0000 (NoSymbol) 0x1008ff2e (XF86WWW) + 159 0x1008ff5a (XF86DOS) 0x0000 (NoSymbol) 0x1008ff5a (XF86DOS) + 160 0x1008ff2d (XF86ScreenSaver) 0x0000 (NoSymbol) 0x1008ff2d (XF86ScreenSaver) + 161 0x1008ff74 (XF86RotateWindows) 0x0000 (NoSymbol) 0x1008ff74 (XF86RotateWindows) + 162 0x1008ff7f (XF86TaskPane) 0x0000 (NoSymbol) 0x1008ff7f (XF86TaskPane) + 163 0x1008ff19 (XF86Mail) 0x0000 (NoSymbol) 0x1008ff19 (XF86Mail) + 164 0x1008ff30 (XF86Favorites) 0x0000 (NoSymbol) 0x1008ff30 (XF86Favorites) + 165 0x1008ff33 (XF86MyComputer) 0x0000 (NoSymbol) 0x1008ff33 (XF86MyComputer) + 166 0x1008ff26 (XF86Back) 0x0000 (NoSymbol) 0x1008ff26 (XF86Back) + 167 0x1008ff27 (XF86Forward) 0x0000 (NoSymbol) 0x1008ff27 (XF86Forward) + 168 + 169 0x1008ff2c (XF86Eject) 0x0000 (NoSymbol) 0x1008ff2c (XF86Eject) + 170 0x1008ff2c (XF86Eject) 0x1008ff2c (XF86Eject) 0x1008ff2c (XF86Eject) 0x1008ff2c (XF86Eject) + 171 0x1008ff17 (XF86AudioNext) 0x0000 (NoSymbol) 0x1008ff17 (XF86AudioNext) + 172 0x1008ff14 (XF86AudioPlay) 0x1008ff31 (XF86AudioPause) 0x1008ff14 (XF86AudioPlay) 0x1008ff31 (XF86AudioPause) + 173 0x1008ff16 (XF86AudioPrev) 0x0000 (NoSymbol) 0x1008ff16 (XF86AudioPrev) + 174 0x1008ff15 (XF86AudioStop) 0x1008ff2c (XF86Eject) 0x1008ff15 (XF86AudioStop) 0x1008ff2c (XF86Eject) + 175 0x1008ff1c (XF86AudioRecord) 0x0000 (NoSymbol) 0x1008ff1c (XF86AudioRecord) + 176 0x1008ff3e (XF86AudioRewind) 0x0000 (NoSymbol) 0x1008ff3e (XF86AudioRewind) + 177 0x1008ff6e (XF86Phone) 0x0000 (NoSymbol) 0x1008ff6e (XF86Phone) + 178 + 179 0x1008ff81 (XF86Tools) 0x0000 (NoSymbol) 0x1008ff81 (XF86Tools) + 180 0x1008ff18 (XF86HomePage) 0x0000 (NoSymbol) 0x1008ff18 (XF86HomePage) + 181 0x1008ff73 (XF86Reload) 0x0000 (NoSymbol) 0x1008ff73 (XF86Reload) + 182 0x1008ff56 (XF86Close) 0x0000 (NoSymbol) 0x1008ff56 (XF86Close) + 183 + 184 + 185 0x1008ff78 (XF86ScrollUp) 0x0000 (NoSymbol) 0x1008ff78 (XF86ScrollUp) + 186 0x1008ff79 (XF86ScrollDown) 0x0000 (NoSymbol) 0x1008ff79 (XF86ScrollDown) + 187 0x0028 (parenleft) 0x0000 (NoSymbol) 0x0028 (parenleft) + 188 0x0029 (parenright) 0x0000 (NoSymbol) 0x0029 (parenright) + 189 0x1008ff68 (XF86New) 0x0000 (NoSymbol) 0x1008ff68 (XF86New) + 190 0xff66 (Redo) 0x0000 (NoSymbol) 0xff66 (Redo) + 191 0x1008ff81 (XF86Tools) 0x0000 (NoSymbol) 0x1008ff81 (XF86Tools) + 192 0x1008ff45 (XF86Launch5) 0x0000 (NoSymbol) 0x1008ff45 (XF86Launch5) + 193 0x1008ff46 (XF86Launch6) 0x0000 (NoSymbol) 0x1008ff46 (XF86Launch6) + 194 0x1008ff47 (XF86Launch7) 0x0000 (NoSymbol) 0x1008ff47 (XF86Launch7) + 195 0x1008ff48 (XF86Launch8) 0x0000 (NoSymbol) 0x1008ff48 (XF86Launch8) + 196 0x1008ff49 (XF86Launch9) 0x0000 (NoSymbol) 0x1008ff49 (XF86Launch9) + 197 + 198 0x1008ffb2 (XF86AudioMicMute) 0x0000 (NoSymbol) 0x1008ffb2 (XF86AudioMicMute) + 199 0x1008ffa9 (XF86TouchpadToggle) 0x0000 (NoSymbol) 0x1008ffa9 (XF86TouchpadToggle) + 200 0x1008ffb0 (XF86TouchpadOn) 0x0000 (NoSymbol) 0x1008ffb0 (XF86TouchpadOn) + 201 0x1008ffb1 (XF86TouchpadOff) 0x0000 (NoSymbol) 0x1008ffb1 (XF86TouchpadOff) + 202 + 203 0xff7e (Mode_switch) 0x0000 (NoSymbol) 0xff7e (Mode_switch) + 204 0x0000 (NoSymbol) 0xffe9 (Alt_L) 0x0000 (NoSymbol) 0xffe9 (Alt_L) + 205 0x0000 (NoSymbol) 0xffe7 (Meta_L) 0x0000 (NoSymbol) 0xffe7 (Meta_L) + 206 0x0000 (NoSymbol) 0xffeb (Super_L) 0x0000 (NoSymbol) 0xffeb (Super_L) + 207 0x0000 (NoSymbol) 0xffed (Hyper_L) 0x0000 (NoSymbol) 0xffed (Hyper_L) + 208 0x1008ff14 (XF86AudioPlay) 0x0000 (NoSymbol) 0x1008ff14 (XF86AudioPlay) + 209 0x1008ff31 (XF86AudioPause) 0x0000 (NoSymbol) 0x1008ff31 (XF86AudioPause) + 210 0x1008ff43 (XF86Launch3) 0x0000 (NoSymbol) 0x1008ff43 (XF86Launch3) + 211 0x1008ff44 (XF86Launch4) 0x0000 (NoSymbol) 0x1008ff44 (XF86Launch4) + 212 0x1008ff4b (XF86LaunchB) 0x0000 (NoSymbol) 0x1008ff4b (XF86LaunchB) + 213 0x1008ffa7 (XF86Suspend) 0x0000 (NoSymbol) 0x1008ffa7 (XF86Suspend) + 214 0x1008ff56 (XF86Close) 0x0000 (NoSymbol) 0x1008ff56 (XF86Close) + 215 0x1008ff14 (XF86AudioPlay) 0x0000 (NoSymbol) 0x1008ff14 (XF86AudioPlay) + 216 0x1008ff97 (XF86AudioForward) 0x0000 (NoSymbol) 0x1008ff97 (XF86AudioForward) + 217 + 218 0xff61 (Print) 0x0000 (NoSymbol) 0xff61 (Print) + 219 + 220 0x1008ff8f (XF86WebCam) 0x0000 (NoSymbol) 0x1008ff8f (XF86WebCam) + 221 + 222 + 223 0x1008ff19 (XF86Mail) 0x0000 (NoSymbol) 0x1008ff19 (XF86Mail) + 224 0x1008ff8e (XF86Messenger) 0x0000 (NoSymbol) 0x1008ff8e (XF86Messenger) + 225 0x1008ff1b (XF86Search) 0x0000 (NoSymbol) 0x1008ff1b (XF86Search) + 226 0x1008ff5f (XF86Go) 0x0000 (NoSymbol) 0x1008ff5f (XF86Go) + 227 0x1008ff3c (XF86Finance) 0x0000 (NoSymbol) 0x1008ff3c (XF86Finance) + 228 0x1008ff5e (XF86Game) 0x0000 (NoSymbol) 0x1008ff5e (XF86Game) + 229 0x1008ff36 (XF86Shop) 0x0000 (NoSymbol) 0x1008ff36 (XF86Shop) + 230 + 231 0xff69 (Cancel) 0x0000 (NoSymbol) 0xff69 (Cancel) + 232 0x1008ff03 (XF86MonBrightnessDown) 0x0000 (NoSymbol) 0x1008ff03 (XF86MonBrightnessDown) + 233 0x1008ff02 (XF86MonBrightnessUp) 0x0000 (NoSymbol) 0x1008ff02 (XF86MonBrightnessUp) + 234 0x1008ff32 (XF86AudioMedia) 0x0000 (NoSymbol) 0x1008ff32 (XF86AudioMedia) + 235 0x1008ff59 (XF86Display) 0x0000 (NoSymbol) 0x1008ff59 (XF86Display) + 236 0x1008ff04 (XF86KbdLightOnOff) 0x0000 (NoSymbol) 0x1008ff04 (XF86KbdLightOnOff) + 237 0x1008ff06 (XF86KbdBrightnessDown) 0x0000 (NoSymbol) 0x1008ff06 (XF86KbdBrightnessDown) + 238 0x1008ff05 (XF86KbdBrightnessUp) 0x0000 (NoSymbol) 0x1008ff05 (XF86KbdBrightnessUp) + 239 0x1008ff7b (XF86Send) 0x0000 (NoSymbol) 0x1008ff7b (XF86Send) + 240 0x1008ff72 (XF86Reply) 0x0000 (NoSymbol) 0x1008ff72 (XF86Reply) + 241 0x1008ff90 (XF86MailForward) 0x0000 (NoSymbol) 0x1008ff90 (XF86MailForward) + 242 0x1008ff77 (XF86Save) 0x0000 (NoSymbol) 0x1008ff77 (XF86Save) + 243 0x1008ff5b (XF86Documents) 0x0000 (NoSymbol) 0x1008ff5b (XF86Documents) + 244 0x1008ff93 (XF86Battery) 0x0000 (NoSymbol) 0x1008ff93 (XF86Battery) + 245 0x1008ff94 (XF86Bluetooth) 0x0000 (NoSymbol) 0x1008ff94 (XF86Bluetooth) + 246 0x1008ff95 (XF86WLAN) 0x0000 (NoSymbol) 0x1008ff95 (XF86WLAN) + 247 + 248 + 249 + 250 + 251 + */ +}; + void XR_GetKeyboardMapping (xclient_t *cl, xReq *request) {//fixme: send the XK equivelents. xGetKeyboardMappingReq *req = (xGetKeyboardMappingReq *)request; char buffer[8192]; xGetKeyboardMappingReply *rep = (xGetKeyboardMappingReply *)buffer; - int i; + int i, y, x; + int *syms = ((int *)(rep+1)); rep->type = X_Reply; - rep->keySymsPerKeyCode = 1; + rep->keySymsPerKeyCode = countof(keyboardmapping[0].keysym); rep->sequenceNumber = cl->requestnum; - rep->length = req->count; + rep->length = 0; rep->pad2 = 0; rep->pad3 = 0; rep->pad4 = 0; @@ -1364,71 +1613,54 @@ void XR_GetKeyboardMapping (xclient_t *cl, xReq *request) for (i = 0; i < req->count; i++) { - switch (req->firstKeyCode+i) - { -/* - case ' ': ((int *)(rep+1))[i] = XK_space; break; - - case K_PGUP: ((int *)(rep+1))[i] = XK_Page_Up; break; - case K_PGDN: ((int *)(rep+1))[i] = XK_Page_Down; break; - case K_HOME: ((int *)(rep+1))[i] = XK_Home; break; - case K_END: ((int *)(rep+1))[i] = XK_End; break; - - case K_LEFTARROW: ((int *)(rep+1))[i] = XK_Left; break; - case K_RIGHTARROW: ((int *)(rep+1))[i] = XK_Right; break; - case K_DOWNARROW: ((int *)(rep+1))[i] = XK_Down; break; - case K_UPARROW: ((int *)(rep+1))[i] = XK_Up; break; - - case K_ENTER: ((int *)(rep+1))[i] = XK_Return; break; - case K_TAB: ((int *)(rep+1))[i] = XK_Tab; break; - case K_ESCAPE: ((int *)(rep+1))[i] = XK_Escape; break; - - case K_F1: ((int *)(rep+1))[i] = XK_F1; break; - case K_F2: ((int *)(rep+1))[i] = XK_F2; break; - case K_F3: ((int *)(rep+1))[i] = XK_F3; break; - case K_F4: ((int *)(rep+1))[i] = XK_F4; break; - case K_F5: ((int *)(rep+1))[i] = XK_F5; break; - case K_F6: ((int *)(rep+1))[i] = XK_F6; break; - case K_F7: ((int *)(rep+1))[i] = XK_F7; break; - case K_F8: ((int *)(rep+1))[i] = XK_F8; break; - case K_F9: ((int *)(rep+1))[i] = XK_F9; break; - case K_F10: ((int *)(rep+1))[i] = XK_F10; break; - case K_F11: ((int *)(rep+1))[i] = XK_F11; break; - case K_F12: ((int *)(rep+1))[i] = XK_F12; break; - - case K_BACKSPACE: ((int *)(rep+1))[i] = XK_BackSpace; break; - case K_DEL: ((int *)(rep+1))[i] = XK_Delete; break; - case K_INS: ((int *)(rep+1))[i] = XK_Insert; break; - case K_PAUSE: ((int *)(rep+1))[i] = XK_Pause; break; - case K_SHIFT: ((int *)(rep+1))[i] = XK_Shift_L; break; - case K_CTRL: ((int *)(rep+1))[i] = XK_Control_L; break; - case K_ALT: ((int *)(rep+1))[i] = XK_Alt_L; break; - - - case K_KP_HOME: ((int *)(rep+1))[i] = XK_Home; break; - case K_KP_UPARROW: ((int *)(rep+1))[i] = XK_Up; break; - - - - case K_KP_PGUP: ((int *)(rep+1))[i] = XK_KP_Page_Up; break; - case K_KP_LEFTARROW: ((int *)(rep+1))[i] = XK_KP_Left; break; - case K_KP_5: ((int *)(rep+1))[i] = XK_KP_Space; break; - case K_KP_RIGHTARROW: ((int *)(rep+1))[i] = XK_KP_Right; break; - case K_KP_END: ((int *)(rep+1))[i] = XK_KP_End; break; - case K_KP_DOWNARROW: ((int *)(rep+1))[i] = XK_KP_Down; break; - case K_KP_PGDN: ((int *)(rep+1))[i] = XK_KP_Page_Down; break; - case K_KP_ENTER: ((int *)(rep+1))[i] = XK_KP_Enter; break; - case K_KP_INS: ((int *)(rep+1))[i] = XK_KP_Insert; break; - case K_KP_DEL: ((int *)(rep+1))[i] = XK_KP_Delete; break; - case K_KP_SLASH: ((int *)(rep+1))[i] = XK_KP_Divide; break; - case K_KP_MINUS: ((int *)(rep+1))[i] = XK_KP_Subtract; break; - case K_KP_PLUS: ((int *)(rep+1))[i] = XK_KP_Add; break; - case K_KP_STAR: ((int *)(rep+1))[i] = XK_KP_Multiply; break; - case K_KP_EQUALS: ((int *)(rep+1))[i] = XK_KP_Enter; break; -*/ - default: - ((int *)(rep+1))[i] = req->firstKeyCode+i; + y = req->firstKeyCode+i; + if (y >= countof(keyboardmapping)) break; + for (x = 0; x < rep->keySymsPerKeyCode; x++) + *syms++ = keyboardmapping[y].keysym[x]; + } + rep->length = i*rep->keySymsPerKeyCode; + + X_SendData(cl, rep, sizeof(*rep)+rep->length*4); +} + +static struct +{ + KEYCODE keysym[8]; +} modifiermapping[] = +{ //these are scancodes + {{0x32, 0x32}}, + {{0x42}}, + {{0x24, 0x69}}, + {{0x40, 0xcd}}, + {{0x4d}}, + {{0}}, + {{0x85, 0x86, 0xce, 0xcf}}, + {{0x5c, 0xcb}}, +}; +void XR_GetModifierMapping (xclient_t *cl, xReq *request) +{//fixme: send the XK equivelents. +// xReq *req = (xReq *)request; + char buffer[8192]; + xGetModifierMappingReply *rep = (xGetModifierMappingReply *)buffer; + int x, y; + KEYCODE *syms = ((KEYCODE *)(rep+1)); + + rep->type = X_Reply; + rep->numKeyPerModifier = countof(modifiermapping[0].keysym); + rep->sequenceNumber = cl->requestnum; + rep->length = (8*rep->numKeyPerModifier * sizeof(KEYCODE) + 3)/4; + rep->pad2 = 0; + rep->pad3 = 0; + rep->pad4 = 0; + rep->pad5 = 0; + rep->pad6 = 0; + + for (y = 0; y < countof(modifiermapping); y++) + { + for (x = 0; x < rep->numKeyPerModifier; x++) + { + *syms++ = modifiermapping[y].keysym[x]; } } @@ -1470,13 +1702,14 @@ void XR_QueryPointer (xclient_t *cl, xReq *request) void XR_CreateCursor (xclient_t *cl, xReq *request) { - xCreateCursorReq *req = (xCreateCursorReq *)request; +// xCreateCursorReq *req = (xCreateCursorReq *)request; // X_SendError(cl, BadImplementation, 0, req->reqType, 0); } void XR_CreateGlyphCursor (xclient_t *cl, xReq *request) { - xCreateGlyphCursorReq *req = (xCreateGlyphCursorReq *)request; +// xCreateGlyphCursorReq *req = (xCreateGlyphCursorReq *)request; + // char buffer[8192]; // xGetKeyboardMappingReply *rep = (xGetKeyboardMappingReply *)buffer; @@ -1491,6 +1724,16 @@ void XR_FreeCursor (xclient_t *cl, xReq *request) // X_SendError(cl, BadImplementation, 0, req->reqType, 0); // X_SendError(cl, BadValue, req->id, X_DestroyWindow, 0); } +void XR_RecolorCursor (xclient_t *cl, xReq *request) +{ +} + +void XR_GrabButton (xclient_t *cl, xReq *request) +{ +} +void XR_UngrabButton (xclient_t *cl, xReq *request) +{ +} void XR_ChangeGCInternal(unsigned int mask, xgcontext_t *gc, CARD32 *param) { @@ -1524,8 +1767,10 @@ void XR_ChangeGCInternal(unsigned int mask, xgcontext_t *gc, CARD32 *param) param++; if (mask & GCFont) { - if (XS_GetResource(*param++, &gc->font) != x_font) - gc->font = NULL; + void *font = NULL; + if (XS_GetResource(*param++, &font) != x_font) + font = NULL; + gc->font = font; } if (mask & GCSubwindowMode) param++; @@ -2364,14 +2609,12 @@ void XR_PutImage(xclient_t *cl, xReq *request) } void XR_GetImage(xclient_t *cl, xReq *request) { - unsigned char *out; + unsigned char *out, *data; unsigned char *in; xGetImageReq *req = (xGetImageReq *)request; xresource_t *drawable; - int i; - unsigned int buffer[65535]; - xGetImageReply *rep = (xGetImageReply *)buffer; + xGetImageReply rep; int drwidth; int drheight; @@ -2398,7 +2641,7 @@ void XR_GetImage(xclient_t *cl, xReq *request) drheight = wnd->height; drbuffer = wnd->buffer; - rep->visual = 0x22; + rep.visual = 0x22; } else if (drawable->restype == x_pixmap) { @@ -2414,7 +2657,7 @@ void XR_GetImage(xclient_t *cl, xReq *request) drheight = pm->height; drbuffer = pm->data; - rep->visual = 0; + rep.visual = 0; } else { @@ -2445,18 +2688,22 @@ void XR_GetImage(xclient_t *cl, xReq *request) return; } - out = (qbyte *)(rep+1); + data = out = alloca(req->width*4*req->height); if (req->format == 2) //32 bit network bandwidth (hideous) { while(req->height) { in = drbuffer + (req->x + req->y*drwidth)*4; +#if 1 + memcpy(out, in, req->width*4); +#else for (i = 0; i < req->width; i++) { out[i*4+0] = in[i*4+0]; out[i*4+1] = in[i*4+1]; out[i*4+2] = in[i*4+2]; } +#endif out += req->width*4; req->height--; @@ -2469,17 +2716,18 @@ void XR_GetImage(xclient_t *cl, xReq *request) return; } - rep->type = X_Reply; - rep->sequenceNumber = cl->requestnum; - rep->length = (out-(qbyte *)(rep+1)+3)/4; - rep->depth = 24; - rep->pad3 = 0; - rep->pad4 = 0; - rep->pad5 = 0; - rep->pad6 = 0; - rep->pad7 = 0; + rep.type = X_Reply; + rep.sequenceNumber = cl->requestnum; + rep.length = (out-data+3)/4; + rep.depth = 24; + rep.pad3 = 0; + rep.pad4 = 0; + rep.pad5 = 0; + rep.pad6 = 0; + rep.pad7 = 0; - X_SendData(cl, rep, sizeof(*rep)+rep->length*4); + X_SendData(cl, &rep, sizeof(rep)); + X_SendData(cl, data, rep.length*4); } void XW_PolyLine(unsigned int *dbuffer, int dwidth, int dheight, int x1, int x2, int y1, int y2, xgcontext_t *gc) @@ -2715,6 +2963,7 @@ void XR_FillPoly(xclient_t *cl, xReq *request) } points+=2; +(void)drbuffer, (void)drwidth, (void)drheight; // XW_PolyLine((unsigned int *)drbuffer, drwidth, drheight, start[0], start[1], end[0], end[1], gc); points++; @@ -3026,7 +3275,7 @@ void Draw_CharToDrawable (int num, unsigned int *drawable, int x, int y, int wid while (drawline-->=0) { for (i=s ; i 128 && source[i]) + if (((qbyte*)(&source[i]))[3] > 128 && source[i]) // GCFunc(gc->fgcolour, drawable[i], gc->function, drawable[i], source[i]); drawable[i] = source[i]; source += font->rowwidth; @@ -3157,6 +3406,14 @@ void XR_OpenFont(xclient_t *cl, xReq *request) //basically ignored. We only supp XS_CreateFont(req->fid, cl, name); } +void XR_CloseFont(xclient_t *cl, xReq *request) //basically ignored. We only support one font... +{ + xResourceReq *req = (xResourceReq *)request; + void *font = XS_GetResourceType(req->id, x_font); + if (font) + XS_DestroyResource(font); +} + void XR_ListFonts(xclient_t *cl, xReq *request) //basically ignored. We only support one font... { // xListFontsReq *req = (xListFontsReq *)request; @@ -3178,8 +3435,8 @@ void XR_QueryFont(xclient_t *cl, xReq *request) //basically ignored. We only sup int i; xCharInfo *ci; xQueryFontReply *rep = (xQueryFontReply *)buffer; - xfont_t *font; - if (XS_GetResource(req->id, &font) != x_font) + xfont_t *font = XS_GetResourceType(req->id, x_font); + if (!font) { X_SendError(cl, BadFont, req->id, req->reqType, 0); return; @@ -3259,6 +3516,40 @@ void XR_AllocColor(xclient_t *cl, xReq *request) X_SendData(cl, &rep, sizeof(rep)); } +void XR_QueryColors(xclient_t *cl, xReq *request) +{ + xQueryColorsReq *req = (xQueryColorsReq *)request; + xQueryColorsReply rep; + xrgb rgb[65536]; + int n; + int *pixel = (int*)(req+1); + + rep.type = X_Reply; + rep.pad1 = 0; + rep.sequenceNumber = cl->requestnum; + rep.length = 0; + rep.nColors = 0; + rep.pad2 = 0; + rep.pad3 = 0; + rep.pad4 = 0; + rep.pad5 = 0; + rep.pad6 = 0; + rep.pad7 = 0; + + for (n = 0; n < req->length - sizeof(*req)/4; n++) + { + rgb[n].red = ((pixel[n]>>16)&0xff)<<8; + rgb[n].green = ((pixel[n]>>8)&0xff)<<8; + rgb[n].blue = ((pixel[n]>>0)&0xff)<<8; + rgb[n].pad = 0; + } + rep.nColors = n; + rep.length = (sizeof(xrgb)*n+3)/4; + + X_SendData(cl, &rep, sizeof(rep)); + X_SendData(cl, rgb, rep.length*4); +} + void XR_LookupColor(xclient_t *cl, xReq *request) { typedef struct { @@ -3384,9 +3675,9 @@ void XR_SendEvent (xclient_t *cl, xReq *request) { int count; xSendEventReq *req = (xSendEventReq *)request; - xwindow_t *wnd; + xwindow_t *wnd = XS_GetResourceType(req->destination, x_window); - if (XS_GetResource(req->destination, (void**)&wnd) != x_window) + if (!wnd) { X_SendError(cl, BadWindow, req->destination, X_SendEvent, 0); return; @@ -3554,19 +3845,27 @@ void X_InitRequests(void) XRequests[X_PolyLine] = XR_PolyLine; XRequests[X_PolySegment] = XR_PolyLine; XRequests[X_QueryPointer] = XR_QueryPointer; +// XRequests[X_ChangeKeyboardMapping] = XR_ChangeKeyboardMapping; +// XRequests[X_SetModifierMapping] = XR_ChangeKeyboardMapping; XRequests[X_GetKeyboardMapping] = XR_GetKeyboardMapping; XRequests[X_GetKeyboardControl] = XR_GetKeyboardControl; + XRequests[X_GetModifierMapping] = XR_GetModifierMapping; XRequests[X_AllocColor] = XR_AllocColor; XRequests[X_LookupColor] = XR_LookupColor; + XRequests[X_QueryColors] = XR_QueryColors; XRequests[X_GetGeometry] = XR_GetGeometry; XRequests[X_CreateCursor] = XR_CreateCursor; XRequests[X_CreateGlyphCursor] = XR_CreateGlyphCursor; + XRequests[X_RecolorCursor] = XR_RecolorCursor; XRequests[X_FreeCursor] = XR_FreeCursor; - XRequests[X_WarpPointer] = XR_WarpPointer; + XRequests[X_GrabButton] = XR_GrabButton; + XRequests[X_UngrabButton] = XR_UngrabButton; + XRequests[X_WarpPointer] = XR_WarpPointer; XRequests[X_ListFonts] = XR_ListFonts; XRequests[X_OpenFont] = XR_OpenFont; + XRequests[X_CloseFont] = XR_CloseFont; XRequests[X_QueryFont] = XR_QueryFont; XRequests[X_PolyText8] = XR_PolyText; XRequests[X_PolyText16] = XR_PolyText; @@ -3585,6 +3884,7 @@ void X_InitRequests(void) XRequests[X_SendEvent] = XR_SendEvent; + XRequests[X_ConvertSelection] = XR_ConvertSelection; XRequests[X_GetSelectionOwner] = XR_GetSelectionOwner; XRequests[X_SetSelectionOwner] = XR_SetSelectionOwner; diff --git a/plugins/xsv/x_res.c b/plugins/xsv/x_res.c index 1c1cc92af..52ef46675 100644 --- a/plugins/xsv/x_res.c +++ b/plugins/xsv/x_res.c @@ -36,6 +36,18 @@ int XS_GetResource(int id, void **data) } return x_none; } +void *XS_GetResourceType(int id, int requiredtype) +{ + xresource_t *res; + if (id < 0) + return NULL; + + res = Hash_GetKey(&restable, id); + + if (res && res->restype == requiredtype) + return res; + return NULL; +} Atom XS_FindAtom(char *name) { @@ -83,8 +95,8 @@ void XS_DestroyResource(xresource_t *res) } if (res->restype == x_gcontext) { - xgcontext_t *gc = (xgcontext_t *)res; -/* +/* xgcontext_t *gc = (xgcontext_t *)res; + gc->references--; if (gc->references > 0) return; @@ -341,8 +353,23 @@ void XS_RaiseWindow(xwindow_t *wnd) bigger->sibling = wnd; wnd->sibling = NULL; } +static qboolean XS_IsAncestor(xwindow_t *w, xwindow_t *check) +{ + xwindow_t *p; + if (w) + for (p = w->parent; p; p = p->parent) + { + if (p == check) + return true; + } + return false; +} void XS_SetParent(xwindow_t *wnd, xwindow_t *parent) { + if (XS_IsAncestor(parent, wnd)) + parent = wnd==rootwindow?NULL:rootwindow; + if (wnd == parent) + parent = wnd==rootwindow?NULL:rootwindow; XS_ClearParent(wnd); if (parent) { @@ -367,6 +394,57 @@ void XS_SetParent(xwindow_t *wnd, xwindow_t *parent) XS_RaiseWindow(wnd); } +void X_Resize(xwindow_t *wnd, int newx, int newy, int neww, int newh) +{ + xEvent ev; + +/* if (wnd->xpos == newx && wnd->ypos == newy) + { + ev.u.u.type = ResizeRequest; + ev.u.u.detail = 0; + ev.u.u.sequenceNumber = 0; + ev.u.resizeRequest.window = wnd->res.id; + ev.u.resizeRequest.width = wnd->width; + ev.u.resizeRequest.height = wnd->height; + + X_SendNotificationMasked(&ev, wnd, StructureNotifyMask); + X_SendNotificationMasked(&ev, wnd, SubstructureNotifyMask); + + return; + }*/ + + wnd->xpos = newx; + wnd->ypos = newy; + + if ((wnd->width != neww || wnd->height != newh) && wnd->buffer) + { + free(wnd->buffer); + wnd->buffer = NULL; + } + wnd->width = neww; + wnd->height = newh; + + if (wnd->mapped) + xrefreshed = true; + + ev.u.u.type = ConfigureNotify; + ev.u.u.detail = 0; + ev.u.u.sequenceNumber = 0; + ev.u.configureNotify.event = wnd->res.id; + ev.u.configureNotify.window = wnd->res.id; + ev.u.configureNotify.aboveSibling = None; + ev.u.configureNotify.x = wnd->xpos; + ev.u.configureNotify.y = wnd->ypos; + ev.u.configureNotify.width = wnd->width; + ev.u.configureNotify.height = wnd->height; + ev.u.configureNotify.borderWidth = 0; + ev.u.configureNotify.override = wnd->overrideredirect; + ev.u.configureNotify.bpad = 0; + + X_SendNotificationMasked(&ev, wnd, StructureNotifyMask); + X_SendNotificationMasked(&ev, wnd, SubstructureNotifyMask); +} + xwindow_t *XS_CreateWindow(int wid, xclient_t *owner, xwindow_t *parent, short x, short y, short width, short height) { xwindow_t *neww; @@ -488,22 +566,28 @@ xfont_t *XS_CreateFont(int id, xclient_t *owner, char *fontname) unsigned char *in, *insrc; f = fopen("xfont.raw", "rb"); - if (!f) - return NULL; - fseek(f, 0, SEEK_END); - len = ftell(f); - fseek(f, 0, SEEK_SET); + if (f) + { + fseek(f, 0, SEEK_END); + len = ftell(f); + fseek(f, 0, SEEK_SET); - if (len == 256*256*3) + if (len == 256*256*3) + { + width = height = 256; + } + else if (len == 128*128*3) + width = height = 128; + else + { //urm, no idea. + fclose(f); + return NULL; + } + } + else { width = height = 256; - } - else if (len == 128*128*3) - width = height = 128; - else - { //urm, no idea. - fclose(f); - return NULL; + len = width*height*3; } newfont = malloc(sizeof(xfont_t) + (width*height*4)); @@ -522,13 +606,16 @@ xfont_t *XS_CreateFont(int id, xclient_t *owner, char *fontname) newfont->depth = 32; in = insrc = malloc(len); - fread(in, len, 1, f); - fclose(f); + if (f) + { + fread(in, len, 1, f); + fclose(f); + } out = newfont->data; for (i = 0; i < width*height; i++) { - *out = (in[0]) + (in[1]<<8) + (in[2]<<16) + (255<<24); + *out = (in[0]) + (in[1]<<8) + (in[2]<<16) + (255u<<24); out++; in+=3; }